我们首先在终端命令行更新 pip3:
$ sudo pip3 install -U pip
安装 Django2 的最终版本 2.2.9:
$ pip install django==2.2.9
此外我们还要安装一些其它的基础工具包,先安装 ipython 和 mysqlclient 这两个,其它的在需要时再进行安装:
$ pip install ipython mysqlclient
使用命令pip freeze查看当前 python 环境下已经安装的各种包及其依赖包:

2.创建Web项目
接下来我们来学习创建属于我们自己的 Web 项目。
执行命令django-admin startproject初始化一个 Django 项目,后面的参数为项目名称,我们这里的项目名称定为 myProject。执行 tree myProject 命令查看项目的目录结构:
$ django-admin startproject myProject$ ll$ tree myProject

对主目录下的文件和目录依次说明:
- manage.py 项目的入口文件,在后面的实验中我们会大量使用它来执行一些命令用来创建应用、启动项目、控制数据表迁移等。
- myProject 主目录下的同名子目录,为项目的核心目录,它里面包含配置文件和管理应用的文件。
- myProject/init.py 每个子目录都会包含这样一个 init.py 文件,它是一个空文件,在需要的时候会引入目录下的对象。
- myProject/settings.py 配置文件,里面包含对数据库的设置项、CSRF Token 的设置项、模板的设置项等全部设置。
- myProject/urls.py 路由控制文件,处理客户端请求,分发到对应的视图函数去处理。
- myProject/wsgi.py 处理请求和响应,我们很少去动它。
在终端执行 cd myProject 命令进入到项目的主目录,然后执行如下命令创建一个名为 myApp 的应用,注意实验环境里不可以创建名为 test 的应用,某些情况下它与 Python 模块冲突:
$ cd myProject$ python3 manage.py startapp myApp

如上所示,创建了名为 myApp 的应用后,在项目的主目录下出现了名为 myApp 的目录,这就是应用目录。
应用中的文件说明如下:
- myApp/admin.py 用于控制后台管理的文件,在后面的实验中会用到。
- myApp/apps.py 用于管理应用本身的文件,包括应用的名字如何命名,默认就是 myApp 。
- myApp/init.py 空文件,前面已经介绍过。
- myApp/migrations 这是用于记录数据库变更信息的目录,Django 中自带的数据库版本控制功能就体现在这个目录,在学习数据存储时会详细介绍。
- myApp/models.py 创建映射类的文件,熟悉 Flask 的同学一定不陌生。
- myApp/tests.py 编写测试代码的文件。
- myApp/views.py 创建视图函数的文件,视图函数用于处理客户端发来的请求。
3.配置文件
创建一个应用后,该应用并未与项目产生联系,需要在配置文件中添加此应用。
$ vim myProject/settings.py
修改 INSTALLED_APPS 项,添加我们创建的应用名称 myApp,如下:

修改完成后,键盘按 Esc 之后,输入:wq保存并退出。
启动项目
我们的实验环境为 Web 环境,其实本质上就是一个在线集成工具,类似 VSCode。它内部没有浏览器,但提供了一个 8080 端口的 Web 服务。我们可以在启动项目之后,点击右侧工具栏的 “Web 服务”,会自动在你的浏览器上打开一个新标签来访问你的项目:
新打开一个终端并输入命令:
$ cd myProject$ python3 manage.py runserver 0:8080

其中 manage.py 作为入口文件,它有很多选项提供各种各样的功能。runserver 为启动项目的选项,后面的 0:8080 为 0.0.0.0:8080 的简写,冒号前面为 IP 地址,后面为端口号。
启动项目后,点击工具栏的“Web 服务”按钮,打开新的页面,会出现如下图所示的报错:

这是因为我们的实验环境稍有些特殊,需要在配置文件中增加一项设置。
打开另一个终端标签,进入项目的主目录 myProject,打开 myProject/settings.py 文件,修改 ALLOWED_HOSTS 这一项为上述划线字符(注意:每次打开内容可能不一样,每启动一次都需要修改一下该项内容)。它也是一个列表,在列表中添加报错中所示的 header 信息,也就是单引号中的内容:

同时,我们将欢迎页适配为中文,方便后续操作和阅读,将 LANGUATE_CODE 和 TIME_ZONE 修改如下:

修改并保存后,ctrl+c 关闭服务器并重新打开 Web 服务,如下:

恭喜你,成功运行起第一个 Django 项目。
运行完毕过后 ctrl+c 关闭服务器,接下来开始介绍 Django 各个模块的用法。
4.视图View
创建应用
Django 中,每一个应用都是一个 Python 包,并且遵守着相同的约定。
Django 自带一个工具,可以帮你生成应用的基本目录,极大地提高了开发效率。
在实验二中我们已经创建一个 myProject 的目录,并创建了一个 myApp 的应用目录,终端输入命令按照以下顺序(实验二已创建则无需再执行):
$ sudo pip3 install -U pip$ pip install django==2.2.9$ pip install ipython mysqlclient$ django-admin startproject myProject$ cd myProject$ python3 manage.py startapp myApp
由于实验二已经详细讲述创建步骤,这里不再赘述,但是相关环境的搭建以及各配置文件请按照实验二的要求修改(注:新开终端进到 myProject 目录并打开 8080 服务端口python3 manage.py runserver 0:8080,点击 Web 服务进入 Django 欢迎页)。
我们创建的 myProject 总体目录结构如下:
myProject├── manage.py├── myApp│ ├── admin.py│ ├── apps.py│ ├── __init__.py│ ├── migrations│ │ └── __init__.py│ ├── models.py│ ├── tests.py│ └── views.py└── myProject├── __init__.py├── __pycache__│ ├── __init__.cpython-35.pyc│ └── settings.cpython-35.pyc├── settings.py├── urls.py└── wsgi.py
我们可以通过直接打开右侧文件来编辑,同时也可以直接在终端输入命令编辑相关文件:

编写视图
编辑 vim myApp/views.py 文件:
# myApp/views.pyfrom django.shortcuts import renderfrom django.http import HttpResponsedef index(request):return HttpResponse("Hello, world!")
大家可以看出来,我们想要看到的就是在页面中输出Hello, world!。
但仅编写视图函数,并不能在 Django 页面上显示出来。如果想要看到效果,我们需要使用 URL 来映射到它。
myProject/urls.py
接下来需要思考一下,视图函数是有了,它映射到那个 URL 路径下呢?也就是说浏览器访问了哪个网址,才会调用这个视图函数处理?
需要编辑myProject/urls.py文件,修改为以下代码:
# myProject/urls.pyfrom django.contrib import adminfrom django.urls import pathfrom myApp import views # 引入视图函数urlpatterns = [path('admin/', admin.site.urls),path('', views.index, name='index'), # 新增路由映射]
最外层的 myApp 目录也就是 /home/project/myProject 为项目的主目录,引入其它文件中的对象,可以使用绝对路径或相对路径。
urlpatterns 为路由映射到视图函数的控制列表,当服务器收到浏览器发送过来的请求时,首先到这里检查是否有对应的视图函数。如果是https://www.abc.com就让 index 来处理,如果是https://www.abc.com/admin/就让 admin.site.urls 来处理。
这一步我们将应用myApp里视图函数与 URL 映射到了一起。保存后刷新欢迎页,效果如下:

这样就可以看到我们定义的 index 视图函数生效了。
我们来详细讲解一下path()函数,它共有 4 个参数:
| 参数 | 意义 | 是否必须 |
|---|---|---|
| route | route 是一个匹配 URL 的准则(类似正则表达式)。当 Django 响应一个请求时,它会从 urlpatterns 的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。 | 必须 |
| view | 当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest 对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。 | 必须 |
| kwargs | 任意个关键字参数可以作为一个字典传递给目标视图函数。 | 可选 |
| name | 为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。 | 可选 |
在上面的代码中,route为空意味着我们可以直接在桌面环境下用链接http://localhost:8000/myApp/访问该视图函数。
5.模型Model
配置数据库
Django 的项目设置都包含在了myProject/myProject/settings.py中。
对于数据库,配置文件使用了SQLite作为默认的数据库文件。对于只是初步尝试 Django 的我们来说,这十分方便,无需再去配置其他的东西。
在实际开发中,我们会用到其它更具扩展性的数据库。例如MySQL、Oracle等。
如果你选择使用这些数据库,你需要安装相应数据库的绑定,然后改变设置文件中的DATABASE default。
本课程默认使用 SQLite 数据库,想要使用其它数据库,可以参考Django 官方文档 DATABASE。记得配置设置文件中的TIME_ZONE为自己所在地的时区,中国地区为Asia/Shanghai。
TIME_ZONE = 'Asia/Shanghai'
创建模型
模型是真实数据的简明描述。它包含了存储的数据所必要的字段和行为。Django 遵循不要重复自己(DRY 原则)。它的目标是让你只需要定义数据模型,然后其它的东西你都不用关心,都会自动从模型生成。
实验前,先进入到项目主目录:
$ cd myProject
在我们创建的图书馆应用中,需要创建一个模型Book。Book模型包括四个字段:书名、作者、出版社、出版日期。
向myApp/models.py文件中写入如下代码:
# myApp/models.pyfrom django.db import modelsclass Book(models.Model):name = models.CharField(max_length=200)author = models.CharField(max_length=100)pub_house = models.CharField(max_length=200)pub_date = models.DateTimeField('date published')
从代码可以看出,模型是django.db.models.Model类的子类。每个模型有一些类变量,它们都表示模型里的一个数据库字段。
每个字段都是Field类的实例。比如字符字段是CharField,日期字段被表示为DateTImeField。这将告诉 Django 每个字段要处理的数据类型。
定义某些Field类实例需要参数。如上面的max_length=100中的max_length。这个参数的用处不止于用来定义数据结构,也用于验证数据。
激活模型
通过前面的代码,Django 可以:
- 为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
- 创建可以与 Book 对象进行交互的 Python 数据库 API。
为了实现上述功能,我们首先要将 myApp 应用安装到我们项目中。
因为MyappConfig类写在文件 myApp/apps.py 中,所以它的路径为myApp.apps.MyappConfig。
在设置文件中添加路径:
# myProject/settings.pyINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',# 'myApp', 这一注释掉,不注释掉会因重复而报错'myApp.apps.MyappConfig',]
现在你的 Django 项目会包含 myApp 应用。 运行下面的命令:
$ python3 manage.py makemigrations myApp
你会看到这样的输出:

通过运行makemigrations命令,Django 会检测你对模型文件的修改,并且把修改的部分储存为一次迁移。
让我们看看迁移命令会执行哪些 SQL 语句。
$ python3 manage.py sqlmigrate myApp 0001
可以看到创建的 SQL 语句:

现在运行 migrate 命令,在数据库里创建新定义的模型的数据表:
$ python3 manage.py migrate
看到如下画面,则表示成功!

使用API
现在尝试一下 Django 为我们创建的各种 API:
$ python3 manage.py shell
使用这个命令而不是简单的使用 “Python” 是因为 manage.py 会设置DJANGO_SETTINGS_MODULE环境变量,这个变量会让 Django 根据myProject/settings.py文件来设置 Python 包的导入路径。
In [1]: from myApp.models import BookIn [2]: Book.objects.all() # 获取 Book 所有对象Out[1]: <QuerySet []>In [3]: from django.utils import timezoneIn [4]: b = Book(name='Business', author='Tom', pub_house='First Press', pub_date=timezone.now()) #创建In [5]: b.save() #保存In [6]: b.idOut[6]: 1In [7]: b.nameOut[7]: 'Business'In [8]: b.pub_dateOut[8]: datetime.datetime(2020, 4, 27, 7, 37, 59, 123686, tzinfo=<UTC>)
实验总结
本实验,我们学习了以下知识点:
- Django 模型
- 数据库配置
- 模型 API
记住,改变模型需要这三步:
- 编辑
models.py文件,改变模型。 - 运行
python manage.py makemigrations为模型的改变生成迁移文件。 - 运行
python manage.py migrate来应用数据库迁移。
6.模板Template
模板是一个文本,用于分离文档的表现形式和内容。
创建模板
首先,在你的 myProject/myApp 目录里创建一个 templates 目录。Django 将会在这个目录里查找模板文件。
在 myProject/myProject/settings.py 文件中的TEMPLATES配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了DjangoTemplates后端,并设置APP_DIRS = True。这一选项将会让DjangoTemplates在每个INSTALLED_APPS文件夹中寻找 templates 子目录。

新建模板文件 myApp/templates/myApp/detail.html,并向其中写入如下代码:
<!--myApp/templates/myApp/detail.html--><h1>Book List</h1><table><tr><td>书名</td><td>作者</td><td>出版社</td><td>出版时间</td></tr>{% for book in book_list.all %}<tr><td>{{ book.name }}</td><td>{{ book.author }}</td><td>{{ book.pub_house }}</td><td>{{ book.pub_date }}</td></tr>{% endfor %}</table>
模板统一使用点符号.来访问变量的属性。在示例{{ book.name }}中,首先 Django 尝试对 book 对象使用字典查找(也就是使用obj.get(str)操作),如果失败了就尝试属性查找(也就是obj.str操作),结果是成功了。如果这一操作也失败的话,将会尝试列表查找(也就是obj[int]操作)。
在{% for ... in ... %}循环中发生的函数调用:book_list.all被解释为 Python 代码book_list.objects.all(),将会返回一个可迭代的Book对象,这一对象可以在{% for ... in ... %}标签内部使用。
创建视图
现在我们要创建视图来返回图书列表:
# myApp/views.pyfrom django.shortcuts import renderfrom django.http import HttpResponsefrom myApp.models import Bookdef detail(request):book_list = Book.objects.order_by('-pub_date')[:5]context = {'book_list': book_list}return render(request, 'myApp/detail.html', context)
在此视图函数detail中,首先将数据库的 Book 列表按照pub_date时间来排序,存储到变量book_list中。
“载入模板,填充上下文,再返回由它生成的 HttpResponse 对象”是一个非常常用的操作流程。于是 Django 提供了一个快捷函数render()。
render()函数把request对象作为它的第一个参数,模板作为第二个参数,字典作为它的可选的第三个参数。它返回给定模板呈现的给定文本的一个HttpResponse对象。
在这里,context信息将会返回到模板myApp/detail.html。
绑定链接
将新视图添加进 myProject.urls 模块里:
# myProject/urls.pyfrom django.contrib import adminfrom django.urls import pathfrom myApp import viewsurlpatterns = [# path('admin/', admin.site.urls),path('', views.detail, name='detail'),]
运行
为了达到好的效果,可以参考前面模型实验,多创建几个 Book 实例,添加步骤见实验三。
现在运行我们的项目:
$ python3 manage.py runserver 0:8080
当得到如下结构时,表示我们成功!

7.使用 MTV 模式实现数据库增删改
本实验,我们将实战前面学习的知识点,完成对数据的增删改。
知识点
- Django 基础
- HTML 基础
实验代码
以下是本节实验的关键代码下载链接,大家可以下载作为参考:
$ wget https://labfile.oss.aliyuncs.com/courses/1660/Django_Book.zip$ unzip Django_Book.zip
编程学习最重要的是动手实践,所以希望大家尽可能按照实验步骤写出代码。
设计表单
在上一个实验中的 myApp/templates/myApp/detail.html 里,添加一个表单。
<form action="" method="post" name="addBook">{% csrf_token %}<p><span>书名:</span><input type="text" name="name" /></p><p><span>作者:</span><input type="text" name="author" /></p><p><span>出版社:</span><input type="text" name="pub_house" /></p><input type="submit" value="添加" /></form>
简要说明:
- 上面的模板是输入相应的书名、作者和出版社后,点击添加,将数据存储到数据库并刷新页面显示出来。
- 我们设置表单的
action="/addBook/",并设置method="post"。使用method="post"(与其相对的是method="get")是非常重要的,因为这个提交表单的行为会改变服务器端的数据,无论何时,当你需要创建一个改变服务器端数据的表单时,请使用method="post"。这不是 Django 的特定技巧,这是优秀的网站开发技巧。 - 由于我们创建一个 POST 表单(它具有修改数据的作用),所以我们需要小心跨站点请求伪造。 但你不必太过担心,因为 Django 已经拥有一个用来防御它的非常容易使用的系统。 简而言之,所有针对内部 URL 的 POST 表单都应该使用
{% csrf_token %}模板标签。
命名空间
教程项目只有一个应用 myApp。在一个真实的 Django 项目中,可能会有五个,十个,二十个,甚至更多应用。Django 如何分辨重名的 URL 呢?
举个例子,myApp 应用有 detail 视图,可能另一个博客应用也有同名的视图。Django 如何知道action=""标签到底对应哪一个应用的 URL 呢?
答案是:在根 URLconf 中添加命名空间。在myProject/urls.py文件中稍作修改,加上app_name设置命名空间:
from django.contrib import adminfrom django.urls import pathfrom myApp import viewsapp_name = 'myApp' # 添加这一行urlpatterns = [# path('admin/', admin.site.urls),path('', views.detail, name='detail'),path('addBook/', views.addBook, name='addBook'),]
现在回到myApp/templates/myApp/detail.html更改action:
<form action="/addBook/" method="post" name="addBook"></form>
添加书籍
创建addBook函数来实现我们添加书籍的功能。
将下面的代码添加到 myApp/views.py:
# myApp/views.pyfrom django.http import HttpResponseRedirectfrom django.urls import reversedef addBook(request):if request.method == 'POST':temp_name = request.POST['name']temp_author = request.POST['author']temp_pub_house = request.POST['pub_house']from django.utils import timezonetemp_book = Book(name=temp_name, author=temp_author, pub_house=temp_pub_house, pub_date=timezone.now())temp_book.save()# 重定向return HttpResponseRedirect(reverse('detail'))
在 myProject/urls.py 里添加 URL 地址映射:
# myProject/urls.pypath('addBook/', views.addBook, name='addBook'),
简单说明:
request.POST是一个类字典对象,可以通过关键字的名字获取提交的数据。 这个例子中,request.POST['name']以字符串形式返回name的值。request.POST的值永远是字符串。- 在添加书籍之后,代码返回一个
HttpResponseRedirect而不是常用的HttpResponse,HttpResponseRedirect只接收一个参数:用户将要被重定向的 URL。 - 你应该在每次处理 POST 数据时,都返回
HttpResponseRedirect。这也不是 Django 的特定技巧,这是优秀的网站开发的实践。 - 在这个例子中,我们在
HttpResponseRedirect的构造函数中使用reverse()函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。reverse()调用后将返回这样一个字符串:/detail/。
添加书籍功能完成,现在可以随意添加书籍。
打开 Web 服务访问链接就可以查看效果:
添加前:

添加后:

删除书籍
删除书籍功能实现起来也很简单。
首先,在myApp/detail.html中设计我们的模板。
{% for book in book_list.all %}<tr><td>{{ book.name }}</td><td>{{ book.author }}</td><td>{{ book.pub_house }}</td><td>{{ book.pub_date }}</td><td><a href="{% url 'delBook' book.id %}">删除</a></td><!--只添加这一行--></tr>{% endfor %}</table>
可以看出,只需要在每个书籍后面添加一个删除的按钮。删除时也传递了需要删除的图书的id。
接着,配置 url,只需在myProject/urls.py中添加这一行:
# myProject/urls.pypath('delBook/<int:book_id>', views.deleteBook, name='delBook'),
这里<int:book_id>是接收传递的参数book_id。
最后设计视图函数:
# myApp/views.pydef deleteBook(request, book_id):bookID = book_idBook.objects.filter(id=bookID).delete()# 重定向return HttpResponseRedirect(reverse('detail'))
这个视图中,获取到 book 的 id,根据 id 来删除指定书籍。最后跟上面的添加书籍函数相同,使用重定位刷新页面。
打开 Web 服务访问链接查看效果:
删除后:

8.总结
本实验将带大家深入了解 Django 的 MTV 设计模式。
MTV
MVC
在说 MTV 模式之前,让我们来简单的说说著名的 MVC 模式。
MVC,是模型(Model)-视图(View)-控制器(Controller)的缩写。其具体定义如下:
- M:模型(Model),数据存取层,负责业务对象和数据库对象。
- V:视图(View),与用户的交互,负责显示与怎样显示。
- C:控制器(Controller),接受用户动作,调用模型,输出相应视图。
三者以一种插件似的,松耦合的方式连接在一起。

MTV
Django 的 MTV 设计模式是借鉴和遵循 MVC 的。
MTV 具体定义如下:
- M:模型(Model),负责业务对象和数据库的关系映射。
- T:模板(Template),负责如何把页面展示给用户。
- V:视图(View),负责业务逻辑,并在适当时候调用模型和模板。
URL 分发器
URL 分发器的作用是将页面请求分发给不同的视图(View)处理,视图再调用相应的模型(Model)和模板(Template)。
Django Web 框架:

一个误区是把 MVC 模式与 MTV 模式等价替换。其实这样是不对的。
在 MTV 模式中,MVC 中的 View 分成了视图 View(展现哪些数据)和模板 Template(如何展现)2 个部分,而控制器(Controller)这个要素由框架自己来实现了,我们需要做的就是把 URL 对应到视图 V 就可以了,通过这样的 URL 配置,系统将一个请求发送到一个合适的视图。
源码链接
https://github.com/Arrrrray/shiyanlou-code/tree/master/myProject
