Commit 9eaf6530 authored by jackfrued's avatar jackfrued

更新了Django第二天的文档

parent 09a4ec0d
venv venv
.idea .idea
*.pyc *.pyc
## Django实战(01) - 快速上手 ## Django 2.x实战(01) - 快速上手
Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在呼唤新的英雄的时代,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,而且在应用更新时用户通常不需要做任何的处理就能使用更新后的应用,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。 Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在呼唤新的英雄的时代,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,而且在应用更新时用户通常不需要做任何的处理就能使用更新后的应用,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。
...@@ -8,6 +8,8 @@ Web开发的早期阶段,开发者需要手动编写每个页面,例如一 ...@@ -8,6 +8,8 @@ Web开发的早期阶段,开发者需要手动编写每个页面,例如一
![](./res/web-application.png) ![](./res/web-application.png)
> 说明:相信有经验的读者会发现,这张图中其实还少了很多东西,例如反向代理服务器、数据库服务器、防火墙等,而且图中的每个节点在实际项目部署时可能是一组节点组成的集群。当然,如果你对这些没有什么概念也不要紧,后续的课程中我们会为大家进行讲解。
| 术语 | 解释 | | 术语 | 解释 |
| ------------- | ------------------------------------------------------------ | | ------------- | ------------------------------------------------------------ |
| **URL/URI** | 统一资源定位符/统一资源标识符,网络资源的唯一标识 | | **URL/URI** | 统一资源定位符/统一资源标识符,网络资源的唯一标识 |
...@@ -19,13 +21,24 @@ Web开发的早期阶段,开发者需要手动编写每个页面,例如一 ...@@ -19,13 +21,24 @@ Web开发的早期阶段,开发者需要手动编写每个页面,例如一
| **Web服务器** | 接受HTTP请求,然后返回HTML文件、纯文本文件、图像等资源给请求者 | | **Web服务器** | 接受HTTP请求,然后返回HTML文件、纯文本文件、图像等资源给请求者 |
| **Nginx** | 高性能的Web服务器,也可以用作[反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86)[负载均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1) 和 [HTTP缓存](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) | | **Nginx** | 高性能的Web服务器,也可以用作[反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86)[负载均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1) 和 [HTTP缓存](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) |
#### HTTP协议
### Python的Web框架 这里我们稍微费一些笔墨来谈谈上面提到的HTTP。HTTP(超文本传输协议)是构建于TCP(传输控制协议)之上应用级协议,它利用了TCP提供的可靠的传输服务实现了Web应用中的数据交换。按照维基百科上的介绍,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,也就是说这个协议是浏览器和Web服务器之间传输的数据的载体。关于这个协议的详细信息以及目前的发展状况,大家可以阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html)[《互联网协议入门》](http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)系列以及[《图解HTTPS协议》](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)进行了解,下图是我于2009年9月10日凌晨4点在四川省网络通信技术重点实验室用开源协议分析工具Ethereal(抓包工具WireShark的前身)截取的访问百度首页时的HTTP请求和响应的报文(协议数据),由于Ethereal截取的是经过网络适配器的数据,因此可以清晰的看到从物理链路层到应用层的协议数据。
Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。 HTTP请求(请求行+请求头+空行+[消息体]):
![](./res/http-request.png)
HTTP响应(响应行+响应头+空行+消息体):
![](./res/http-response.png)
> 说明:但愿这两张如同泛黄的照片般的截图帮助你大概的了解到HTTP是一个怎样的协议。
### Django概述 ### Django概述
Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。
在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。 在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。
![](./res/mvc.png) ![](./res/mvc.png)
...@@ -49,11 +62,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -49,11 +62,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
>>> sys.version_info >>> sys.version_info
``` ```
2. 创建项目文件夹并切换到该目录。 2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目
```Shell ```Shell
$ mkdir hello_django $ mkdir oa
$ cd hello_django $ cd oa
``` ```
3. 创建并激活虚拟环境。 3. 创建并激活虚拟环境。
...@@ -69,7 +82,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -69,7 +82,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
```Shell ```Shell
(venv)$ python -m pip install --upgrade pip (venv)$ python -m pip install --upgrade pip
``` ```
> 注意:虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。 > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。
5. 安装Django。 5. 安装Django。
...@@ -77,7 +90,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -77,7 +90,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
(venv)$ pip install django (venv)$ pip install django
``` ```
指定版本号来安装对应的Django的版本。
```Shell ```Shell
(venv)$ pip install django==1.11 (venv)$ pip install django==1.11
...@@ -95,23 +108,32 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -95,23 +108,32 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
>>> import django >>> import django
>>> django.get_version() >>> django.get_version()
``` ```
![](./res/django-version.png) 下图展示了Django版本和Python版本的对应关系,在我们的项目中我们选择了最新的Django 2.0的版本。
| Django版本 | Python版本 |
| ---------- | ----------------------- |
| 1.8 | 2.7、3.2、3.3、3.4、3.5 |
| 1.9、1.10 | 2.7、3.4、3.5 |
| 1.11 | 2.7、3.4、3.5、3.6 |
| 2.0 | 3.4、3.5、3.6 |
7. 使用`django-admin`创建项目。 > 说明:在创建这篇文章时Django 2.1版本尚未正式发布,因此我们的教程使用了2.0.5版本。
7. 使用`django-admin`创建项目,项目命名为oa。
```Shell ```Shell
(venv)$ django-admin startproject hello_django . (venv)$ django-admin startproject oa .
``` ```
> 注意:上面的命令最后的.表示在当前路径下创建项目。 > 注意:上面的命令最后的那个点,它表示在当前路径下创建项目。
执行上面的命令后看看生成的文件和文件夹,它们的作用如下所示: 执行上面的命令后看看生成的文件和文件夹,它们的作用如下所示:
- `manage.py`: 一个让你用各种方式管理 Django 项目的命令行工具。 - `manage.py`: 一个让你用各种方式管理 Django 项目的命令行工具。
- `hello_django/__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。 - `oa/__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
- `hello_django/settings.py`:Django 项目的配置文件。 - `oa/settings.py`:Django 项目的配置文件。
- `hello_django/urls.py`:Django 项目的 URL 声明,就像你网站的“目录”。 - `oa/urls.py`:Django 项目的 URL 声明,就像你网站的“目录”。
- `hello_django/wsgi.py`:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。 - `oa/wsgi.py`:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。
8. 启动服务器运行项目。 8. 启动服务器运行项目。
...@@ -119,32 +141,65 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -119,32 +141,65 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
在浏览器中输入<http://127.0.0.1:8000>访问我们的服务器,效果如下图所示。
![](./res/django-index-1.png)
> 说明1:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。 > 说明1:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。
> 说明2:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。 > 说明2:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。
> 说明3:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:56789`来指定绑定的IP地址和端口。 > 说明3:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:56789`来指定绑定的IP地址和端口。
9. 创建应用程序(一个项目可以包含多个应用程序)。 > 说明4:可以通过Ctrl+C来终止服务器的运行。
9. 接下来我们进入项目目录oa并修改配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。
```Shell
(venv)$ cd oa
(venv)$ vim settings.py
```
```Python
# 此处省略上面的内容
# 设置语言代码
LANGUAGE_CODE = 'zh-hans'
# 设置时区
TIME_ZONE = 'Asia/Chongqing'
# 此处省略下面的内容
```
10. 回到manage.py所在的目录,刷新刚才的页面。
```Shell
(venv)$ cd ..
(venv)$ python manage.py runserver
```
![](./res/django-index-2.png)
#### 动态页面
1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。
```Shell ```Shell
(venv)$ python manage.py startapp hrs (venv)$ python manage.py startapp hrs
``` ```
创建的目录结构如下所示: 执行上面的命令会在当前路径下创建hrs目录,其目录结构如下所示:
- `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。 - `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
- `admin.py`:可以用来注册模型,让Django自动创建管理界面。 - `admin.py`:可以用来注册模型,让Django自动创建管理界面。
- `apps.py`:当前应用的配置。 - `apps.py`:当前应用的配置。
- `migrations`:存放与模型有关的数据库信息。 - `migrations`:存放与模型有关的数据库迁移信息。
- `__init__.py`略过 - `__init__.py`一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包
- `models.py`:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)。 - `models.py`:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)。
- `tests.py`测试应用的各种测试函数。 - `tests.py`包含测试应用各项功能的测试类和测试函数。
- `views.py`:处理请求并返回响应的函数(MVC中的C,MVT中的V)。 - `views.py`:处理请求并返回响应的函数(MVC中的C,MVT中的V)。
2. 进入应用目录修改视图文件views.py。
#### 动态页面
1. 进入应用目录修改视图文件views.py。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
...@@ -160,7 +215,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -160,7 +215,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
``` ```
2. 在应用目录创建一个urls.py文件并映射URL。 3. 在应用目录创建一个urls.py文件并映射URL。
```Shell ```Shell
(venv)$ touch urls.py (venv)$ touch urls.py
...@@ -176,13 +231,13 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -176,13 +231,13 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
path('', views.index, name='index'), path('', views.index, name='index'),
] ]
``` ```
> 说明:上面使用的path函数是Django 2.0中新添加的函数,除此之外还有re_path是支持正则表达式的URL映射函数;Django 1.x中是用url函数来设定URL映射。 > 说明:上面使用的path函数是Django 2.x中新添加的函数,除此之外还有re_path是支持正则表达式的URL映射函数;Django 1.x中是用url函数来设定URL映射。
3. 切换到项目目录,修改该目录下的urls.py文件合并应用中设定的URL 4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并
```Shell ```Shell
(venv) $ cd .. (venv) $ cd ..
(venv) $ cd hello_django (venv) $ cd oa
(venv) $ vim urls.py (venv) $ vim urls.py
``` ```
...@@ -196,9 +251,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -196,9 +251,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
] ]
``` ```
4. 启动项目并访问应用。 5. 启动项目并访问应用。
```Shell ```Shell
(venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -206,7 +262,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -206,7 +262,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
> 说明:如果想实现远程访问,需要先确认防火墙是否已经打开了8000端口,而且需要在配置文件settings.py中修改ALLOWED_HOSTS的设置,添加一个'*'表示允许所有的客户端访问Web应用。 > 说明:如果想实现远程访问,需要先确认防火墙是否已经打开了8000端口,而且需要在配置文件settings.py中修改ALLOWED_HOSTS的设置,添加一个'*'表示允许所有的客户端访问Web应用。
5. 修改views.py生成动态内容。 6. 修改views.py生成动态内容。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
...@@ -245,9 +301,9 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -245,9 +301,9 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
#### 使用视图模板 #### 使用视图模板
上面生成动态视图的方式在实际开发中是无法接受的,为了解决这个问题,我们可以提前准备一个模板页,所谓模板页就是一个带占位符的HTML页面,当我们将程序中获得的数据替换掉占位符的时候,一个动态页面就生成好了。 上面通过拼接HTML代码的方式生成动态视图的做法在实际开发中是无能接受的,这一点我相信是不言而喻的。为了解决这个问题,我们可以提前准备一个模板页,所谓模板页就是一个带占位符的HTML页面,当我们将程序中获得的数据替换掉页面中的占位符时,一个动态页面就产生了。
可以用Django框架中template模块的Template类创建模板对象,通过模板对象的render方法实现对模板的渲染。所谓的渲染就是用数据替换掉模板页中的占位符,Django框架通过shortcuts模块的快捷函数render简化了渲染模板的操作,具体的用法如下所示。 我们可以用Django框架中template模块的Template类创建模板对象,通过模板对象的render方法实现对模板的渲染。所谓的渲染就是用数据替换掉模板页中的占位符,Django框架通过shortcuts模块的快捷函数render简化了渲染模板的操作,具体的用法如下所示。
1. 先回到manage.py文件所在的目录创建一个templates文件夹。 1. 先回到manage.py文件所在的目录创建一个templates文件夹。
...@@ -273,7 +329,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -273,7 +329,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
<body> <body>
<h1>{{ greeting }}</h1> <h1>{{ greeting }}</h1>
<hr> <hr>
<h3>今天推荐3种水果是:</h3> <h3>今天推荐{{ num }}种水果是:</h3>
<ul> <ul>
{% for fruit in fruits %} {% for fruit in fruits %}
<li>{{ fruit }}</li> <li>{{ fruit }}</li>
...@@ -282,6 +338,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -282,6 +338,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
</body> </body>
</html> </html>
``` ```
注意在模板页中我们使用了`{{ greeting }}`这样的模板占位符语法,也使用了`{% for %}`这样的模板指令,如果对此不熟悉并不要紧,我们会在后续的内容中进一步的讲解模板的用法。
3. 回到应用目录,修改views.py文件。 3. 回到应用目录,修改views.py文件。
...@@ -308,13 +365,13 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -308,13 +365,13 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
``` ```
这里我们还没有办法让视图找到模板文件index.html,需要修改settings.py配置模板文件所在的路径。 此为止,我们还没有办法让views.py中的render函数找到模板文件index.html,为此我们需要修改settings.py文件,配置模板文件所在的路径。
4. 切换到项目目录修改settings.py文件。 4. 切换到项目目录修改settings.py文件。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ cd hello_django (venv)$ cd oa
(venv)$ vim settings.py (venv)$ vim settings.py
``` ```
...@@ -340,10 +397,17 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -340,10 +397,17 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
# 此处省略下面的内容 # 此处省略下面的内容
``` ```
5. 重新运行项目查看结果。 5. 重新运行项目查看结果。
```Shell ```Shell
(venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
![](./res/runserver-1.png) ![](./res/runserver.png)
### 总结
至此,我们已经利用Django框架完成了一个非常小的Web应用,虽然它并没有任何的实际价值,但是我们需要通过这个项目了解到Django框架的使用方式。当然,如果使用PyCharm的Professional版本,也可以通过PyCharm的创建项目的选项直接创建Django项目,使用PyCharm的好处在于编写代码时可以获得代码提示、错误修复、自动导入等功能,从而提升开发效率,但是代价是需要支付对应的费用才能使用专业版的PyCharm,社区版的PyCharm中并未包含对Web框架的支持。
此外,学习Django最好的资料肯定是它的[官方文档](https://docs.djangoproject.com/zh-hans/2.0/),除此之外图灵社区最近出版的[《Django基础教程》](http://www.ituring.com.cn/book/2630)也是非常适合初学者的读物。
\ No newline at end of file
## Django实战(02) - 深入模型
在上一个章节中,我们提到了Django是一个基于MVC架构的Web框架,MVC架构要追求的是模型和视图的解耦合,而其中的模型说得更直白一些就是数据,所以通常也被称作数据模型。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,在我们的OA项目中,我们选择使用MySQL来实现数据持久化。
### 配置关系型数据库MySQL
1. 进入oa文件夹,修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。
```Shell
(venv)$ cd oa
(venv)$ vim settings.py
```
```Python
# 此处省略上面的代码
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'hrs',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'oa',
'HOST': 'localhost',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '123456',
}
}
# 此处省略下面的代码
```
在配置ENGINE属性时,常用的可选值包括:
- `'django.db.backends.sqlite3'`:SQLite嵌入式数据库
- `'django.db.backends.postgresql'`:BSD许可证下发行的开源关系型数据库产品
- `'django.db.backends.mysql'`:转手多次目前属于甲骨文公司的经济高效的数据库产品
- `'django.db.backends.oracle'`:甲骨文公司的旗舰关系型数据库产品
其他的配置可以参考官方文档中[数据库配置](https://docs.djangoproject.com/zh-hans/2.0/ref/databases/#third-party-notes)的部分。
NAME属性代表数据库的名称,如果使用SQLite它对应着一个文件,在这种情况下NAME的属性值应该是一个绝对路径。如果使用其他关系型数据库,还要配置对应的USER、PASSWORD、HOST、PORT等属性。
2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。
```Shell
(venv)$ pip install pymysql
```
如果使用Python 3需要修改**项目**`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。
```Python
import pymysql
pymysql.install_as_MySQLdb()
```
3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要先启动MySQL数据库服务器并创建名为oa的数据库,在MySQL中创建数据库的语句如下所示。
```SQL
drop database if exists oa;
create database oa default charset utf8;
```
```Shell
(venv)$ cd ..
(venv)$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
```
4. 可以看到,Django帮助我们创建了10张二维表,这些都是使用Django框架需要的东西,除了这些之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型。
```Shell
(venv)$ cd hrs
(venv)$ vim models.py
```
```Python
from django.db import models
class Dept(models.Model):
"""部门类"""
no = models.IntegerField(primary_key=True, verbose_name='部门编号')
name = models.CharField(max_length=20, verbose_name='部门名称')
location = models.CharField(max_length=10, verbose_name='部门所在地')
class Meta:
db_table = 'tb_dept'
class Emp(models.Model):
"""员工类"""
no = models.IntegerField(primary_key=True, verbose_name='员工编号')
name = models.CharField(max_length=20, verbose_name='员工姓名')
job = models.CharField(max_length=10, verbose_name='职位')
mgr = models.IntegerField(null=True, blank=True, verbose_name='主管编号')
sal = models.DecimalField(max_digits=7, decimal_places=2, verbose_name='月薪')
comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='补贴')
dept = models.ForeignKey(Dept, on_delete=models.PROTECT, verbose_name='所在部门')
class Meta:
db_table = 'tb_emp'
```
5. 通过模型创建数据表。
```Shell
(venv)$ cd ..
(venv)$ python manage.py makemigrations hrs
Migrations for 'hrs':
hrs/migrations/0001_initial.py
- Create model Dept
- Create model Emp
(venv)$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, hrs, sessions
Running migrations:
Applying hrs.0001_initial... OK
```
执行完数据迁移操作之后,可以在通过图形化的MySQL客户端工具查看到E-R图(实体关系图)。
![](./res/er-graph.png)
### 在后台管理模型
### 使用ORM完成模型的CRUD操作
#### 新增
#### 删除
#### 更新
#### 查询
最后,我们通过上面掌握的知识来实现部门展示以及根据获取部门对应员工信息的功能。
### Django模型最佳实践
1. 正确的模型命名和关系字段命名。
2. 设置适当的related_name属性。
3. 用OneToOneField代替ForeignKeyField(unique=True)。
4. 通过迁移操作来添加模型。
5. 用NoSQL来应对需要降低范式级别的场景。
6. 如果布尔类型可以为空要使用NullBooleanField。
7. 在模型中放置业务逻辑。
8. 用ModelName.DoesNotExists取代ObjectDoesNotExists。
9. 在数据库中不要出现无效数据。
10. 不要对QuerySet调用len函数。
11. 将QuerySet的exists()方法的返回值用于if条件。
12. 用DecimalField来存储货币相关数据而不是FloatField。
13. 定义\_\_str\_\_方法。
14. 不要将数据文件放在同一个目录中。
### 模型定义参考
#### 字段
对字段名称的限制
- 字段名不能是Python的保留字,否则会导致语法错误
- 字段名不能有多个连续下划线,否则影响ORM查询操作
Django模型字段类
| 字段类 | 默认小组件 | 说明 |
| --------------------- | ------------------ | ------------------------------------------------------------ |
| AutoField | 无 | 自增ID字段 |
| BigIntegerField | NumberInput | 64位有符号整数 |
| BinaryField | 无 | 存储二进制数据的字段,对应Python的bytes类型 |
| BooleanField | CheckboxInput | 存储True或False |
| CharField | TextInput | 长度较小的字符串 |
| DateField | DateInput | 存储日期,有auto_now和auto_now_add属性 |
| DateTimeField | DateTimeInput | 存储日期和日期,两个附加属性同上 |
| DecimalField | TextInput | 存储固定精度小数,有max_digits(有效位数)和decimal_places(小数点后面)两个必要的参数 |
| DurationField | TextInput | 存储时间跨度 |
| EmailField | TextInput | 与CharField相同,可以用EmailValidator验证 |
| FileField | ClearableFileInput | 文件上传字段 |
| FloatField | TextInput | 存储浮点数 |
| ImageField | ClearableFileInput | 其他同FileFiled,要验证上传的是不是有效图像 |
| IntegerField | NumberInput | 存储32位有符号整数。 |
| GenericIPAddressField | TextInput | 存储IPv4或IPv6地址 |
| NullBooleanField | NullBooleanSelect | 存储True、False或null值 |
| PositiveIntegerField | NumberInput | 存储无符号整数(只能存储正数) |
| SlugField | TextInput | 存储slug(简短标注) |
| SmallIntegerField | NumberInput | 存储16位有符号整数 |
| TextField | Textarea | 存储数据量较大的文本 |
| TimeField | TextInput | 存储时间 |
| URLField | URLInput | 存储URL的CharField |
| UUIDField | TextInput | 存储全局唯一标识符 |
#### 字段属性
通用字段属性
| 选项 | 说明 |
| -------------- | ------------------------------------------------------------ |
| null | 数据库中对应的字段是否允许为NULL,默认为False |
| blank | 后台模型管理验证数据时,是否允许为NULL,默认为False |
| choices | 设定字段的选项,各元组中的第一个值是设置在模型上的值,第二值是人类可读的值 |
| db_column | 字段对应到数据库表中的列名,未指定时直接使用字段的名称 |
| db_index | 设置为True时将在该字段创建索引 |
| db_tablespace | 为有索引的字段设置使用的表空间,默认为DEFAULT_INDEX_TABLESPACE |
| default | 字段的默认值 |
| editable | 字段在后台模型管理或ModelForm中是否显示,默认为True |
| error_messages | 设定字段抛出异常时的默认消息的字典,其中的键包括null、blank、invalid、invalid_choice、unique和unique_for_date |
| help_text | 表单小组件旁边显示的额外的帮助文本。 |
| primary_key | 将字段指定为模型的主键,未指定时会自动添加AutoField用于主键,只读。 |
| unique | 设置为True时,表中字段的值必须是唯一的 |
| verbose_name | 字段在后台模型管理显示的名称,未指定时使用字段的名称 |
ForeignKey属性
1. limit_choices_to
2. related_name
3. relate_query_name
4. to_field
5. db_constraint
6. on_delete
ManyToManyField属性
1. symmetrical
2. through
3. throughfields
4. db_table
#### 模型元数据选项
| 选项 | 说明 |
| --------------------- | ------------------------------------------------------------ |
| abstract | |
| app_label | 如果定义模型的应用不在INSTALLED_APPS中可以用该属性指定 |
| db_table | 模型使用的数据表名称 |
| db_tablespace | |
| default_related_name | 关联对象回指这个模型时默认使用的名称,默认为<model_name>_set |
| get_latest_by | |
| managed | 设置为True时,Django在迁移中创建数据表并在执行flush管理命令时把表移除 |
| order_with_respect_to | |
| ordering | 对象的默认排序 |
| permissions | |
| default_permissions | 默认为`('add', 'change', 'delete')` |
| proxy | |
| unique_together | |
| index_together | |
| verbose_name | |
| verbose_name_plural | |
### 数据库API参考
按字段查找可以用的条件:
1. exact / iexact
2. contains / icontains
3. in
4. gt / gte / lt / lte
5. startswith / istartswith / endswith / iendswith
6. range
7. year / month / day / week_day / hour / minute / second
8. isnull
9. search
10. regex / iregex
跨关系查找
from django.db import models from django.db import models
# ORM - 对象关系映射
# 对象模型 <---> 关系模型
# 实体类 <---> 二维表
# 属性 <---> 列
# 对象 <---> 记录
class Dept(models.Model): class Dept(models.Model):
no = models.IntegerField(primary_key=True, verbose_name='部门编号') no = models.IntegerField(primary_key=True, verbose_name='部门编号')
name = models.CharField(max_length=20, verbose_name='部门名称') name = models.CharField(max_length=20, verbose_name='部门名称')
location = models.CharField(max_length=10, verbose_name='部门所在地') location = models.CharField(max_length=10, verbose_name='部门所在地')
# excellent = models.BooleanField(default=0, verbose_name='是否优秀')
def __str__(self):
return self.name
class Meta: class Meta:
db_table = 'tb_dept' db_table = 'tb_dept'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment