当今的网站实际上都是富应用程序(rich application), 就像成熟的桌面应用程序一样。Python提供了一组开发Web应用程序的卓越工具。使用Django开发一个名为“学习笔记”(Learning Log)的项目,这是一个在线日志系统,让你能够记录所学习的有关呢特定主题的知识。
Django是一个Web框架—一套用于帮助开发交互式网站的工具。Django能够响应网页请求,还能让你更轻松地读写数据库、管理用户等。
1. 建立项目
1.1 制定规范
我们要编写一个名为“学习笔记”的Web应用程序, 让用户能够记录感兴趣的主题、并在学习每个主题的过程中添加日志条目。“学习笔记”的主页对这个网站进行描述,并邀请用户注册或登录。用户登录后,就可以创建新主题、添加新条目以及阅读既有的条目。
1.2 建立虚拟环境
要使用Django, 首先需要建立一个虚拟工作环境。虚拟环境是系统的一个位置,你可以在其中安装包,并将其与其他Python包隔离。将项目的库与其他项目分离是有益的,为项目新建一个目录,将其命名为learning_log,再在终端切换到这个目录,并创建一个虚拟环境。
⚠️:本人的操作环境:
macOS Monterey Version 12.4
开发环境:PyCharm2020
命令如下:
(venv) (base) liuxiaowei@localhost learning_log % python -m venv 11_env
1.3 激活虚拟环境
建立虚拟环境后,需要使用下面的命令激活它:
(venv) (base) liuxiaowei@localhost learning_log % source 11_env/bin/activate
(11_env) (base) liuxiaowei@localhost learning_log %
这个命令11_env/bin中的脚本activate。环境处于活动状态时,环境名将包含在括号内,如下图所示:
在这种情况下,你可以在环境中安装包,并使用已安装的包。而且在11_env中安装的包仅在该环境处于活动状态时才可用。要停止使用虚拟环境,可执行命令deactivate:
(11_env) (base) liuxiaowei@localhost learning_log % deactivate
如果关闭运行虚拟环境的终端,虚拟环境也将不再处于活动状态。
1.4 安装Django
创建并激活虚拟环境后,就可以安装Django了(本案例使用1.11版):
(11_env) (base) liuxiaowei@localhost learning_log % pip install Django==1.11
Collecting Django==1.11
Using cached Django-1.11-py2.py3-none-any.whl (6.9 MB)
Collecting pytz
Using cached pytz-2022.1-py2.py3-none-any.whl (503 kB)
Installing collected packages: pytz, Django
Successfully installed Django-1.11 pytz-2022.1
备注:Django仅在虚拟环境处于活动状态时才可用。
1.5 在Django中创建项目
在依然处于活动的虚拟环境的情况下(11_env包含在括号内),执行如下命令来新建一个项目:
(11_env) (base) liuxiaowei@localhost learning_log % django-admin.py startproject learning_log .
(11_env) (base) liuxiaowei@localhost learning_log % ls
11_env learning_log manage.py
(11_env) (base) liuxiaowei@localhost learning_log % ls learning_log
__init__.py settings.py urls.py wsgi.py
这个命令末尾的句点让新项目使用合适的目录结构,这样开发完成后可以轻松地将应用程序部署到服务器。目录learning_log包含4个文件,其中最重要的是settings.py、urls.py和wsgi.py。文件settings.py指定Django如何与你的系统交互以及如何管理项目。文件urls.py告诉Django应创建哪些网页来响应浏览器请求。文件wsgi.py帮助Django提供它创建 的文件,这个文件名是web server gateway interface(Web服务器网关接口)的首字母缩写。
1.6 创建数据库
Django将大部分与项目相关的信息都存储在数据库中,因此我们需要创建一个供Django使用的数据库。为给项目“学习笔记”创建数据库,请在处于活动虚拟环境中情况下执行下面的命令:
(11_env) (base) liuxiaowei@localhost learning_log % python manage.py migrate
Traceback (most recent call last):
......
SyntaxError: Generator expression must be parenthesized
注意:本人执行这个命令的时候,报错。如下图所示:
解决办法:如上图箭头所指,去掉这个逗号就可以解决了。
(11_env) (base) liuxiaowei@localhost learning_log % ls
11_env db.sqlite3 learning_log manage.py
我们将修改数据库称为迁移数据库。首先执行命令migrate时,将让Django确保数据库与项目的当前状态匹配。运行命令ls,其输出表明Django又创建了一个文件—db.sqlite3。SQLite时一种使用单个文件的数据库,是编写简单应用程序的理想选择。
1.7 查看项目
核实Django是否正确地创建了项目。执行命令runserver, 如下所示:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
June 14, 2022 - 03:57:41
Django version 1.11, using settings 'learning_log.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Django启动一个服务器,让你能够查看系统中的项目,了解它们的工作情况。在浏览器中输入URL以请求网页时,该Django服务器将进行响应:生成合适的网页,并将其发送给浏览器。现在打开一款Web浏览器,并输入URL:http://127.0.0.1:8000/;将看到如下图所示的页面:
这个页面是Django创建的,上图表示一切正常。现在暂时不关闭这个服务器,若要关闭这个服务器,按Ctrl+C即可。
2. 创建应用程序
Django项目由一系列应用程序组成,它们协同工作,让项目成为一个整体。在前面打开的终端窗口中应该还运行着runserver。请再打开一个终端窗口,并切换到manage.py所在的目录。激活该虚拟环境,再执行命令startapp:
(venv) (base) liuxiaowei@MacBook-Air learning_log % source 11_env/bin/activate
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py startapp learning_logs
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls
11_env db.sqlite3 learning_log learning_logs manage.py
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls learning_logs/
__init__.py admin.py apps.py migrations models.py tests.py views.py
命令startup appname让Django建立创建应用程序所需的基础设施。查看项目目录,将看到其中新增一个文件夹learning_logs,打开这个文件夹,其中最重要的文件是models.py、admin.py和views.py。models.py来定义要在应用程序中管理的数据。
2.1 定义模型
打开models.py,创建自己的模型。模型告诉Django如何处理应用程序中的存储数据。在代码层面,模型就是一个类,包含属性和方法。下面表示用户将要存储的主题的模型。代码如下:
models.py
from django.db import models
# Create your models here.
class Topic(models.Model):
"""用户学习的主题"""
text = models.CharField(max_length=200) # 存储少量文本,如名称、标题和城市等
date_added = models.DateTimeField(auto_now_add=True) # 当用户创建主题时,自动设置成当前日期和时间
def __str__(self):
"""返回模型的字符串表示"""
return self.text
我们创建了一个Topic的类,它继承了Model–Django中一个定义了模型基本功能的类。Topic类只有两个属性:text和date_added。
2.2 激活模型
要使用模型,必须让Django将应用程序包含到项目中。为此,打开settings.py(位于目录learning_log/learning_log中),看到如下内容:
settings.py
---snip---
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
---snip---
这是一个列表,告诉Django项目是由哪些应用程序组成的。请将INSTALLED_APPS修改成如下所示:
---snip---
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# My app
'learning_logs',
]
---snip---
这里新建一个名为My app的片段,当前它只包含应用程序learning_logs。然后在终端窗口中执行如下命令:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0001_initial.py
- Create model Topic
命令makemigrations让Django确定该如何修改数据库,使其能够存储与我们定义的新模型相关联的数据。输出表明Django创建了一个名为0001_initial.py的迁移文件,这个文件将在数据库中为模型Topic创建一个表。下面应用这种迁移,让Django替我们修改数据库;命令如下:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0001_initial... OK
备注: 每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:修改models.py;对learning_logs调用make migrations;让Django迁移项目。
2.3 Django管理网站
1. 创建超级用户
Django允许你创建具备所有权限的用户–超级用户。权限决定了用户可执行的操作。在Django中创建超级用户,执行如下命令:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py createsuperuser
Username (leave blank to use 'liuxiaowei'): lxw_admin
Email address: 115323222@qq.com
Password:
Password (again):
Superuser created successfully.
2. 向管理网站注册模型
我们创建应用程序learning_logs时,Django在models.py所在目录中创建了一个名为admin.py的文件,为向管理网站注册Topic,请在admin.py中输入如下代码:
admin.py
from django.contrib import admin
from learning_logs.models import Topic
# Register your models here.
admin.site.register(Topic)
这些代码导入我们要注册的模型Topic,再使用admin.site.register()让给你Django通过管理网站管理我们的模型。现在使用超级用户账户访问管理网站:访问http://localhost:8000/admin/,并输入刚刚创建的超级用户的用户名和密码,如下图所示:
3. 添加主题
单击Topics进入主题网页,几乎是空的,因为我们没有添加任何主题,单击Add,打开一个添加新主题的表单,在第一个框内输入Chess,再单击Save,返回到主题管理页面,其中包含刚刚创建的主题。如下图所示:
本案例创建Chess和Rock Climbing两个主题。
2.4 定义模型Entry
要记录学到的国际象棋和攀岩知识,需要为用户可在学习笔记中添加的条目定义模型。每个条目都与特定主题相关联,这种关系被称为多对一关系,即多个条目可关联到同一个主题。
下面是模型Entry的代码:
models.py
from django.db import models
# Create your models here.
class Topic(models.Model):
---snip---
class Entry(models.Model):
"""学到的有关某个主题的具体知识"""
topic = models.ForeignKey(Topic, on_delete = models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示"""
return self.text[:50] + '...'
2.5 迁移模型Entry
由于我们添加了一个新模型因此需要再次迁移数据库。修改models.py,执行命令 python manage.py makemigrations app_name ,再执行命令python manage.py migrate。
命令如下:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0002_entry.py
- Create model Entry
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0002_entry... OK
生成了一个新的迁移文件—0002_entry.py, 它告诉Django如何修改数据库,使其能够存储与模型Entry相关的信息。执行命令migrate,我们发现Django应用了这种迁移且一切顺利。
2.6 向管理网站注册Entry
我们还需要注册模型Entry。为此需要将admin.py修改成类似如下的内容:
admin.py
from django.contrib import admin
from learning_logs.models import Topic, Entry
# Register your models here.
admin.site.register(Topic)
admin.site.register(Entry)
返回到http://localhost:8000/admin/,你将看到learning_logs下列出了Entries。单击Entries的Add链接,或者单击En tries再选择Add entry。
2.7 Django shell
输入一些数据以后, 就可以通过交互式终端会话以编程方式查看这些数据了。这种交互式环境称为Django shell,是测试项目和排除其故障的理想之地。下面是一个交互式shell会话示例:
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py shell
Python 3.9.12 (v3.9.12:b28265d7e6, Mar 23 2022, 18:17:11)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
在活动的虚拟环境中执行时,命令python manage.py shell 启动一个python解释器,可使用它来探索存储在项目数据库中的数据。在这里,我们导入模块learning_logs.models中的模型Topic,然后使用方法Topic.objects.all()来获取模型Topic的所有实例;它返回的是一个列表,称为查询集(queryset)。
下面演示如何查看分配给每个主题对象的ID:
>>> topics = Topic.objects.all()
>>> for topic in topics:
... print(topic.id, topic)
...
1 Chess
2 Rock Climbing
从以上输出可知,主题Chess的ID为1,而Rock Climbing的ID为2。知道对象的ID后, 就可获取该对象并查看其任何属性。通过如下命令可以查看主题Chess的属性text和date_added的值:
>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2022, 6, 14, 9, 4, 29, 528211, tzinfo=<UTC>)
还可以查看与主题关联的条目。利用这种关联,Django能够获取与特定主题相关联的所有条目,如下所示:
>>> t.entry_set.all()
<QuerySet [<Entry: Entry object>, <Entry: Entry object>, <Entry: Entry object>]>
注意: 每次修改模型后,都要重启shell, 这样才能看到修改的效果。要退出shell会话,可按Ctrl+D ,如果是Windows系统,按Ctrl+Z,再按回车键。
3. 创建网页:学习笔记主页
使用Django创建网页过程通常分三个阶段:定义URL、编写视图和编写模板。每个URL都被映射到特定视图——视图函数获取并处理网页所需的数据。
3.1 映射URL
用户通过在浏览器中输入URL以及单击链接来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,它是用户用来访问项目的基础URL。当前,基础URL(http://localhost:8000/)返回默认的Django网站,让我们知道正确地建立了项目。我们将修改这一点,将这个基础URL映射到“学习笔记”的主页。
打开项目文件夹learning_log中的文件urls.py,看到如下代码:
urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
]
我们需要包含learning_logs的URL:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'', include('learning_logs.urls', namespace='learning_logs')),
]
添加了一行代码来包含模块learning_logs.urls。这行代码包含实参namespace。默认的urls.py包含在文件夹learning_log中,现在我们需要在文件夹learning_logs中创建另一个urls.py文件:
urls.py
"""定义learning_logs的URL模式"""
from django.conf.urls import url
from . import views
urlpatterns = [
# 主页
url(r'^$', views.index, name='index')
]
3.2编写视图
视图函数接收请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器——这通常是使用定义了网页是什么样的模版实现的。
learning_logs中的文件views.py是你执行命令python manage.py startapp时自动生成的, 其内容如下:
views.py
from django.shortcuts import render
# Create your views here.
当前这个文件只导入了函数render(), 它根据视图提供的数据渲染响应。如何为主页编写视图,代码如下:
from django.shortcuts import render
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html')
3.3编写模版
模版定义了网页的结构。模版指定了网页时什么样的,而每当网页被请求时,Django将填入相关的数据。模板让你能够访问视图提供的任何数据。我们的主页视图没有提供任何数据,因此模板非常简单。
在文件夹learning_logs中新建一个文件夹,并将其命名为templates。在文件夹templates中,再新建一个文件夹,并将其命名为learning_logs。在最里面的文件夹learning_logs中,新建一个文件,并将其命名为index.html,再在这个文件中编写如下代码:
index.html
<p>Learning Log</p>
<p>Learning Log helps you keep track of your leanring, for any topic you're learning about.</p>
现在请求这个项目的基础URL–http://localhost:8000/,将看到刚才创建的网页,而不是默认的Django网页。如下图所示:
4. 创建其他网页
制定创建网页的流程后,可以开始扩充“学习笔记”项目了。创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们都将指定URL模式,编写一个视图函数,并编写一个模板。
4.1 模板继承
1. 父模板
首先创建一个名为base.html的模板,并将其存储在index.html所在目录中。这文件包含所有页面的元素;其他模板都继承base.html。
base.html
<p>
<a href = "{% url 'learning_logs:index' %}">Learning Log</a>
</p>
{% block content %}{% endblock content %}
2. 子模板
重新编写index.html,使其继承base.html,如下所示:
index.html
<p>Learning Log</p>
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Learning Log helps you keep track of your leanring, for any topic you're learning about.</p>
{% endblock content %}
4.2 显示所有主题的页面
1. URL模式
定义显示所有主题的页面的URL。通常,使用一个简单的URL片段来指出网页显示的信息;这里使用topics, 因此URL http://localhost:8000/topics/将返回显示所有主题的页面。先修改learning_logs/urls.py:
urls.py
"""定义learning_logs的URL模式"""
from django.conf.urls import url
from . import views
urlpatterns = [
# 主页
url(r'^$', views.index, name='index'),
# 显示所有的主题
url(r'^topics/$', views.topics, name='topics'),
]
2. 视图
在views.py中添加代码如下:
views.py
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html')
def topics(request):
"""显示所有的主题"""
topics = Topic.objects.order_by('date_added')
context = {'topics':topics}
return render(request, 'learning_logs/topics.html', context)
3. 模板
创建一个文件,命名为topics.html,存储到index.html所在的目录中。如下所示:
topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>{{ topic }}</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
修改父模板,使其包含到显示所有主题的页面链接:
base.html
<p>
<a href = "{% url 'learning_logs:index' %}">Learning Log</a>
<a href = "{% url 'learning_logs:topics' %}">Topics</a>
</p>
{% block content %}{% endblock content %}
现在刷新浏览器的中主页,将看到链接Topics。 单击这个链接,将看到如下图所示的网页:
4.3 显示特定主题的页面
1. URL模式
显示特定主题的页面的UR L模式与前面的所有URL模式稍有不同,因为它将使用主题的id属性来指出请求的是哪个主题。
修改learning_logs/urls.py:
urls.py
---snip---
urlpatterns = [
---snip--
# 显示特定主题
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
]
2. 视图
函数topic()需要从数据库中获取指定的主题以及与之相关联的所有条目,如下所示:
views.py
---snip---
def topic(request):
"""显示单个主题机器所有的条目"""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
3. 模板
这个模板需要显示主题的名称和条目的内容;如果当前主题不包含任何条目,我们还需向用户指出这点:
topic.html
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic:{{ topic }}</p>
<p>Entries:</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
4. 将显示所有主题的页面中的每个主题都设置为链接
修改模版topics.html,如下所示:
topics.html
---snip---
{% for topic in topics %}
<li><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></li>
{% empty %}
---snip---
刷新显示所有主题的页面,再单击其中一个主题,页面如下图所示: