我们首先在终端命令行更新 pip3:

  1. $ sudo pip3 install -U pip

安装 Django2 的最终版本 2.2.9:

  1. $ pip install django==2.2.9

此外我们还要安装一些其它的基础工具包,先安装 ipython 和 mysqlclient 这两个,其它的在需要时再进行安装:

  1. $ pip install ipython mysqlclient

使用命令pip freeze查看当前 python 环境下已经安装的各种包及其依赖包:
1.搭建 Django 环境 - 图1

2.创建Web项目

接下来我们来学习创建属于我们自己的 Web 项目。

执行命令django-admin startproject初始化一个 Django 项目,后面的参数为项目名称,我们这里的项目名称定为 myProject。执行 tree myProject 命令查看项目的目录结构:

  1. $ django-admin startproject myProject
  2. $ ll
  3. $ tree myProject

1.搭建 Django 环境 - 图2

对主目录下的文件和目录依次说明:

  • manage.py 项目的入口文件,在后面的实验中我们会大量使用它来执行一些命令用来创建应用、启动项目、控制数据表迁移等。
  • myProject 主目录下的同名子目录,为项目的核心目录,它里面包含配置文件和管理应用的文件。
  • myProject/init.py 每个子目录都会包含这样一个 init.py 文件,它是一个空文件,在需要的时候会引入目录下的对象。
  • myProject/settings.py 配置文件,里面包含对数据库的设置项、CSRF Token 的设置项、模板的设置项等全部设置。
  • myProject/urls.py 路由控制文件,处理客户端请求,分发到对应的视图函数去处理。
  • myProject/wsgi.py 处理请求和响应,我们很少去动它。

在终端执行 cd myProject 命令进入到项目的主目录,然后执行如下命令创建一个名为 myApp 的应用,注意实验环境里不可以创建名为 test 的应用,某些情况下它与 Python 模块冲突:

  1. $ cd myProject
  2. $ python3 manage.py startapp myApp

1.搭建 Django 环境 - 图3

如上所示,创建了名为 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.配置文件

创建一个应用后,该应用并未与项目产生联系,需要在配置文件中添加此应用。

  1. $ vim myProject/settings.py

修改 INSTALLED_APPS 项,添加我们创建的应用名称 myApp,如下:
1.搭建 Django 环境 - 图4

修改完成后,键盘按 Esc 之后,输入:wq保存并退出。

启动项目

我们的实验环境为 Web 环境,其实本质上就是一个在线集成工具,类似 VSCode。它内部没有浏览器,但提供了一个 8080 端口的 Web 服务。我们可以在启动项目之后,点击右侧工具栏的 “Web 服务”,会自动在你的浏览器上打开一个新标签来访问你的项目:

新打开一个终端并输入命令:

  1. $ cd myProject
  2. $ python3 manage.py runserver 0:8080

1.搭建 Django 环境 - 图5

其中 manage.py 作为入口文件,它有很多选项提供各种各样的功能。runserver 为启动项目的选项,后面的 0:8080 为 0.0.0.0:8080 的简写,冒号前面为 IP 地址,后面为端口号。

启动项目后,点击工具栏的“Web 服务”按钮,打开新的页面,会出现如下图所示的报错:

1.搭建 Django 环境 - 图6

这是因为我们的实验环境稍有些特殊,需要在配置文件中增加一项设置。

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

1.搭建 Django 环境 - 图7

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

1.搭建 Django 环境 - 图8

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

1.搭建 Django 环境 - 图9

恭喜你,成功运行起第一个 Django 项目。

运行完毕过后 ctrl+c 关闭服务器,接下来开始介绍 Django 各个模块的用法。

4.视图View

创建应用

Django 中,每一个应用都是一个 Python 包,并且遵守着相同的约定。

Django 自带一个工具,可以帮你生成应用的基本目录,极大地提高了开发效率。

在实验二中我们已经创建一个 myProject 的目录,并创建了一个 myApp 的应用目录,终端输入命令按照以下顺序(实验二已创建则无需再执行):

  1. $ sudo pip3 install -U pip
  2. $ pip install django==2.2.9
  3. $ pip install ipython mysqlclient
  4. $ django-admin startproject myProject
  5. $ cd myProject
  6. $ python3 manage.py startapp myApp

由于实验二已经详细讲述创建步骤,这里不再赘述,但是相关环境的搭建以及各配置文件请按照实验二的要求修改(注:新开终端进到 myProject 目录并打开 8080 服务端口python3 manage.py runserver 0:8080,点击 Web 服务进入 Django 欢迎页)。
我们创建的 myProject 总体目录结构如下:

  1. myProject
  2. ├── manage.py
  3. ├── myApp
  4. ├── admin.py
  5. ├── apps.py
  6. ├── __init__.py
  7. ├── migrations
  8. └── __init__.py
  9. ├── models.py
  10. ├── tests.py
  11. └── views.py
  12. └── myProject
  13. ├── __init__.py
  14. ├── __pycache__
  15. ├── __init__.cpython-35.pyc
  16. └── settings.cpython-35.pyc
  17. ├── settings.py
  18. ├── urls.py
  19. └── wsgi.py

我们可以通过直接打开右侧文件来编辑,同时也可以直接在终端输入命令编辑相关文件:
1.搭建 Django 环境 - 图10

编写视图

编辑 vim myApp/views.py 文件:

  1. # myApp/views.py
  2. from django.shortcuts import render
  3. from django.http import HttpResponse
  4. def index(request):
  5. return HttpResponse("Hello, world!")

大家可以看出来,我们想要看到的就是在页面中输出Hello, world!
但仅编写视图函数,并不能在 Django 页面上显示出来。如果想要看到效果,我们需要使用 URL 来映射到它。

myProject/urls.py

接下来需要思考一下,视图函数是有了,它映射到那个 URL 路径下呢?也就是说浏览器访问了哪个网址,才会调用这个视图函数处理?

需要编辑myProject/urls.py文件,修改为以下代码:

  1. # myProject/urls.py
  2. from django.contrib import admin
  3. from django.urls import path
  4. from myApp import views # 引入视图函数
  5. urlpatterns = [
  6. path('admin/', admin.site.urls),
  7. path('', views.index, name='index'), # 新增路由映射
  8. ]

最外层的 myApp 目录也就是 /home/project/myProject 为项目的主目录,引入其它文件中的对象,可以使用绝对路径或相对路径。
urlpatterns 为路由映射到视图函数的控制列表,当服务器收到浏览器发送过来的请求时,首先到这里检查是否有对应的视图函数。如果是https://www.abc.com就让 index 来处理,如果是https://www.abc.com/admin/就让 admin.site.urls 来处理。

这一步我们将应用myApp里视图函数与 URL 映射到了一起。保存后刷新欢迎页,效果如下:

1.搭建 Django 环境 - 图11

这样就可以看到我们定义的 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 的我们来说,这十分方便,无需再去配置其他的东西。

在实际开发中,我们会用到其它更具扩展性的数据库。例如MySQLOracle等。

如果你选择使用这些数据库,你需要安装相应数据库的绑定,然后改变设置文件中的DATABASE default

本课程默认使用 SQLite 数据库,想要使用其它数据库,可以参考Django 官方文档 DATABASE。记得配置设置文件中的TIME_ZONE为自己所在地的时区,中国地区为Asia/Shanghai

  1. TIME_ZONE = 'Asia/Shanghai'

创建模型

模型是真实数据的简明描述。它包含了存储的数据所必要的字段和行为。Django 遵循不要重复自己(DRY 原则)。它的目标是让你只需要定义数据模型,然后其它的东西你都不用关心,都会自动从模型生成。

实验前,先进入到项目主目录:

  1. $ cd myProject

在我们创建的图书馆应用中,需要创建一个模型BookBook模型包括四个字段:书名、作者、出版社、出版日期。
myApp/models.py文件中写入如下代码:

  1. # myApp/models.py
  2. from django.db import models
  3. class Book(models.Model):
  4. name = models.CharField(max_length=200)
  5. author = models.CharField(max_length=100)
  6. pub_house = models.CharField(max_length=200)
  7. 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

在设置文件中添加路径:

  1. # myProject/settings.py
  2. INSTALLED_APPS = [
  3. 'django.contrib.admin',
  4. 'django.contrib.auth',
  5. 'django.contrib.contenttypes',
  6. 'django.contrib.sessions',
  7. 'django.contrib.messages',
  8. 'django.contrib.staticfiles',
  9. # 'myApp', 这一注释掉,不注释掉会因重复而报错
  10. 'myApp.apps.MyappConfig',
  11. ]

现在你的 Django 项目会包含 myApp 应用。 运行下面的命令:

  1. $ python3 manage.py makemigrations myApp

你会看到这样的输出:
1.搭建 Django 环境 - 图12

通过运行makemigrations命令,Django 会检测你对模型文件的修改,并且把修改的部分储存为一次迁移。

让我们看看迁移命令会执行哪些 SQL 语句。

  1. $ python3 manage.py sqlmigrate myApp 0001

可以看到创建的 SQL 语句:
1.搭建 Django 环境 - 图13

现在运行 migrate 命令,在数据库里创建新定义的模型的数据表:

  1. $ python3 manage.py migrate

看到如下画面,则表示成功!
1.搭建 Django 环境 - 图14

使用API

现在尝试一下 Django 为我们创建的各种 API:

  1. $ python3 manage.py shell

使用这个命令而不是简单的使用 “Python” 是因为 manage.py 会设置DJANGO_SETTINGS_MODULE环境变量,这个变量会让 Django 根据myProject/settings.py文件来设置 Python 包的导入路径。

  1. In [1]: from myApp.models import Book
  2. In [2]: Book.objects.all() # 获取 Book 所有对象
  3. Out[1]: <QuerySet []>
  4. In [3]: from django.utils import timezone
  5. In [4]: b = Book(name='Business', author='Tom', pub_house='First Press', pub_date=timezone.now()) #创建
  6. In [5]: b.save() #保存
  7. In [6]: b.id
  8. Out[6]: 1
  9. In [7]: b.name
  10. Out[7]: 'Business'
  11. In [8]: b.pub_date
  12. Out[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 子目录。

1.搭建 Django 环境 - 图15

新建模板文件 myApp/templates/myApp/detail.html,并向其中写入如下代码:

  1. <!--myApp/templates/myApp/detail.html-->
  2. <h1>Book List</h1>
  3. <table>
  4. <tr>
  5. <td>书名</td>
  6. <td>作者</td>
  7. <td>出版社</td>
  8. <td>出版时间</td>
  9. </tr>
  10. {% for book in book_list.all %}
  11. <tr>
  12. <td>{{ book.name }}</td>
  13. <td>{{ book.author }}</td>
  14. <td>{{ book.pub_house }}</td>
  15. <td>{{ book.pub_date }}</td>
  16. </tr>
  17. {% endfor %}
  18. </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 ... %}标签内部使用。

创建视图

现在我们要创建视图来返回图书列表:

  1. # myApp/views.py
  2. from django.shortcuts import render
  3. from django.http import HttpResponse
  4. from myApp.models import Book
  5. def detail(request):
  6. book_list = Book.objects.order_by('-pub_date')[:5]
  7. context = {'book_list': book_list}
  8. 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 模块里:

  1. # myProject/urls.py
  2. from django.contrib import admin
  3. from django.urls import path
  4. from myApp import views
  5. urlpatterns = [
  6. # path('admin/', admin.site.urls),
  7. path('', views.detail, name='detail'),
  8. ]

运行

为了达到好的效果,可以参考前面模型实验,多创建几个 Book 实例,添加步骤见实验三。

现在运行我们的项目:

  1. $ python3 manage.py runserver 0:8080

当得到如下结构时,表示我们成功!
1.搭建 Django 环境 - 图16

7.使用 MTV 模式实现数据库增删改

本实验,我们将实战前面学习的知识点,完成对数据的增删改。

知识点

  • Django 基础
  • HTML 基础

实验代码

以下是本节实验的关键代码下载链接,大家可以下载作为参考:

  1. $ wget https://labfile.oss.aliyuncs.com/courses/1660/Django_Book.zip
  2. $ unzip Django_Book.zip

编程学习最重要的是动手实践,所以希望大家尽可能按照实验步骤写出代码。

设计表单

在上一个实验中的 myApp/templates/myApp/detail.html 里,添加一个表单。

  1. <form action="" method="post" name="addBook">
  2. {% csrf_token %}
  3. <p><span>书名:</span><input type="text" name="name" /></p>
  4. <p><span>作者:</span><input type="text" name="author" /></p>
  5. <p><span>出版社:</span><input type="text" name="pub_house" /></p>
  6. <input type="submit" value="添加" />
  7. </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设置命名空间:

  1. from django.contrib import admin
  2. from django.urls import path
  3. from myApp import views
  4. app_name = 'myApp' # 添加这一行
  5. urlpatterns = [
  6. # path('admin/', admin.site.urls),
  7. path('', views.detail, name='detail'),
  8. path('addBook/', views.addBook, name='addBook'),
  9. ]

现在回到myApp/templates/myApp/detail.html更改action

  1. <form action="/addBook/" method="post" name="addBook"></form>

添加书籍

创建addBook函数来实现我们添加书籍的功能。

将下面的代码添加到 myApp/views.py:

  1. # myApp/views.py
  2. from django.http import HttpResponseRedirect
  3. from django.urls import reverse
  4. def addBook(request):
  5. if request.method == 'POST':
  6. temp_name = request.POST['name']
  7. temp_author = request.POST['author']
  8. temp_pub_house = request.POST['pub_house']
  9. from django.utils import timezone
  10. temp_book = Book(name=temp_name, author=temp_author, pub_house=temp_pub_house, pub_date=timezone.now())
  11. temp_book.save()
  12. # 重定向
  13. return HttpResponseRedirect(reverse('detail'))

在 myProject/urls.py 里添加 URL 地址映射:

  1. # myProject/urls.py
  2. path('addBook/', views.addBook, name='addBook'),

简单说明:

  • request.POST是一个类字典对象,可以通过关键字的名字获取提交的数据。 这个例子中,request.POST['name']以字符串形式返回name的值。request.POST的值永远是字符串。
  • 在添加书籍之后,代码返回一个HttpResponseRedirect而不是常用的HttpResponseHttpResponseRedirect只接收一个参数:用户将要被重定向的 URL。
  • 你应该在每次处理 POST 数据时,都返回HttpResponseRedirect。这也不是 Django 的特定技巧,这是优秀的网站开发的实践。
  • 在这个例子中,我们在HttpResponseRedirect的构造函数中使用reverse()函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。reverse()调用后将返回这样一个字符串:/detail/

添加书籍功能完成,现在可以随意添加书籍。

打开 Web 服务访问链接就可以查看效果:

添加前:

1.搭建 Django 环境 - 图17

添加后:

1.搭建 Django 环境 - 图18

删除书籍

删除书籍功能实现起来也很简单。

首先,在myApp/detail.html中设计我们的模板。

  1. {% for book in book_list.all %}
  2. <tr>
  3. <td>{{ book.name }}</td>
  4. <td>{{ book.author }}</td>
  5. <td>{{ book.pub_house }}</td>
  6. <td>{{ book.pub_date }}</td>
  7. <td><a href="{% url 'delBook' book.id %}">删除</a></td><!--只添加这一行-->
  8. </tr>
  9. {% endfor %}
  10. </table>

可以看出,只需要在每个书籍后面添加一个删除的按钮。删除时也传递了需要删除的图书的id
接着,配置 url,只需在myProject/urls.py中添加这一行:

  1. # myProject/urls.py
  2. path('delBook/<int:book_id>', views.deleteBook, name='delBook'),

这里<int:book_id>是接收传递的参数book_id
最后设计视图函数:

  1. # myApp/views.py
  2. def deleteBook(request, book_id):
  3. bookID = book_id
  4. Book.objects.filter(id=bookID).delete()
  5. # 重定向
  6. return HttpResponseRedirect(reverse('detail'))

这个视图中,获取到 book 的 id,根据 id 来删除指定书籍。最后跟上面的添加书籍函数相同,使用重定位刷新页面。
打开 Web 服务访问链接查看效果:

删除后:

1.搭建 Django 环境 - 图19

8.总结

本实验将带大家深入了解 Django 的 MTV 设计模式。

MTV

MVC

在说 MTV 模式之前,让我们来简单的说说著名的 MVC 模式。

MVC,是模型(Model)-视图(View)-控制器(Controller)的缩写。其具体定义如下:

  • M:模型(Model),数据存取层,负责业务对象和数据库对象。
  • V:视图(View),与用户的交互,负责显示与怎样显示。
  • C:控制器(Controller),接受用户动作,调用模型,输出相应视图。

三者以一种插件似的,松耦合的方式连接在一起。

1.搭建 Django 环境 - 图20

MTV

Django 的 MTV 设计模式是借鉴和遵循 MVC 的。

MTV 具体定义如下:

  • M:模型(Model),负责业务对象和数据库的关系映射。
  • T:模板(Template),负责如何把页面展示给用户。
  • V:视图(View),负责业务逻辑,并在适当时候调用模型和模板。

URL 分发器

URL 分发器的作用是将页面请求分发给不同的视图(View)处理,视图再调用相应的模型(Model)和模板(Template)。

Django Web 框架:

1.搭建 Django 环境 - 图21

一个误区是把 MVC 模式与 MTV 模式等价替换。其实这样是不对的。

在 MTV 模式中,MVC 中的 View 分成了视图 View(展现哪些数据)和模板 Template(如何展现)2 个部分,而控制器(Controller)这个要素由框架自己来实现了,我们需要做的就是把 URL 对应到视图 V 就可以了,通过这样的 URL 配置,系统将一个请求发送到一个合适的视图。

源码链接

https://github.com/Arrrrray/shiyanlou-code/tree/master/myProject