Django是一个开放源代码的Web应用框架,由Python写成。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的。Django的主要目标是使得开发复杂的、数据库驱动的网站变得简单。
对于Django 框架,我的评价是:这是一个全功能的Web 开发框架。Web 开发所需要的一切它都包含了,你不需要去选择,只需要去熟悉,然后使用。
相对于 Flask、web.py 和 Bottle 这一类微型框架来说,Django 上手会有点复杂,但也并不难,因为官网的新手指导写得很清晰。在众多框架中,Django 的文档算是相当优秀和完整的了。
下面,我们用Django简单做一个系统出来。一起来熟悉 Django,了解它所提供的具体功能点。
另外,最重要的一个事情是如何查看文档。Django 的文档即便是写得再好,也会让人“迷路”。之后我们会快速地把Django 文档中涉及的各个模块都实践一下,最终做出一个简单的系统来。
在正式开发流程中,有可能会在选型阶段来完成这部分内容。我们需要做一个简单的系统,找找感觉,或者说看看实际使用中的匹配程度。
好了,让我们开始吧。
如何阅读 Django 文档
用文字来描述如何阅读文档似乎不是一件容易的事,我尽量表达清楚。
文档结构
Django 是基于 MVC(Model-View-Controller)模式的框架,虽然也有人称其为 MTV 模式的,但是概念大同小异。我们只需知道,无论它是 MVC 模式还是 MTV 模式,甚至是其他什么模式,最终的目的都是解耦,把一个软件系统划分为一层一层的结构,让每一层的逻辑更加纯粹,便于开发人员维护。
我们先看一下 Django 处理请求的示意图(如图 3-1 所示),从中可以直观了解到 Django 提供了哪些模块以及各个模块所处的位置。
基于图3-1,我们再来看看Django 文档。
从大的划分上来说,Django 文档先是分出了这么几个部分:Model 层、View 层、模板层和Form模块。剩下的部分都是功能文档,比如Pagination(分页模块)和Caching(缓存模块)等,这些是可以贯穿所有层的模块。
而每个模块或者说层又分为不同的模块,下面我们简要介绍一下常用的模块。
1. Model 部分
Model 在整个项目结构中是直接同数据库打交道的一层,所以数据处理的部分都在这一层。在业务开发中,关于纯数据操作的部分,建议都放到这一层来做。
- Models:模型定义相关的使用说明,字段类型,meta 配置。
- QuerySets:在Model 的基础上怎么查看数据,有哪些接口可以用(比如all 和filter等),以及如何更进一步进行定制。毕竟ORM 在查询上会有一些限制,但是在这一部分中你可以找到如何自定义查询。
- Model instances:Model 的实例,一个实例可以理解为表中的一条记录。这个实例有哪些操作,如何修改表的数据,都在这了。
- Migrations:在开发阶段,我们可能会不断调整表的结构,这部分就是用来做表结构调整的。理论上,我们只需要知道两个命令—— makemigrations 和migrate 就行了,但是如果你想了解更多,可以仔细看看这部分内容。
- Advanced:高级部分(别被“高级”这两字吓到)。这里涉及如何自定义Manager(也就是常用的Model.objects.all 中的objects),以及如果不爽ORM的查询限制,但是又想使用ORM对象的映射,可以考虑使用原生SQL。另外,关于事务、聚合、搜索以及多数据库支持等更多关于Model 层的需求,都可以看这里。
- Other:这一部分有两块建议一定要看看。一个是Legacy databases(遗留数据库)。想象一下,有人甩给你一个遗留的CMS 项目,要将其改成Django 的,你拿到数据库后,可以直接根据此数据库生成Model。之后你再花几分钟写写admin 部分的代码,CMS 就出来了,是不是很高效!另外一部分就是Optimize database access,一定要看!避免你踩坑。如果不想看英文,可以到我的博客中看中文翻译。虽然版本较老,但是依然适用,详见“翻译了Django 1.4 数据库访问优化部分”,网址为https://www.the5fire.com/django-database-access-optimization.html
2. View 部分
在上一节的文档中,我们知道如何跟数据库打交道,简单来说就是可以操作数据库了。而在这部分(View)中,侧重点将在业务上。通过获取数据、过滤数据和整合数据,拿到我们想要的结果,然后传递到模板中,最终通过HttpResponse 渲染出来。
在Django 的文档中,View 部分包含了URL 配置、HTTP Request、HTTP Response 以及处理请求的view 函数和类级的View 等。下面我们一一列举。
- The basics:包含URL 配置、View 方法以及常用装饰器,比如想给这个接口增加缓存或者增加限制(只允许GET 请求)等。
- Reference:一些参考,包含内置的View(比如静态文件处理和404 页面处理等),Request 和Response 对象介绍,TemplateResponse 对象介绍。
- File uploads:这部分介绍了文件上传的内容,它是Web 开发中常遇到的问题,其中提供了一些内置的模块来帮你处理上传上来的文件。它也会告诉你如何自定义后端存储。
- Class-based views:这部分可以理解为更复杂的view 函数,只不过这里介绍的是类。通过类可以提供更好的复用,从而避免自己写很多代码。当你发现你的view 中有太多业务代码时,可以考虑参考这部分内容把代码改造为class-based view(简称CBV)。如果你的代码中有很多类似的view 函数,也可以考虑这么做。这部分文档就是告诉你,在Django中,如何更好地构建和复用View。
- Advanced:更高级的部分,告诉你如何把数据导出为CSV 或者PDF 格式。之所以冠名为“更高级”,可能是因为用得少。
- Middleware:中间件(中间层),无论怎么翻译,你得理解它的作用,这一部分代码作用于WSGI(或者socket 连接)和View 之间。还记得第2 章讲的WSGI 中间件的内容吗,一样的逻辑,都是对view 函数做了一个包装(确切地说,是对View 的输入request 和输出response 进行处理),但是稍微复杂了一些。在Django 中,安全、session、整站缓存的内容都在这一块了。
3. Template 部分
这是Django 声称对设计师友好的部分,并且确实如此。因为它提供的语法很简单,即便你不懂编程,也可以很容易学习和使用。
- The basics:这部分介绍了Django 模板的基本配置以及基本的模板语法,还有如何使用其他模板引擎(比如Jinja2)的配置。
- For designers:这部分说是给设计师看的,但是你也应该看一看,里面包含基础的控制语句、注释、内置的过滤器和标签,还有最重要的针对用户友好的数字的展示。
- For programmers:这个程序员更应该看看了,里面包含如何将数据传递到模板中,如何配置模板以至于能够在View 中更好地渲染模板,还有如何对现有模板所提供的简单功能做更多定制。
4. Form 部分
无论是对于传统的、需要通过Form来提交数据的页面,还是通过Ajax 的方式提交数据到后端,Form 都是非常好用的,它可以帮我们比较优雅地处理和校验来自外部的数据。它的工作原理跟ORM 很像(关于ORM是什么,可以参见https://www.the5fire.com/what-is-orm.html)。Form是对HTML 中Form表单的抽象,可以方便我们用Python 代码来直接操作页面传递过来的数据。
- The basics:基础的API 介绍,里面有类似于Model 的Field 的部分,还有组件(widget)的部分。
- Advanced:更丰富的用法,包括如何把Form同Model 结合(Model 也有Field,Form也有Field,共用一套行不行?),以及如何把静态资源渲染到页面上。如果Form足够好用,我们根本无须频繁操作模板。还有如何布局字段,一行展示一个还是一行展示多个。此外,还有更加详细、深入的部分,那就是如何定义字段级别的验证功能,比如页面上只允许输入数字的地方如何验证。
这部分在开发admin 时很常用,因为admin 跟Model 结合紧密,如果需要去改模板的话,成本会有点高,所以更好的做法是通过自定义Form以及widget 来实现我们需要的功能。很多时候,我们不需要操作页面就可以完成需求,这对后端程序员来说是相当友好的。
另外,即便是对于现在流行的单页应用,也可以使用Form来处理接口拿到的数据,其逻辑跟传统的提交表单的方式没太多不同。
5. 开发流程
这一部分建议一定要看!很重要。如果说前面的内容都是为了让你掌握使用Django 的技能,那么这一阶段就是让你的代码释放能力。开发完成后,应该怎么部署系统?有哪些东西是需要配置的?线上环境应该是什么样的?
你应该仔细看看这部分。
- Settings:不得不说,Django 的可配置项非常多,即便是工作了多年的人,也可能有一些配置完全没用到过。但是,你知道有哪些配置,每个配置的作用是什么吗?举个简单的例子,你想让系统出现异常时自动发送邮件告警,该怎么做?自己写一个发邮件的功能?想太多了,看看这些配置,你就知道了,只需要配置几个参数就能达到目的。
- Applications:在Django 中,App 是一个很重要的概念,我们需要把业务拆分成不同的App来开发,那么如何管理这些App,如何在程序运行时获取到有哪些App,每个App 的信息是什么,这里可以了解到。
- Exceptions:异常是每个程序员(不论什么水平)都无法避免的问题,唯一的差别在于,水平较高的人能够轻松通过异常提示找到问题所在。这一部分列举了Django 中常用异常的定义,在什么情况下会出现异常。这部分只需浏览一遍即可,很多异常你在开发时会经常遇到,尤其是使用Django 的初期。
- django-admin and manage.py:这是每一个人接触Django 时用到的第一个命令,这部分详细介绍了django-admin 和manage.py 的使用方式,以及它们有哪些功能,比如最常用的runserver 以及migrate 等。另外,如果你需要自定义类似于runserver 这样的命令时,可以参考这部分。
- Testing:单元测试是开发过程中的重要一环,它可以帮助你编写更稳定的代码,避免开发中的一些问题。在Django 中编写TestCase 是相对容易的事,因为只需要根据它封装好的结构来编写自己的代码就可以了。通过掌握很少的知识,就能完成单元测试的编写。这一部分建议跟着写一遍,会有收获的。
- Deployment:代码开发完了,最终怎么上线,怎么部署,怎么给用户提供服务呢?这些问题可以在这一部分得到解决。这部分详细讲解了如何通过WSGI 结合其他程序来部署你的应用,如何配置静态文件,如何收集线上的异常。这一部分文档需要你反复查看,尤其是当你准备上线项目时,但要注意文档只是用来参考的。
6. 其余部分
Django 的文档非常丰富,除了上面介绍的几个比较大的主题外,还有很多小的主题也需要了解,这里就不详细罗列了,只是大概说说,等用到时,能够知道到哪儿去看即可。
- The admin:如果你需要做基于内容的管理系统,这部分是逃不掉的。当然,你可以选择不用它的admin,完全自己实现,但是相信我,掌握admin 能够大大提高开发效率,没有什么比定义好一个Model,简单配置后就能做出一个后台管理系统更爽的事了。不过话说回来,Django 文档上关于admin 部分的介绍没有那么多,但是从基本使用上来说是够用的。更多的定制需要去参考源码。
- Security:安全模块,这部分也是一个成熟框架的标志,一个成熟的框架一定经历了种种安全的检验。这部分介绍了常见的Web 开发的安全问题,以及Django 是如何处理的。如果打算做Web 开发,安全问题不可忽视!
- Internationalization and localization:国际化和本地化(也称i18n 和l10n)。如果你想要编写国际化的软件,详细阅读这部分必不可少。另外,Django 默认的语言是英语,时区是美国时间,因此在做admin 部分系统文案的展示时,需要修改默认语言为中文(zh-hans),如果要操作时间的话,需要把时区改为中国(Asia/Shanghai)。这里需要注意的是,Django在写数据库时,日期和时间用的都是UTC 时间。因此,我们需要在这一部分了解如何使用Django 提供的函数处理时区转换。
- Performance and optimization:性能和优化,它们也一样很重要!对于程序员来说,精确知道你写的代码做了什么事很重要。这部分算是Django 性能优化的索引。
- Geographic framework:这部分介绍了如何使用Django 开发GIS 系统,我在日常开发中没有用到,不做过多说明。
- Common Web application tools:这部分你会经常用到,涉及用户系统、权限系统、缓存、日志、邮件发送、RSS、分页、消息处理、序列化、session、sitemap、静态文件管理和数据校验等。随用随查即可,你需要知道的是Django 提供了这些功能。
- Other core functionalities:其他的一些核心功能,比如ETag 设置、内容类型和通用关系(如果你需要添加自定义的权限配置,或者想要了解Django 的权限逻辑时,需要了解这部分内容)、页面管理App、页面重定向模块、信号模块(这也很常用,尤其是当你打算监听某个模型的数据操作时)、静态检测系统、site 模块、Unicode 的使用等。随着对Django用得越多,这些东西慢慢地都可以掌握。
- The Django open-source project:这部分包含如何参与到Django 社区中,以及Django的发布流程、设计原则等一些社区文化类的文档。
总结
Django 文档已经相当棒了,非常完善,除了admin 部分写得不是很深入。这就涉及另外一个话题,看文档还是看代码。关于admin 部分,如果有较多的需求,建议看完文档后去摸索代码。如果你熟悉了前面介绍的几个层,那么admin 代码对你来说也不是什么难事儿,甚至在某种情况下学得更快。
除了admin 部分,其他的文档上基本都有。不过也没有必要像背字典那样去看文档,有空䁖一眼,遇到问题䁖一眼。随着实践越来越多,对文档会越来越熟悉,对它的依赖会越来越少。Django 的代码结构跟文档一样划分清晰,所以我现在大部分的问题都是靠读代码来解决,并且这是一个能够不断产生正向作用的方式,你越熟悉代码,越有助于写出更好的代码,越能理解Django 的源码思路。
最后需要提到的一点是,无论是Django 文档还是其他文档资料,都仅供参考。正确的答案始终是在你电脑上的代码里。不论是框架版本的差异,还是文档上的书写错误,都可能会捉弄你,而你电脑上的代码始终不会骗你。
另外,可能有人会觉得Django 的文档这么多,这么多功能什么时候才能充分掌握呀!这个问题完全不用担心,无论你选择什么框架,一开始接触的都是简单逻辑,随着使用经验的增长,你掌握的技能也越来越多,对Django 的了解也越来越多。
对于选择学习Django 来说,它提供了很清晰的使用路线,你不需要像学习其他框架那样,在入门之后还需要去网上搜罗各种资料才能明白接下来要干什么。
因此,选择学习Django,是一件性价比很高的事情。接下来,我们通过一个简单的例子来感受Django 的魅力。
学员管理系统的后台开发
在上一节中,你了解了Django 所提供的功能,这一节就来切实体会一下,快速过一下Django的各个模块。你最好打开熟悉的IDE(集成开发环境),一起写起来。建议你在Linux 上开始这个练习。无论你是否喜欢在Linux 上做开发,掌握如何配置Linux 的开发环境和部署环境都是实际工作中无法绕过的门槛。
一、需求
我们先用一句话来描述需求:提供一个学员管理系统,其中有一个前台页面来展示现有学员,并供新学员提交申请,一个后台负责处理申请。
接下来,我们逐步实现。
二、初始化环境
首先,创建虚拟环境:
mkvirtualenv student-env -p `which python3.6`
其中,最后的 -p 指明虚拟环境使用的是Python 3.6。不熟悉的可以看“使用virtualenv 创建虚拟Python 环境”,详见https://www.the5fire.com/virtualenv-python-env.html。或者使用python3.6 -mvenv student-env 来创建虚拟环境。
然后激活虚拟环境:
workon student-env
如果是后面的创建方式,需要通过source student-env/bin/activate 来激活。接着,安装Django,本书写作时的最新版为1.11:
pip install django~=1.11
这里解释一下,~=表示安装指定版本的最新版,这里会去安装Django 1.11.x 版本,x 表示1.11版本的最新版的小版本号。
三、创建项目
虽然可以不创建虚拟环境就安装Django,但是我还是建议你在虚拟环境中安装,因为在实际开发中,你可能需要维护不止一个项目。不同项目所依赖库的版本也不同,如果你都安装到root或者user 下,会出现冲突。
好了,切换到你喜欢的目录中,比如 /home/the5fire/workspace/。然后,创建项目根目录:mkdir student_house,这是我们的项目目录。接着,再来创建项目:cd student_house && django-admin startproject student_sys(注意:要保证上面的虚拟环境是激活状态)。
此时我们能得到下面的结构:
四、创建App
进入student_house/student_sys 中,通过上一步创建好的manage.py 创建一个App:python manage.py startapp student。现在目录结构如下:
创建好之后,我们开始编写代码。
五、编写代码
我们可以在Model 层开始写代码了,这是一个简单的需求,只需要一个Model 就可以满足。在文件student_house/student_sys/student/models.py 中编写如下代码:()
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
class Student(models.Model):
SEX_ITEMS = [
(1, '男'),
(2, '女'),
(0, '未知'),
]
STATUS_ITEMS = [
(0, '申请'),
(1, '通过'),
(2, '拒绝'),
]
name = models.CharField(max_length=128, verbose_name="姓名")
sex = models.IntegerField(choices=SEX_ITEMS, verbose_name="性别")
profession = models.CharField(max_length=128, verbose_name="职业")
email = models.EmailField(verbose_name="Email")
qq = models.CharField(max_length=128, verbose_name="QQ")
phone = models.CharField(max_length=128, verbose_name="电话")
status = models.IntegerField(choices=STATUS_ITEMS, default=0, verbose_name=
"审核状态")
created_time = models.DateTimeField(auto_now_add=True, editable=False,
verbose_name="创建时间")
def __str__(self):
return '<student>'.format(self.name)
class Meta:
verbose_name = verbose_name_plural = "学员信息"
/<student>
代码说明:如果是基于Python 3.6 的代码,可以去掉最上面的两行coding: utf-8 以及from__future__ import unicode_literals。这里加上这两行,是为了兼容Python2.7。这意味着,如果你们公司目前整体是基于Python 2.7 的,那应该尽量在代码上加上这两行,方便以后进行升级。
再来写admin.py:
from django.contrib import admin
from .models import Student
class StudentAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'sex', 'profession', 'email', 'qq', 'phone',
'status', 'created_time')
list_filter = ('sex', 'status', 'created_time')
search_fields = ('name', 'profession')
fieldsets = (
(None, {
'fields': (
'name',
('sex', 'profession'),
('email', 'qq', 'phone'),
'status',
)
}),
)
admin.site.register(Student, StudentAdmin)
写完这两个配置,Model 和admin 的界面就好了。接下来,我们把student 这个App 放到settings.py 中。
我们只需要在INSTALLED_APPS 配置的最后或者最前面增加'student'即可。
settings.py 文件如下:
INSTALLED_APPS = [
'student',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
好了,后台部分完成了。接着,我们通过下面的命令创建表以及超级用户。
- cd student_house/student_sys/:如果你已经在student_sys 目录下,就不用切换目录了。
- python manage.py makemigrations:创建数据库迁移文件。
- python manage.py migrate:创建表。
- python manage.py createsuperuser:根据提示,输入用户名、邮箱和密码。
此时通过 python manage.py runserver 命令启动项目后,访问 http://127.0.0.1:8000,会看到一个提示页,这是因为我们还没开发首页。我们可以进入到admin 的页面:http://127.0.0.1:8000/admin/。用你创建好的账户登录,就能看到一个完整的带有CURD 的后台了。
六、基础配置(中文)
通过上面的配置,你看到的界面应该是英文的,并且时区也是UTC 时区,所以需要进一步配置。
在settings 中做如下配置:
LANGUAGE_CODE = 'zh-hans' # 语言
TIME_ZONE = 'Asia/Shanghai' # 时区
USE_I18N = True # 语言
USE_L10N = True # 数据和时间格式
USE_TZ = True # 启用时区
修改完这些之后,刷新一下试试。你可以尝试修改上面的配置,看看分别对应什么功能。
到这一部分,我们基本完成了admin 的部分。在下一节中,我们来完成页面提交数据的部分,看下如何使用Form。
七、总结
对于简单的后台需求,通过admin 很容易满足,这也是Django admin 被称为“杀手锏”的原因。
学员管理系统的前台开发
有了上一节中Model 和admin 的部分,我们已经得到一个管理后台了。接着,我们来做一个简单的用户提交申请的表单页面。
一、开发首页
首先,在student/views.py 文件中编写下面的代码:
from django.shortcuts import render
def index(request):
words = 'World!'
return render(request, 'index.html', context={'words': words})
我们定义了函数index,其参数是request,它是Django 对用户发送过来的HTTP 请求的封装。从request 对象上,我们能得到一些有用的信息。在函数最后,我们用Django 提供的一个快捷方法render 来渲染页面,其中使用了模板文件index.html。我们需要在student 目录下创建templates 文件夹,这个文件夹是Django 在渲染页面时会默认查找的。
这部分需要多说几句。Django 在渲染模板或者静态页面时,会去每个App 下查找,这里说的App 就是settings.py 文件中配置的INSTALLED_APPS 中的App。Django 会去这些App 目录下的templates 文件夹查找你在render 上用到的模板,并且是顺序查找的(从上到下)。这意味着,如果你有两个App,比如studentA 和studentB,若这两个App 中都存在templates/index.html,那么Django 会加载位置在前的那个App 的index.html 文件。静态文件也是同样的逻辑,你可以自行尝试。通过这种方式,我们也可以覆盖admin 的部分模板。不过这是基于Django 内置的规则,有些隐晦。如果可以通过其他方式解决,不建议使用这种路径覆盖的方式。
创建好templates/index.html 之后,我们开始编写页面代码:
<title>学员管理系统-by the5fire/<title>
Hello {{ words }}!
这里面只用到一个Django 的模板语法——{{ words }}。这里需要注意,words 两侧的空格是依据“Django 编码规范”(详见https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/)来写的。这个语法的意思就是从上下文中获取到words 变量。这个变量是在index 中调用render 时传递过来的。
接着,再配置一下URL,也就是提供一个URL 映射,可以让用户访问URL 时把数据发送到我们定义的index 这个View 上。
我们直接修改student_sys 目录下的urls.py 文件:
from django.conf.urls import url
from django.contrib import admin
from student.views import index
urlpatterns = [
url(r'^$', index, name='index'),
url(r'^admin/', admin.site.urls),
]
然后通过python manage.py runserver 命令再次启动项目,访问http://127.0.0.1:8000,就能看到输出的Hello World!!了。
二、输出数据
接下来的工作就是把数据从数据库表里面取出来,渲染到页面上。你可以先在admin 后台创建几条学员数据,以便于测试。
我们需要修改views.py 中的代码:
from django.shortcuts import render
from .models import Student
def index(request):
students = Student.objects.all()
return render(request, 'index.html', context={'students': students})
先解释一下上面代码的意思:index 函数已经定义过了,也好理解,内部逻辑做了一些调整。首先,通过Student 模型拿到所有的student 数据,接着把数据放到context 里面传递到模板中。
接着,修改index.html 中的代码:
<title>学员管理系统-by the5fire/<title>
- {{ student.name }} - {{ student.get_status_display }}
{% for student in students %}
{% endfor %}
这样我们就输出了一个简单的列表,展示了学员名称和目前状态。这里有一个地方需要注意,那就是{{ student.get_status_display }}。在Model 中我们只定义了status 字段,并未定义这样的字段,为什么能通过这种方式取到数据呢?并且在admin 中,也没有使用这样的字段。
原因就是,对于设置了choices 的字段,Django 会帮我们提供一个方法(注意是方法),用来获取这个字段对应的要展示的值。回头看看status 的定义:
## 省略上下文代码
STATUS_ITEMS = [
(0, '申请'),
(1, '通过'),
(2, '拒绝'),
]
status = models.IntegerField(choices=STATUS_ITEMS, verbose_name="审核状态")
## 省略上下文代码
在admin 中,展示带有choices 属性的字段时,Django 会自动帮我们调用get_status_display方法,所以不用配置。而在我们自己写的模板中,这需要自己来写。并且为了简化模板的使用,默认只支持无参数的方法调用,你只需要写方法名称即可,后面的括号不能写,Django 会自行帮你调用(如果是方法的话)。
三、提交数据
输出数据之后,我们再来开发提交数据的功能,这部分我们用一下Form。
首先,创建一个forms.py 文件,它跟views.py 同级。编写如下代码:
from django import forms
from .models import Student
class StudentForm(forms.Form):
name = forms.CharField(label='姓名', max_length=128)
sex = forms.ChoiceField(label='性别', choices=Student.SEX_ITEMS)
profession = forms.CharField(label='职业', max_length=128)
email = forms.EmailField(label='邮箱', max_length=128)
qq = forms.CharField(label='QQ', max_length=128)
phone = forms.CharField(label='手机', max_length=128)
这个StudentForm 的定义是不是很熟悉?它跟Model 的定义类似,那么我们能不能复用Model 的代码呢?答案是:可以。还记得3.1 节文档介绍的部分吗?有一个ModelForm 可以用。
我们来改一下:
from django import forms
from .models import Student
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = (
'name', 'sex', 'profession',
'email', 'qq', 'phone'
)
只需要这么改就行了,不需要重复定义N 多个字段。如果有修改对应字段类型的需求,比如把qq 改成IntegerField 来做数字校验,也是可以声明出来的。当然,我们也可以通过定义clean 方法的方式来做。我们来改一下代码,增加QQ 号必须为纯数字的校验:
from django import forms
from .models import Student
class StudentForm(forms.ModelForm):
def clean_qq(self):
cleaned_data = self.cleaned_data['qq']
if not cleaned_data.isdigit():
raise forms.ValidationError('必须是数字!')
return int(cleaned_data)
class Meta:
model = Student
fields = (
'name', 'sex', 'profession',
'email', 'qq', 'phone'
)
其中clean_qq 就是Form 会自动调用来处理每个字段的方法。比如,在这个Form 中,你可以通过定义clean_phone 来处理电话号码,可以定义clean_email 来处理邮箱等。如果验证失败,可以通过raise forms.ValidationError('必须是数字!')的方式返回错误信息,这个信息会存储在Form中,最终会被我们渲染到页面上。
有了Form,接下来需要做的就是在页面中展示Form,让用户能够填写信息提交表单。同时对于提交的数据,我们需要先做校验,通过后再将其保存到数据库中。我们来看看views.py 最终的样子:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .forms import StudentForm
from .models import Student
def index(request):
students = Student.objects.all()
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
cleaned_data = form.cleaned_data
student = Student()
student.name = cleaned_data['name']
student.sex = cleaned_data['sex']
student.email = cleaned_data['email']
student.profession = cleaned_data['profession']
student.qq = cleaned_data['qq']
student.phone = cleaned_data['phone']
student.save()
return HttpResponseRedirect(reverse('index'))
else:
form = StudentForm()
context = {
'students': students,
'form': form,
}
return render(request, 'index.html', context=context)
里面有一个form.cleaned_data 对象,它是Form 根据字段类型对用户提交的数据做完转换之后的结果。另外,还用了reverse 方法。我们在urls.py 中定义index 的时候,声明了name='index',所以这里可以通过reverse 来拿到对应的URL。这么做的好处是,不需要硬编码URL 到代码中,这意味着如果以后有修改URL 的需求,只要index 的名称不变,这个地方的代码就不用改。
上面我们通过手动构建Student 对象的方法来保存Student 数据。其实在ModelForm中,因为有了Model 的定义,手动构建Student 对象的步骤可以省掉,我们可以将代码修改如下:
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.shortcuts import render
from .forms import StudentForm
from .models import Student
def index(request):
students = Student.objects.all()
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('index'))
else:
form = StudentForm()
context = {
'students': students,
'form': form,
}
return render(request, 'index.html', context=context)
对于HTTP GET 请求的数据,最后我们会把StudentForm 的实例传到模板中,这样用户最终才能看到一个可以填写数据的表单。要在模板中加form,是相当简单的一件事。最终模板index.html 的代码如下:
<title>学员管理系统-by the5fire/<title>
- {{ student.name }} - {{ student.get_status_display }}
{% for student in students %}
{% endfor %}
其中{% csrf_token %}是Django 对提交数据安全性做的校验,这意味着,如果没有这个token,提交过去的数据是无效的。这是用来防止跨站伪造请求攻击的一个手段。在Web 开发中,如果有类似需求的话,这也是我们可以用来参考的技术点。
只需要这么写,Django 就会帮我们自动把所有字段列出来。当然,如果需要调整样式,就要通过自己增加CSS 样式文件解决了。
四、优化数据,获取逻辑
在上述代码中,我们在view 函数中通过students = Student.objects.all()的方式获取到了所有学员的数据,但是这种方式在实际使用中会有问题。因为后期可能会对学员的数据进行过滤,比如需要调整为展示所有审核通过的学员,此时就需要调整view 函数中的代码。或者对结果进行缓存,也需要调整代码。
因此,可以把数据获取逻辑封装到Model 层中,对上暴露更语义化的接口。我们来稍微调整一下。
给models.py 中的Student 模型增加一个类方法:
# 省略其他代码
class Student(models.Model):
# 省略其他代码
@classmethod
def get_all(cls):
return cls.objects.all()
同时修改views.py 中的代码:
# 省略其他代码
def index(request):
students = Student.get_all()
# 省略其他代码
这样后期再修改需求时,只需要修改get_all 这个函数即可,改动的影响范围就小了很多。
五、总结
到此为止,功能上已经完备。你可以再次通过命令python manage.py runserver 启动项目,访问http://localhost:8000,看看页面上的功能是否可用。根据自己的想法调整代码,反复运行并查看效果,以便于理解Form的作用。
总结
这一部分中的三个(还有一个文中未提及)技能点有助于你一开始就建立好一个完整的模型,可以帮助你更好地理解Django。不过如果要更好地掌握这些内容,需要进一步实践才行。你需要做的就是把本文的代码完整敲一遍,然后根据自己的理解改改代码,接着不断地运行测试。
本文就到这里,打开你熟悉的IDE,开始运行上面的代码吧,会有很多收获。
——本文节选自《Django企业开发实战:高效Python Web框架指南》
资深开发工程师the5fire多年开发经验总结。
以从零开发一个博客系统为例,介绍Django在日常工作中的应用。
本书共分为四部分。第一部分介绍编码之前的准备工作,包括需求分析、Web开发基础以及选型时Demo的练习。第二部分开始正式实现需求,介绍了环境配置、编码规范以及合理的项目结构划分。通过对Django各部分(Model、Form、admin、View)的介绍和使用,完成了一个基础的博客系统。第三部分在前面的基础上介绍Django第三方插件的使用,通过引入这些插件进一步增强我们的系统。最后一部分也是正式工作中必不可少的部分,包含调试代码、优化系统、压力测试以及自动化等内容。
——
你需要花费更多的时间来学习 Django,这是因为它提供的内容远多于其他框架。刚开始,可能会觉得很多地方不明白,但是等你熟悉了之后就会发现,在 Web 应用开发上,Django 已经提供了很完备的支持,比如登录认证、权限管理、admin 管理后台、缓存系统、常见的 Web 安全防御等。Django 每一层或者模块所提供的功能都很清晰,比如什么样的需求在哪一层来处理,或者在哪个模块中处理。
Django 一开始的学习曲线有点陡,然后是平缓上升的。先陡主要是因为新手需要一下子接受很多东西,但是随着后面不断使用和了解,你会发现,学习所耗费的时间完全值得。你可以更快地做出完善的系统,这会是一笔很划算的投资。
第一部分 初入江湖
第1章 需求
第2章 框架基础和技术选型(到图灵社区阅读:http://www.ituring.com.cn/book/tupubarticle/25223)
第3章 Django小试牛刀
第二部分 正式开发
第4章 进入开发
第5章 奠定项目基石:Model
第6章 开发管理后台
第7章 开发面向用户的界面
第8章 引入前端样式框架Bootstrap
第9章 完成整个博客系统
第三部分 第三方插件的使用
第10章 使用第三方插件增强管理后台
第11章 使用django-rest-framework
第四部分 上线前的准备及线上问题排查
第12章 调试和优化
第13章 配置MySQL和缓存
第14章 上线前的准备
第15章 升级到Django 2.0
第16章 最后总结
附录A 使用Fabric 2.0
附录B 使用uWSGI来启动Django程序
附录C Sentry安装和配置
附录D 评论验证码功能
附录E 通过signal来解耦代码
附录F 实现文章置顶的几种方案
附录G 以腾讯云为例演示部署流程
閱讀更多 圖靈教育 的文章