转至:http://www.cnblogs.com/feixuelove1009/p/5910384.html#top
“全能”选手—Django 1.10文档中文版Part1
本文是博主翻译的Django1.10版本官方文档的第一部分,如时间充裕,争取一直翻译下去,经验不足,或有错漏,敬请指正。
另外对于公开文档进行翻译的版权问题不是很清楚,如有侵权请联系我!
另外,要转载该系列翻译文章,也请联系博主。
3.2 模型和数据库Models and databases
目录
第一章、Django1.10文档组成结构
1.1 获取帮助
1.2 文档的组织形式
1.3 第一步
1.4 模型层
1.5 视图层
1.6 模板层
1.7 表单
1.8 开发流程
1.9 admin站点
1.10 安全性
1.11 国际化和本地化
1.12 性能和优化
1.13 Python兼容性
1.14 地理框架
1.15 常用WEB应用工具
1.16 其它核心功能
1.17 Django开源项目
第二章、起步
2.1 Django速览
- 2.1.1 设计你的模型model
- 2.1.2 安装model
- 2.1.3 使用API
- 2.1.4 功能强大的动态admin后台管理界面
- 2.1.5 设计你的路由系统URLs
- 2.1.6 编写你的视图views
- 2.1.7 设计你的模板
- 2.1.8 总结
2.2 快速安装指南
- 2.2.1 安装Python
- 2.2.2 安装Django
- 2.2.3 安装验证
2.3 第一个Django app,Part 1:请求和响应
- 2.3.1 创建project
- 2.3.2 开发服务器development server
- 2.3.3 创建投票程序(polls app)
- 2.3.4 编写视图
2.4 第一个Django app,Part 2:模型和admin站点
- 2.4.1 数据库安装
- 2.4.2 创建模型models
- 2.4.3 激活模型
- 2.4.4 学会使用API
- 2.4.5 Django admin站点介绍
第一章、Django1.10文档组成结构
1.1 获取帮助
获取帮助的方式:
- 参考第5.1节的FAQ,这里包含许多常见问题的回答
- 查找特定的信息?尝试一下智能检索、混合索引或者内容详细表
- 在10.2.1的DJango用户邮件列表中查找信息或发布问题
- 在django的IRC频道提问或者查找IRC日志看看是否已经有类似问题
- 报告DJango中的bug
1.2 文档的组织形式
Django有许多文档,一个高层次的概览有助于你快速找到想要的信息:
- 入门教程:手把手的教会你创建一个WEB应用。如果你是个Django或者WEB应用开发者新手,从这里开始学习是个不错的选择。也可以先看看下面的“第一步”。
- 主题向导:讨论一些比较关键的主题和概念,属于比较高的水平,提供一些有用的背景信息和解释。
- 参考指南:包含API的使用指导和一些Django内核的其它方面。主要描述Django是怎么工作的,如何使用它。学习这部分的前提是你需要对一些关键概念具有基本的理解和掌握。
- “怎么办”向导:主要讨论一些关键的问题和使用场景。比入门教程更深入,需要你对Django的工作机制有一定的了解。
1.3 第一步
如果你是Django或程序员新手,那么从这里开始吧!
- 从零开始:概览 | 安装
- 入门教程:Part 1:请求和响应 | Part 2:模型和admin站点 | Part 3:视图和模板 | Part 4:表单和泛型 | Part 5:测试 | Part 6:静态文件 | Part 7:自定制admin站点
- 高级教程: 如何编写可重用的应用 | 编写你的第一个Django补丁
1.4 模型层
Django提供了一个抽象的模型层,用于组织和操纵你的WEB应用数据。
- 模型:模型介绍 | 字段类型 | Meta选项 | 模型类
- 查询结果集:执行查询 | 查询结果集方法参考 | 查询表达式
- 模型实例: 实例方法 | 访问关系对象
- 迁移: 迁移介绍 | 操作参考 | 计划编辑 | 编写迁移
- 高级: 管理器 | 原生SQL | 事务 | 聚合 | 查找 | 自定义字段 | 多数据库 | 自定义查询 | 查询表达式 | 条件表达式 | 数据库函数
- 其它:支持的数据库 | 遗留数据库 | 提供初始化数据 | 优化数据库访问 | PostgreSQL特有
1.5 视图层
Django将封装响应用户请求逻辑和返回数据称为“视图”。在下面的列表中,你能找到所有和视图相关的内容:
- 基础: 路由配置 | 视图函数 | 快捷方式 | 装饰器
- 参考: 内置视图 | 请求/响应对象 | 模板响应对象
- 文件上传: 概览 | 文件对象 | 储存API | 管理文件 | 自定义存储
- 基类视图: 概览 | 内置显示视图 | 内置编辑视图 | 混合使用 | API参考 | 分类索引
- 高级: 生成CSV | 生成PDF
- 中间件:概览 | 内置中间件类
1.6 模板层
模板层提供一种人性化的语法,用于渲染展示给用户的内容,主要内容包括下列:
- 基础:概览
- 对于设计师:语言概览 | 内置标签和过滤器 | 人性化
- 对于程序员: 模板API | 自定义标签和过滤器
1.7 表单
Django 提供了一个内容丰富的框架可便利地创建表单及操作表单数据。
- 基础: 概览 | 表单 API | 内置字段 | 内置小工具
- 高级: 模型表单 | 表单外观 | 表单集 | 自定义验证
1.8 开发流程
学习不同的组件和工具,帮助你开发和测试Django应用。
- 设置:概览 | 全部设置列表
- 应用: 概览
- 异常:概览
- Django-admin和manage.py:概览 | 添加自定义命令
- 测试:介绍 | 编写和运行测试 | 导入测试工具 | 高级主题
- 部署:概览 | WSGI服务器 | 部署静态文件 | 通过邮件跟踪代码错误
1.9 admin站点
在这里,你可以找到Django中最受欢迎的功能模块——admin站点的一切:
- admin站点
- admin动作
- admin文档生成器
1.10 安全性
开发 Web 应用时安全是最重要一个的主题,Django 提供了多重保护工具和机制:
- 安全概览
- 披露的Django安全问题
- 点击劫持的防护
- 跨站请求伪造防护
- 加密签名
- 安全中间件
1.11 国际化和本地化
Django 提供了一个强大的国际化和本地化框架,以协助您开发支持多国语言和世界各地区的应用:
- 概览 | 国际化 |本地化 | 本地WEB UI 格式化和表单输入
- 时区
1.12 性能和优化
有许技术和工具可以帮助你的代码运行得更加高效、快速,占用更少的系统资源。
- 性能和优化概览
1.13 Python兼容性
Django 希望兼容多个不同特性和版本的 Python:
- Jython支持
- Python3 兼容性
1.14 地理框架
GeoDjango 想要做一个世界级的地理Web框架。 它的目标是尽可能轻松的构建GIS Web 应用和发挥空间数据的能力。
1.15 常用WEB应用工具
Django 为开发Web应用提供了多种常见的工具:
- 身份认证:概览 | 使用认证系统 | 密码管理 | 自定义认证 | API参考
- 缓存
- 日志
- 发送邮件
- 联合供稿(RSS/Atom)
- 分页
- 消息框架
- 序列化
- 会话
- 站点地图
- 静态文件管理
- 数据验证
1.16 其它核心功能
Django的其它核心功能包括:
- 按需处理内容
- 内容类型和泛型关系
- 简单页面
- 重定向
- 信号
- 系统检查框架
- 站点框架
- 在 Django 中使用 Unicode
1.17 Django开源项目
下面是Django项目本身的开发进程和如何做出贡献相关:
- 社区: 如何参与 | 发布过程 | 团队组织 | 团队会议 | 主要人员 | 源码仓库 | 安全策略 | 邮件列表
- 设计理念: 概述
- 文档: 关于本文档
- 第三方发行: 概述
- Django 的过去: API 稳定性 | 发行说明和升级说明 | 功能弃用时间轴
第二章、起步
2.1 Django速览
Django的开发背景是快节奏的新闻编辑室环境,因此它被设计成一个大而全的web框架,能够快速简单的完成任务。本节将快速介绍如何利用Django搭建一个数据库驱动的WEB应用。
它不会有太多的技术细节,只是让你理解Django是如何工作的。
2.1.1 设计你的模型model
Django提供了ORM,通过它,你能直接使用Python代码来描述你的数据库设计。下面是一个例子:
# mysite/news/models.py
from django.db import models class Reporter(models.Model): full_name = models.CharField(max_length=70) def __str__(self): # __unicode__ on Python 2 return self.full_name class Article(models.Model): pub_date = models.DateField() headline = models.CharField(max_length=200) content = models.TextField() reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE) def __str__(self): # __unicode__ on Python 2 return self.headline
2.1.2 安装model
接下来,进入Django命令行工具,创建数据库表:$ python manage.py migrate
migrate命令查找所有可用的model,如果它还没有在数据库中存在,将根据model创建相应的表。注:也许你需要先执行$ python manage.py makemigrations
命令。
2.1.3 使用API
Django为你提供了大量的方便的数据库操作API,无需你编写额外的代码。下面是个例子:
# Import the models we created from our "news" app
>>> from news.models import Reporter, Article # No reporters are in the system yet. >>> Reporter.objects.all() <QuerySet []> # Create a new Reporter. >>> r = Reporter(full_name='John Smith') # Save the object into the database. You have to call save() explicitly. >>> r.save() # Now it has an ID. >>> r.id 1 # Now the new reporter is in the database. >>> Reporter.objects.all() <QuerySet [<Reporter: John Smith>]> # Fields are represented as attributes on the Python object. >>> r.full_name 'John Smith' # Django provides a rich database lookup API. >>> Reporter.objects.get(id=1) <Reporter: John Smith> >>> Reporter.objects.get(full_name__startswith='John') <Reporter: John Smith> >>> Reporter.objects.get(full_name__contains='mith') <Reporter: John Smith> >>> Reporter.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Reporter matching query does not exist. # Create an article. >>> from datetime import date >>> a = Article(pub_date=date.today(), headline='Django is cool', ... content='Yeah.', reporter=r) >>> a.save() # Now the article is in the database. >>> Article.objects.all() <QuerySet [<Article: Django is cool>]> # Article objects get API access to related Reporter objects. >>> r = a.reporter >>> r.full_name 'John Smith' # And vice versa: Reporter objects get API access to Article objects. >>> r.article_set.all() <QuerySet [<Article: Django is cool>]> # The API follows relationships as far as you need, performing efficient # JOINs for you behind the scenes. # This finds all articles by a reporter whose name starts with "John". >>> Article.objects.filter(reporter__full_name__startswith='John') <QuerySet [<Article: Django is cool>]> # Change an object by altering its attributes and calling save(). >>> r.full_name = 'Billy Goat' >>> r.save() # Delete an object with delete(). >>> r.delete()
2.1.4 功能强大的动态admin后台管理界面
Django包含一个功能强大的admin后台管理模块,使用方便,要素齐全。有助于你快速开发。你只需要在下面两个文件中写几句短短的代码:
mysite/news/models.py
from django.db import models class Article(models.Model): pub_date = models.DateField() headline = models.CharField(max_length=200) content = models.TextField() reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
mysite/news/admin.py
from django.contrib import admin from . import models admin.site.register(models.Article)
2.1.5 设计你的路由系统URLs
Django主张干净、优雅的路由设计,不建议在路由中出现类似.php或.asp之类的字眼。
路由都写在URLconf文件中,它建立起URL匹配模式和python毁掉函数之间的映射,起到了解耦的作用。下面是一个例子:
mysite/news/urls.py
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
Django通过正则表达式,分析访问请求的url地址,匹配相应的views,调用对应的函数。
2.1.6 编写你的视图views
每一个视图都必须做下面两件事情之一:返回一个包含请求页面数据的HttoResponse对象或者弹出一个类似404页面的异常。
通常,视图通过参数获取数据,并利用它们渲染加载的模板。下面是一个例子:
mysite/news/views.py
from django.shortcuts import render from .models import Article def year_archive(request, year): a_list = Article.objects.filter(pub_date__year=year) context = {'year': year, 'article_list': a_list} return render(request, 'news/year_archive.html', context)
2.1.7 设计你的模板
Django有一个模板查找路径,在settings文件中,你可以指定路径列表,Django自动按顺序在列表中查找你调用的模板。一个模板看起来是下面这样的:
mysite/news/templates/news/year_archive.html
{% extends "base.html" %}
{% block title %}Articles for {{ year }}{% endblock %}
{% block content %}
<h1>Articles for {{ year }}</h1> {% for article in article_list %} <p>{{ article.headline }}</p> <p>By {{ article.reporter.full_name }}</p> <p>Published {{ article.pub_date|date:"F j, Y" }}</p> {% endfor %} {% endblock %}
Django使用自己的模板渲染语法,Jinja2就是参考它设计出来的。双大括号包含起来的是变量,它将被具体的值替换。圆点不但可以用来查询属性,也可以用来调用字典键值,列表索引和调用函数。
Django具有模板继承、导入和加载的概念,分别使用extend、include和load语法。下面是一个基础模板大概的样子:
mysite/templates/base.html
{% load static %}
<html>
<head> <title>{% block title %}{% endblock %}</title> </head> <body> <img src="{% static "images/sitelogo.png" %}" alt="Logo" /> {% block content %}{% endblock %} </body> </html>
子模板继承母模板的内容,并加入自己独有的部分。通过更换母版,可以快速的修改整改站点的外观和样式。
2.1.8 总结
Django为你提供了大量的模块和组件,包括模板系统、模型系统、视图系统以及其他一些通用组件和专用组件。他们之间都是独立的,同时也是可选的,你完全可以使用自己的模板、模型、视图。但是,Django给你提供的是一个集成度高的高效率整体框架,如果你自己的水平不是很高,那建议还是使用Django提供的吧。
2.2 快速安装指南
在第三章的第一节有详细的安装指南,这里只是一个简单的安装向导,用于快速搭建环境进入下面的章节。
2.2.1 安装Python
Django与python版本的对应关系。
Django version | Python versions |
---|---|
1.8 | 2.7, 3.2 (until the end of 2016), 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.5+ |
请前往Python官网下载并安装python。 另外,python和Django自带轻量级数据库SQLite3,因此,在学习阶段你无需安装并配置其他的数据库。
2.2.2 安装Django
如果你是通过升级的方式安装Django,那么你需要先卸载旧的版本,具体查看3.1.4节。
你可以通过下面的3种方法安装Django:
- 安装你的操作系统提供的发行版本
- 安装官方版本(推荐)
- 安装开发版本
这里,请前往Django官网下载最新版本或通过pip3 install django进行安装。
下面是Django官方对版本的支持策略:
而这是未来发布版本的路线图:
2.2.3 安装验证
进入python环境,输入下列命令,注意版本号和你新安装的一致:
>>> import django
>>> print(django.get_version())
1.10
或者使用命令$ python -m django --version
查看版本号。
下面,我们将进入官方文档提供的编写第一个Django app教程!
2.3 第一个Django app,Part 1:请求和响应
在这个例子中,我们将编写一个问卷调查网站,它包含下面两部分:
- 一个可以让人们进行投票和查看结果的公开站点
- 一个让你可以进行增删改查的后台admin管理界面
本教程使用Django 1.10 及Python 3.4以上版本!
2.3.1 创建project
进入你指定的某个目录,运行下面的命令:$ django-admin startproject mysite
这将在目录下生成一个mysite目录,也就是你的这个Django项目的根目录。它包含了一系列自动生成的目录和文件,具备各自专有的用途。注意:在给项目命名的时候必须避开Django和Python的保留关键字,比如“django”,“test”等,否则会引起冲突和莫名的错误。对于mysite的放置位置,不建议放在传统的/var/wwww目录下,它会具有一定的数据暴露危险,因此Django建议你将项目文件放在例如/home/mycode类似的位置。
一个新建立的项目结构大概如下:
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
详细解释:
- 外层的mysite/目录与Django无关,只是你项目的容器,可以任意命名。
- manage.py:一个命令行工具,用于与Django进行不同方式的交互脚本,非常重要!
- 内层的mysite/目录是真正的项目文件包裹目录,它的名字是你引用内部文件的包名,例如:mysite.urls。
- mysite/init.py:一个定义包的空文件。
- mysite/settings.py:项目的主配置文件,非常重要!
- mysite/urls.py:路由文件,所有的任务都是从这里开始分配,相当于Django驱动站点的内容表格,非常重要!
- mysite/wsgi.py:一个基于WSGI的web服务器进入点,提供底层的网络通信功能,通常不用关心。
2.3.2 开发服务器development server
进入mystie目录,输入下面的命令:$ python manage.py runserver
你会看到下面的提示:
Performing system checks...
System check identified no issues (0 silenced).
You have unapplied migrations; your app may not work properly until they are applied.
Run `python manage.py migrate' to apply them.
September 07, 2016 - 15:50:53 Django version 1.10, using settings `mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Django提供了一个用于开发的web服务器,使你无需配置一个类似Ngnix的线上服务器,就能让站点运行起来。但你也不要将开发服务器用于生产环境,它只是一个简易的测试服务器。
现在,在浏览器访问http://127.0.0.1:8000/,你将看到Django的欢迎阴面,一切OK!
django开发服务器(以后省略)默认运行在内部的8000端口,如果你想指定,请在命令中显示给出,例如:$ python manage.py runserver 0.0.0.0:8000
上面:Django将运行在8000端口,整个子网内都将可以访问,而不是本机。
注意: Django的开发服务器具有自动重载功能,当你的代码有修改,每隔一段时间服务器将自动更新。但是,有一些例如增加文件的动作,不会触发服务器重载,这时就需要你自己手动重启。
2.3.3 创建投票程序(polls app)
app与project的区别:
- 一个app实现某个功能,比如博客、公共档案数据库或者简单的投票系统;
- 一个project是配置文件和多个app的集合,他们组合成整个站点;
- 一个project可以包含多个app;
- 一个app可以属于多个project!
app的存放位置可以是任何地点,但是通常我们将它们都放在与manage.py同级目录下,这样方便导入文件。
进入mysite目录,确保与manage.py文件处于同一级,输入下述命令:$ python manage.py startapp polls
系统会自动生成 polls目录,其结构如下:
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
2.3.4 编写视图
在polls/views.py文件中,输入下列代码:
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
为了调用该视图,我们还需要编写urlconf。现在,在polls目录中新建一个文件,名字为urls.py,在其中输入代码如下:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), ]
下一步是让项目的主urls文件指向我们建立的polls这个app独有的urls文件,你需要先导入include模块,打开mysite/urls.py文件,代码如下:
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^polls/', include('polls.urls')), url(r'^admin/', admin.site.urls), ]
include语法相当于二级路由策略,它将接收到的url地址去除了它前面的正则表达式,将剩下的字符串传递给下一级路由进行判断。在路由的章节,有更加详细的用法指导。
include的背后是一种即插即用的思想。项目根路由不关心具体app的路由策略,只管往指定的二级路由转发,实现了解耦的特性。app所属的二级路由可以根据自己的需要随意编写,不会和其它的app路由发生冲突。app目录可以放置在任何位置,而不用修改路由。这是软件设计里很常见的一种模式。
建议:除了admin路由外,你应该尽量给每个app设计自己独立的二级路由。
好了,路由也搭建成功,下面我们启动服务器,然后在浏览器中访问地址http://localhost:8000/polls/
。一切正常的话,你将看到“Hello, world. You’re at the polls index.”
url()函数可以传递4个参数,其中2个是必须的:regex和view,以及2个可选的参数:kwargs和name。下面是具体的解释:
regex:
regex是正则表达式的通用缩写,它是一种匹配字符串或url地址的语法。Django拿着用户请求的url地址,在urls.py文件中对urlpatterns列表中的每一项条目从头开始进行逐一对比,一旦遇到匹配项,立即执行该条目映射的视图函数或二级路由,其后的条目将不再继续匹配。因此,url路由的编写顺序至关重要!
需要注意的是,regex不会去匹配GET或POST参数或域名,例如对于https://www.example.com/myapp/,regex只尝试匹配myapp/。对于https://www.example.com/myapp/?page=3,regex也只尝试匹配myapp/。
如果你想深入研究正则表达式,可以读一些相关的书籍或专论,但是在Django的实践中,你不需要多高深的正则表达式知识。
性能注释:正则表达式会进行预先编译当URLconf模块加载的时候,因此它的匹配搜索速度非常快,你通常感觉不到。
view:
当正则表达式匹配到某个条目时,自动将封装的HttpRequest对象作为第一个参数,正则表达式“捕获”到的值作为第二个参数,传递给该条目指定的视图。如果是简单捕获,那么捕获值将作为一个位置参数进行传递,如果是命名捕获,那么将作为关键字参数进行传递。
kwargs:
任意数量的关键字参数可以作为一个字典传递给目标视图。
name:
对你的URL进行命名,可以让你能够在Django的任意处,尤其是模板内显式地引用它。相当于给URL取了个全局变量名,你只需要修改这个全局变量的值,在整个Django中引用它的地方也将同样获得改变。这是极为古老、朴素和有用的设计思想,而且这种思想无处不在。
2.4 第一个Django app,Part 2:模型和admin站点
接着上一部分,本节将讲述如何安装数据库,编写第一个模型以及简要的介绍下Django自动生成的admin站点。
2.4.1 数据库安装
打开mysite/settings.py配置文件。Django默认使用内置的SQLite数据库。当然,如果你是在创建一个实际的项目,请使用类似MySql的生产用数据库,避免以后面临数据库切换的头疼。
如果你想使用别的数据库,请先安装相应的数据库模块,并将settings文件中DATABASES ’default’的键值进行相应的修改,用于连接你的数据库。其中:
ENGINE(引擎):可以是’django.db.backends.sqlite3’或者’django.db.backends.postgresql’,’django.db.backends.mysql’, or ’django.db.backends.oracle’,当然其它的也行。
NAME(名称):数据库的名字。如果你使用的是默认的SQLite,那么数据库将作为一个文件将存放在你的本地机器内,NAME应该是这个文件的完整绝对路径,包括文件名。设置中的默认值os.path.join(BASE_DIR, ’db.sqlite3’),将把该文件储存在你的项目目录下。
如果你不是使用默认的SQLite数据库,那么一些诸如USER,PASSWORD和HOST的参数必须手动指定!更多细节参考后续的数据库章节。
注意:
- 在使用非SQLite的数据库时,请务必首先在数据库提示符交互模式下创建数据库,你可以使用命令:“CREATE DATABASE database_name;”。
- 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据库。
- 如果你使用的是SQLite,那么你无需做任何预先配置,直接使用就可以了。
在修改settings文件时,请顺便将TIME_ZONE设置为你所在的时区。
同时,请注意settings文件中顶部的INSTALLED_APPS设置项。它保存了所有的在当前项目中被激活的Django应用。你必须将你自定义的app注册在这里。每个应用可以被多个项目使用,而且你可以打包和分发给其他人在他们的项目中使用。
默认情况,INSTALLED_APPS中会自动包含下列条目,它们都是Django自动生成的:
- django.contrib.admin:admin站点
- django.contrib.auth:身份认证系统
- django.contrib.contenttypes:内容类型框架
- django.contrib.sessions:会话框架
- django.contrib.messages:消息框架
- django.contrib.staticfiles:静态文件管理框架
上面的每个应用都至少需要使用一个数据库表,所以在使用它们之前我们需要在数据库中创建这些表。使用这个命令:$ python manage.py migrate。
migrate命令将遍历INSTALLED_APPS设置中的所有项目,在数据库中创建对应的表,并打印出每一条动作信息。如果你感兴趣,可以在你的数据库命令行下输入:\dt
(PostgreSQL), SHOW TABLES; (MySQL), 或 .schema (SQLite) 来列出 Django 所创建的表。
提示:对于极简主义者,你完全可以在INSTALLED_APPS内注释掉任何或者全部的Django提供的通用应用。这样,migrate也不会再创建对应的数据表。
2.4.2 创建模型models
Django通过自定义python类的形式来定义具体的模型,每个模型代表数据库中的一张表,每个类的实例代表数据表中的一行数据,类中的每个变量代表数据表中的一列字段。Django通过ORM对数据库进行操作,奉行代码优先的理念,将python程序员和数据库管理员进行分工解耦。
在这个简单的投票应用中,我们将创建两个模型:Question和Choice。Question包含一个问题和一个发布日期。Choice包含两个字段:选择的文本和投票计数。每一条Choice都关联到一条Question。这些都是由python的类来体现,编写的全是python的代码,不接触任何sql语句。现在,编辑polls/models.py文件,具体代码如下:
polls/models.py
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
上面的代码非常简单明了。每一个类都是django.db.models.Model的子类。每一个字段都是Field类的一个实例,例如用于保存字符数据的CharField和用于保存时间类型的DateTimeField,它们告诉Django每一个字段保存的数据类型。
每一个 Field 实例的名字就是字段的名字(如: question_text 或者 pub_date )。在你的Python代码中会使用这个值,你的数据库也会将这个值作为表的列名。
你也可以在每个Field中使用一个可选的第一位置参数用于提供一个人类可读的字段名,让你的模型更友好,更易读,并且将被作为文档的一部分来增强代码的可读性。在本例中,仅定义了一个符合人类习惯的字段名Question.pub_date。对于模型中的其他字段,机器名称就已经足够我们认读了。
一些Field类必须提供某些特定的参数。例如CharField需要你指定max_length。这不仅是数据库结构的需要,同样也用于我们后面会谈到的数据验证功能。
有必填参数,当然就会有可选参数,比如在votes里我们将其默认值设为0.
最后请注意,我们使用ForeignKey定义了一个外键关系。它告诉Django,每一个Choice关联到一个对应的Question。Django支持通用的数据关系:一对一,多对一和多对多。
2.4.3 激活模型
上面的代码看着有点少,但却给予Django大量的信息,据此,Django会做下面两件事:
- 创建该app对应的数据库表结构
- 为Question和Choice对象创建基于python的数据库访问API
但是,首先,我们得先告诉项目,我们已经安装了投票应用。
Django思想:应用是“可插拔的”:你可以在多个项目使用一个应用,你也可以分发应用,它们不会被捆绑到一个给定的 Django 项目中。
要将应用添加到项目中,需要在INSTALLED_APPS设置中增加指向该应用的配置文件的链接。对于本例的投票应用,它的配置类文件是polls/apps.py,路径格式为’polls.apps.PollsConfig’。我们需要在INSTALLED_APPS中,将该路径添加进去。它看起来是这样的:
mysite/settings.py
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
现在Django已经知道你的投票应用的存在了,并把它加入了大家庭。我们再运行下一个命令:$ python manage.py makemigrations polls。你会看到类似下面的提示:
Migrations for 'polls':
polls/migrations/0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice
通过运行migrations命令,相当于告诉Django你对你的模型有改动,并且你想把这些改动保存为一个“迁移”。
migrations是Django保存模型修改记录的文件,它们是保存在磁盘上的文件。在例子中,它就是polls/migrations/0001_initial.py文件,你可以打开它看看,里面保存的都是可编辑的内容,方便你随时手动修改。
接下来有一个叫做migrate的命令将对数据库执行真正的迁移动作,下面我们就要介绍它。但是,在此之前,让我们先看看在migration的时候实际试行的SQL语句是什么。有一个叫做sqlmigrate的 命令可以展示sql语句,例如:
$ python manage.py sqlmigrate polls 0001
你将会看到如下类似的文本(经过适当的格式调整,方便阅读):
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ( "id" serial NOT NULL PRIMARY KEY, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL ); -- -- Create model Question -- CREATE TABLE "polls_question" ( "id" serial NOT NULL PRIMARY KEY, "question_text" varchar(200) NOT NULL, "pub_date" timestamp with time zone NOT NULL ); -- -- Add field question to choice -- ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL; ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT; CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id"); ALTER TABLE "polls_choice" ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id" FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED; COMMIT;
请注意:
- 实际的输出内容将取决于您使用的数据库会有所不同。上面的是PostgreSQL的输出。
- 表名是自动生成的,通过组合应用名 (polls) 和小写的模型名 – question 和 choice 。 ( 你可以重写此行为。)
- 主键 (IDs) 是自动添加的。( 你也可以重写此行为。)
- 按照惯例,Django 会在外键字段名上附加 "_id" 。 (你仍然可以重写此行为。)
- 外键关系由FOREIGN KEY显示声明。不要担心DEFERRABLE部分,它只是告诉PostgreSQL不要实施外键直到事务结束。
- 生成 SQL 语句时针对你所使用的数据库,会为你自动处理特定于数据库的字段,例如 auto_increment (MySQL), serial (PostgreSQL), 或integer primary key (SQLite) 。 在引用字段名时也是如此 – 比如使用双引号或单引号。
- 这些 sql 命令并比较在你的数据库中实际运行,它只是在屏幕上显示出来,以便让你了解 Django 真正执行的是什么。
如果你感兴趣,也可以运行python manage.py check命令,它将检查项目中所有没有进行迁移或者链接数据库的错误。
现在,我们可以运行migrate命令,在数据库中进行真正的表操作了。
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表上面。Django通过一张叫做django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些migrations尚未提交。
migrations的功能非常强大,允许你随时修改你的模型,而不需要删除或者新建你的数据库或数据表,在不丢失数据的同时,实时动态更新数据库。我们将在后面的章节对此进行深入的阐述,但是现在,我们只需要记住修改模型时的操作分三步:
- 在models.py中修改模型
- 运行python manage.py makemigrations为改动创建迁移记录
- 运行python manage.py migrate,将迁移同步到数据库,落实修改动作。
之所以要将创建和实施迁移的动作分成两个命令两步走是因为你也许要通过版本控制系统(例如github,svn)提交你的项目代码,如果没有一个中间过程的保存文件(migrations),那么github如何知道以及记录、同步、实施你所进行过的模型修改动作呢?毕竟,github不和数据库直接打交道,也没法和你本地的数据库打交道。但是分开之后,你只需要将你的migration文件(例如上面的0001)上传到github,它就会知道一切。
2.4.4 学会使用API
下面,让我们进入python交互环境,学习使用Django提供的数据库访问API。要进入python的shell,请输入命令:
$ python manage.py shell
相比较直接输入“python”命令的方式进入python环境,调用manage.py参数能将DJANGO_SETTINGS_MODULE环境变量导入,它将自动按照mysite/settings.py中的设置,配置好你的python shell环境,这样,你就可以导入和调用任何你项目内的模块了。
或者你也可以这样,先进入一个纯净的python shell环境,然后启动Django,具体如下:
>>> import django
>>> django.setup()
如果上述操作出现AttributeError异常,有可能是你正在使用一个和当前教程不匹配的Django版本。解决办法是学习较低版本的教程或更换更新版本的Django。
不管是哪种方式,你最终都是让python命令能够找到正确的模块地址,得到正确的导入。
当你进入shell后,尝试一下下面的API吧:
>>> from polls.models import Question, Choice # 导入我们写的模型类 # 现在系统内还没有questions >>> Question.objects.all() <QuerySet []> # 创建一个新的question # Django推荐使用timezone.now()代替python内置的datetime.datetime.now() from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 你必须显式的调用save()方法,才能将对象保存到数据库内 >>> q.save() # 默认情况,你会自动获得一个自增的名为id的主键 >>> q.id 1 # 通过python的属性调用方式,访问模型字段的值 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) # 通过修改属性来修改字段的值,然后显式的调用save方法进行保存。 >>> q.question_text = "What's up?" >>> q.save() # objects.all() 用于查询数据库内的所有questions >>> Question.objects.all() <QuerySet [<Question: Question object>]>
这里等一下:上面的是一个不可读的内容展示,你无法从中获得任何直观的信息,为此我们需要一点小技巧,让Django在打印对象时显示一些我们指定的信息。返回polls/models.py文件,修改一下question和Choice这两个类,代码如下:
polls/models.py
from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible # 当你想支持python2版本的时候才需要这个装饰器 class Question(models.Model): # ... def __str__(self): # 在python2版本中使用的是__unique__ return self.question_text @python_2_unicode_compatible class Choice(models.Model): # ... def __str__(self): return self.choice_text
这个技巧不但对你打印对象时很有帮助,在你使用Django的admin站点时也同样有帮助。
请注意,这些都是普通的Python方法。下面我们自定义一个方法,作为示范:
polls/models.py
import datetime
from django.db import models from django.utils import timezone class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
请注意上面分别导入了两个关于时间的模块,一个是python内置的datetime一个是Django工具包提供的timezone。
保存修改后,我们重新启动一个新的python shell,再来看看其他的API:
>>> from polls.models import Question, Choice # 先看看__str__()的效果,直观多了吧? >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # Django提供了大量的关键字参数查询API >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> >>> Question.objects.filter(question_text__startswith='What') <QuerySet [<Question: What's up?>]> # 获取今年发布的问卷 >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # 查询一个不存在的ID,会弹出异常 >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # Django为主键查询提供了一个缩写:pk。下面的语句和Question.objects.get(id=1)效果一样. >>> Question.objects.get(pk=1) <Question: What's up?> # 看看我们自定义的方法用起来怎么样 >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True # 让我们试试主键查询 >>> q = Question.objects.get(pk=1) # 显示所有与q对象有关系的choice集合,目前是空的,还没有任何关联对象。 >>> q.choice_set.all() <QuerySet []> # 创建3个choices. >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # Choice对象可通过API访问和他们关联的Question对象 >>> c.question <Question: What's up?> # 同样的,Question对象也可通过API访问关联的Choice对象 >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 # API会自动进行连表操作,通过双下划线分割关系对象。连表操作可以无限多级,一层一层的连接。 # 下面是查询所有的Choices,它所对应的Question的发布日期是今年。(重用了上面的current_year结果) >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> # 使用delete方法删除对象 >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
获取更多关于模型关系的信息,请查看6.15.4章节。更多的数据库API和如何使用双下划线进行查询,请看3.2.2章节。
2.4.5 Django admin站点介绍
设计理念:为你的团队或客户编写用于增加、修改和删除内容的admin站点是一件非常乏味的工作并且没有多少创造性。因此,Django自动地为你通过模型构造了一个admin站点。这个站点只给站点管理员使用,并不对大众开放。
- 创建管理员用户
首先,通过下面的命令,创建一个可以登录admin站点的用户:
$ python manage.py createsuperuser
输入用户名:
Username: admin
输入邮箱地址:
Email address: admin@example.com
输入密码:
Password: **********
Password (again): *********
Superuser created successfully.
注意:Django1.10版本后,超级用户的密码强制要求具备一定的复杂性,不能再偷懒了。
- 启动开发服务器
服务器启动后,在浏览器访问http://127.0.0.1:8000/admin/。你就能看到admin的登陆界面了:
- 进入admin站点
利用刚才建立的admin账户,登陆站点,你将看到如下的界面:
当前已经有两个可编辑的内容:groups和users。它们是django.contrib.auth模块提供的身份认证框架。
- 在admin中注册你的投票应用
现在你还无法看到你的投票应用,必须先在admin中进行注册,告诉admin站点,请将poll的模型加入站点内,接受站点的管理。
打开polls/admin.py文件,加入下面的内容:
polls/admin.py
from django.contrib import admin from .models import Question admin.site.register(Question)
- 浏览admin站点的功能
注册question模型后,刷新admin页面就能看到Question栏目了。
点击“Questions”,进入questions的修改列表页面。这个页面会显示所有的数据库内的questions对象,你可以在这里对它们进行修改。看到下面的“What’s up?”了么?它就是我们先前创建的一个question,并且通过__str__方法的帮助,显示了较为直观的信息,而不是一个冷冰冰的对象类型名称。