简述Django建站步骤
官方文档地址:Django 文档 | Django 文档 | Django (djangoproject.com)
创建项目
-
使用如下命令创建一个Django项目:
django-admin startproject mysite
此命令会在当前目录下创建一个
mysite
目录,其内容如下:mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py其中,需要重点关注的是
urls.py
和settings.py
文件,他们分别用于管理站点的路由和配置。 -
使用如下名命令创建一个应用:
python manage.py startapp app_name
此命令会创建一个
app_name
目录,内容大致如下:app_name/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.pymodels.py
和views.py
分别存放应用的模型层和视图层代码。此外,还可以新建一个urls.py
文件,将应用的所有路由配置存放在这,方便管理。 -
使用如下命令可以启动内置的简易web服务器,用于调试:
python manage.py runserver
路由管理
一般情况下,Django会根据配置文件中的ROOT_URLCONF
项(默认为项目根目录的urls.py
文件)寻找名为 urlpatterns
变量并且按序匹配正则表达式。路由列表项一般由path
或re_path
函数生成。
关于路由是如何匹配的,在此不再赘述,官方文档讲解非常详细:URL调度器 | Django 文档 | Django (djangoproject.com)
反向解析
当项目的url地址发生变化,我们需要同步修改代码中使用到的url,这显然非常容易出错和遗漏。使用反向解析技术,在使用url时,不直接硬编码,而是使用url映射的地址,这样,即使url发生了变化,在代码中也可自动的映射到正确的地址。
在使用path
(或re_path
)指定urlpatterns时,在函数中指定name
变量的值,如:path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
,可以直接利用news-year-archive
这个名字映射到正确的url地址。
在模板和视图中,可以采用如下方式进行反向解析:
-
模板,使用
url
模板标签{# 参数是可选的 #}
{% url '别名' ['参数1' '参数2...'] %}
{# 如 #}
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
<a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a> -
视图,使用
reverse()
函数from django.http import HttpResponseRedirect
from django.urls import reverse
def redirect_to_year(request):
# ...
year = 2006
# ...
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
模板
默认情况下,在配置文件的TEMPLATES
项中,APP_DIRS
为True,即Django会搜索每个应用子目录下的templates
文件夹搜索模板源文件。为了方便,可以配置'DIRS': [BASE_DIR / 'templates'],
并在项目根目录下新建一个templates
目录,且为每个应用再创建对应的子目录,将应用的模板源文件放在对应的子目录内。Django按照一定的规则搜索指定的模板文件,使用这种方式可以避免同名模板文件匹配错误的问题。
可能的目录结构如下图所示:
Django 内置了自己的模板系统后端,即 Django 模板语言(DTL),也支持Jinja2及其他后端。下面以DTL为例,介绍模板的使用方式。
模板 | Django 文档 | Django (djangoproject.com)
语法
变量,{{ }}
变量从上下文中输出一个值,上下文是一个类似于字典的对象,将键映射到值。如:
My first name is {{ first_name }}. My last name is {{ last_name }}. |
对于字典查找,属性查找和列表索引查找均以点符号实现:
{{ my_dict.key }} |
标签,{% %}
内置模板标签和过滤器 | Django 文档 | Django (djangoproject.com)
用于将一些服务器端的功能嵌入到模板中,如流程控制等。大部分标签需要与结束标签成对使用,内置标签见上文的链接。
-
if
表达式{% if 条件表达式1 %}
...
{% elif 条件表达式2 %}
...
{% else %}
...
{% endif %}- 可以使用
and
、or
或not
来测试一些变量或取反某个变量 - 可以使用运算符
==
、!=
、<
、>
、<=
、>=
、in
、not in
、is
和is not
- 可以使用
-
for
表达式{% for 变量 in 可迭代对象 [reversed] %}
... |--反向循环一个列表
{% empty %}
... 可迭代对象无数据时填充的语句
{% endfor %}-
内置变量
forloop
变量名 描述 forloop.counter 循环计数器,表示当前循环的索引(从 1 开始)。 forloop.counter0 循环计数器,表示当前循环的索引(从 0 开始)。 forloop.revcounter 反向循环计数器(以最后一次循环为 1,反向计数)。 forloop.revcounter0 反向循环计数器(以最后一次循环为 0,反向计数)。 forloop.first 当前循环为首个循环时,该变量为 True forloop.last 当前循环为最后一个循环时,该变量为 True forloop.parentloop 在嵌套循环中,指向当前循环的上级循环
-
过滤器,|
过滤器转换变量和标签参数的值。
{{ 变量|过滤器1:'参数1'|过滤器2:'参数2' }} |
常用过滤器
过滤器 | 说明 |
---|---|
lower | 将一个字符串转换为全小写 |
upper | 将一个字符串转换为全大写 |
safe | 标记一个字符串在输出前不需要进一步的 HTML 转义 |
add:“n” | 将参数添加到值中 |
truncatechars:“n” | 如果一个字符串的长度超过指定的字符数,则截断它。截断后的字符串将以一个可翻译的省略号(“…”)结束。 |
模板继承
模板继承允许你建立一个基本的“骨架”模板,它包含了你网站的所有常用元素,并定义了子模板可以覆盖的块。对于多个网页有相同内容的网站十分有用。
-
建立
base.html
模板文件(名字随意),在其中用{% block block_name %}{% endblock %}
定义可以被子模板覆盖的块。 -
建立子模板
test.html
,其内容如下:{% extends "base.html" %}
{% block block_name %}My content{% endblock %}第一行使用
extends
标签继承基模板,后面根据需要,使用block
标签覆盖对应的块。对于没有指明需要覆盖的块,则会继承基模板中的内容。
请求和响应对象
请求和响应对象 | Django 文档 | Django (djangoproject.com)
请求对象 HttpRequest
请求字符串
HttpRequest.GET
(以下简称request.GET)是一个QueryDict
类型,继承自MultiValueDict
,是一个多值的字典,即一个键对应的值可以是一个列表。在http请求中,GET方法在请求时可以对一个参数携带多个数值,如?abc=321&abc=123
,此时在Django中输出request.GET
,其结果为<QueryDict: {'abc': ['321', '123']}>
。
只要url中含有请求字符串(?xx=xxx的形式),无论是POST还是GET方法,都可以通过request.GET
来获取请求字符串中的数据。
# 请求链接为:GET 127.0.0.1:8000?test=123&abc=123&abc=321 |
请求体
HttpRequest.body
获得原始的请求体的字节字符串,可用于处理非常规的HTML表单数据。对于处理传统的表单数据,应该使用HttpRequest.POST
。
HttpRequest.POST
获取表单数据,返回值类型为QueryDict。
响应 HttpResponse
每个视图都要负责实例化、填充和返回一个 HttpResponse
对象。
- content,响应的正文内容
- status,状态码
- content-type,响应内容的MIME类型
如果想返回图片,可以参考使用如下的方式:
content = open("a.png","rb").read() |
HttpResponseRedirect 重定向
接收一个url参数表明要重定向的地址。该参数除了可以使用完整的 url路径(https://www.baidu.com),还可以使用不含域名的绝对(/admin/)/相对(admin/)路径。
另外,Django提供了针对重定向的快捷函数redirect
。
Django 便捷函数 | Django 文档 | Django (djangoproject.com)
# 1. 跳转到完全指定的URL |
JsonResponse返回json格式的数据
class JsonResponse
(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs)
帮助创建一个 JSON 编码的响应,并将Content-Type
头设置为 application/json。
- 如果
safe
为True,则data
必须是一个字典,否则会引发TypeError。如果为False,则data
可以为任何JSON 可序列化的对象。 encoder
为序列化器,可以指定其他的序列化器。json_dumps_params
参数是一个关键字参数的字典,用来传递给json.dumps()
调用,用于生成响应。
from django.http import JsonResponse |
模型层
模型 | Django 文档 | Django (djangoproject.com)
定义模型
模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。
自定义的模型需要继承自 django.db.models.Model
,其每个属性都与数据库中的字段对应。如果没有使用primary_key=True
显式地指明主键,Django会自动给模型创建一个自增的主键。一个模型类可以按照如下方式定义:
from django.db import models |
模型中的字段是某个 Field
类的实例,它用于指定数据库数据类型,并且在渲染表单时会使用对应的HTML视图,另外还有基本的有效性验证功能。
字段类型
完整文档:模型字段参考 | Django 文档 | Django (djangoproject.com)
常用的主要有以下几个字段:
- BooleanField
- true/false 字段
- 默认表单部件是
CheckboxInput
- CharField
- 字符串字段
- 默认表单部件是一个
TextInput
- 需要指定max_length
- 大的文本需要使用TextField
- DateField,DateTimeField
- 日期、日期时间字段
- 默认表单部件分别是
DateInput
,DateTimeInput
- 有三个参数可选,但不可同时使用
- auto_now,每次保存对象时,自动将该字段设置为现在。适用于需要记录修改时间的情况
- auto_now_add,当第一次创建对象时,自动将该字段设置为现在。适用于记录创建时间的情况。指定该参数后,即使在创建对象时为该字段赋值也会被忽略。
- default,默认值,当创建对象时会对该字段赋值,则会使用默认值。如果想要将当前时间作为默认值使用,对于这两种字段可以分别使用**
date.today
(来自datetime)和timezone.now
**(来自django.utils)
- DecimalFiel
- 一个固定精度的十进制数,在 Python 中用一个
Decimal
实例来表示。 - 有两个必要参数
- max_digits,指定允许的最大位数
- decimal_places,指定小数位数
- 一个固定精度的十进制数,在 Python 中用一个
- EmailField
- 实际为一个
CharField
,使用EmailValidator
来检查该值是否为有效的电子邮件地址。
- 实际为一个
- IntegerField
- 一个整数。
- 从
-2147483648
到2147483647
的值在 Django 支持的所有数据库中都是安全的。
此外,FileField
、ImageField
、TimeField
等也很常用。
字段选项
模型字段参考 | Django 文档 | Django (djangoproject.com)
可以为字段指定一些额外的参数,部分字段有其自己独有的字段选项,下面介绍的是通用的字段选项:
- null
- 默认为
False
- 如果设置为
True
,当该字段为空时,Django 会将数据库中该字段设置为NULL
- 默认为
- blank
- 默认为
False
- 如果设置为
True
,该字段允许为空。即在进行表单验证时,允许该字段为空
- 默认为
- choices
- 一系列二元组,用作此字段的选项。如果提供了二元组,默认表单小部件是一个选择框,而不是标准文本字段,并将限制给出的选项。
- 比较复杂,参考文档:choices
- db_column
- 这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名
- db_index
- 如果是
True
,将为该字段创建数据库索引
- 如果是
- default
- 设置默认值
- 新增字段时,必须设置该参数
- primary_key
- 如果设置为
True
,将该字段设置为该模型的主键
- 如果设置为
- unique
- 如果设置为
True
,这个字段必须在整个表中保持值唯一
- 如果设置为
Meta选项
可以在模型内部创建Meta
类,来给模型赋予一些属性。
模型 Meta 选项 | Django 文档 | Django (djangoproject.com)
- db_table
- 指定模型数据库的表的名称
使用模型
-
修改设置文件中的
INSTALLED_APPS
,在这个设置中添加包含models.py
文件的模块名称。 -
使用以下命令进行数据库迁移
python manage.py makemigrations
python manage.py migrate
操作数据库
执行查询 | Django 文档 | Django (djangoproject.com)
模型类含有一个objects
管理器对象,数据库的增删改查都可以通过管理器实现。
创建对象
有两种方式可以创建对象:
# 1. 使用create()方法 |
查询
查询操作需要依赖模型的管理器对象(默认为objects),大部分情况返回的是一个QuerySet
,内部存放模型实例。
方法 | 说明 |
---|---|
all() | 查询全部记录 |
get() | 查询单个对象,直接返回对象而不是QuerySet 。如果结果多于一条或没有则分别引发Model.MultipleObjectsReturned 和 Model.DoesNotExist 异常 |
filter(属性1=值1,属性2=值2) | 查询满足给定查询参数的对象,逗号表示与 |
exclude() | 查询不满足给定查询参数的对象 |
values() | 可指定要查询的列名,返回值是QuerySet 但内部存放的是字典,如{‘列1’:值1, ‘列2’:值2} |
values_list() | 与values()类似,但返回的QuerySet 内是各列的值组成的元组 |
order_by(‘-列1’,‘列2’) | 默认升序排列,降序在列名前加’-’ |
自定义查找
当内置的查找语句无法满足要求时(如大于、包含关系等),可以自定义查找方法。Field
查找
使用方法为字段__查询谓词
-
__exact
,等值匹配# SELECT ... WHERE id = 14;
Entry.objects.get(id__exact=14)
# SELECT ... WHERE id IS NULL;
Entry.objects.get(id__exact=None) -
__contains
,__icontains
, 包含指定值,i
前缀表示不区分大小写# SELECT ... WHERE headline LIKE '%Lennon%';
Entry.objects.get(headline__contains='Lennon') -
__(i)startswith
,__(i)endswith
,以…开头/结尾,i
前缀同上# SELECT ... WHERE headline LIKE 'Lennon%';
Entry.objects.filter(headline__startswith='Lennon')
# SELECT ... WHERE headline LIKE '%Lennon';
Entry.objects.filter(headline__endswith='Lennon') -
__gt(e)
,__lt(e)
,大于(等于)/小于(等于)# SELECT ... WHERE id > 4;
Entry.objects.filter(id__gt=4)
此外,还有in
,range
,日期查询等,详见文档。
修改
单个数据的修改
- 通过get()等方法查询得到需要修改的实例对象
- 修改得到的对象的属性
- 调用save()方法保存修改,写入数据库
批量修改
直接调用QuerySet
的update(属性=值)方法
# Update all the headlines with pub_date in 2007. |
删除
与修改相似,也有单个和批量两种方式,分别调用对象和QuerySet
的delete()方法即可。
可以使用伪删除的方式,即在模型中加一个字段标记该对象是否删除。
在执行查询操作时需要对该字段进行过滤。
F对象
一个F
对象代表数据库中某条记录的字段的信息,可以在不获取字段值的情况下对字段进行操作。
# 将所有记录的stories_filed加1 |
F()
是直接委托数据库进行字段的操作,python本身不会获取字段值,因此如果要使用新值,需要调用对象的refresh_from_db()
方法。
另外,F()
分配给模型字段的对象在保存模型实例后会持续存在,并将应用于每个 save()
。如:
reporter = Reporters.objects.get(name='Tintin') |
在这种情况下,stories_filed
将被更新两次。如果最初是 1
,最终值将是 3
。这种持久性可以通过在保存模型对象后重新加载来避免,例如,使用 refresh_from_db()
。
Q对象
使用Q对象可以执行更复杂的查询(如OR,NOT等)。
Q
对象能通过 &
和 |
操作符连接起来。当操作符被用于两个 Q
对象之间时会生成一个新的 Q
对象。
# WHERE question LIKE 'Who%' OR question LIKE 'What%' |
此外, Q
对象也可通过 ~
操作符反转,允许在组合查询中组合普通查询或反向 (NOT
) 查询。
查询函数能混合使用 Q
对象和关键字参数。所有提供给查询函数的参数(即关键字参数或 Q
对象)均通过 “AND” 连接。然而,若提供了 Q
对象,那么它必须位于所有关键字参数之前。
# SELECT * from polls WHERE question LIKE 'Who%' |
聚合查询
聚合 | Django 文档 | Django (djangoproject.com)
对部分或全部数据进行查询和操作。
整表聚合
支持Sum
,Avg
,Count
,Max
,Min
语法:MyModel.objects.aggregate(结果变量名=聚合函数('列'))
,返回字典{'结果变量名':值}
分组聚合
指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(或平均值等),即为查询集的每一项生成聚合。
语法:QuerySet.annotate(结果变量名=聚合函数('列'))
,返回值为QuerySet
原生SQL操作
执行原生 SQL 查询 | Django 文档 | Django (djangoproject.com)
有两种方式:
- 使用管理器的
raw(raw_query, params=(), translations=None)
方法,仅支持查询操作,返回RawQuerySet
集合对象。 - 使用django.db.connection对象,该对象代表默认的数据库连接
- 调用
connection.cursor()
来获取一个指针对象,使用完成后需要释放,因此可使用with语句 - 调用
cursor.execute(sql, [params])
来执行该 SQL - 调用
cursor.fetchone()
,或cursor.fetchall()
获取结果数据
- 调用
不推荐使用原生SQL操作,使用不当会遭到SQL注入攻击。
模型关系
在定义模型关系的时候,也需要指定删除该模型时,对关联模型的操作,这一行为通过字段的on_delete
参数进行设定,主要有以下几种方式:
-
CASCADE,级联删除。Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,也删除了包含 ForeignKey 的对象。
-
PROTECT,通过引发
ProtectedError
,防止删除被引用对象。 -
RESTRICT,通过引发
RestrictedError
来防止删除被引用的对象。与PROTECT
不同的是,如果被引用的对象也引用了一个在同一操作中被删除的不同对象,但通过CASCADE
关系,则允许删除被引用的对象。 -
SET_NULL,设置
ForeignKey
为空;只有当null
为True
时,才有可能。 -
SET_DEFAULT,将
ForeignKey
设置为默认值,必须为ForeignKey
设置一个默认值。 -
SET(),将
ForeignKey
设置为传递给SET()
的值,如果传递了一个可调用的值,则为调用它的结果。from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]
class MyModel(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET(get_sentinel_user),
) -
DO_NOTHING,不采取任何行动。
以下例子来源于B站视频:2021最新版Django全套视频(django框架快速上手)_哔哩哔哩_bilibili。例子举得不错,讲解也很详细。
一对一关系
在模型中使用OneToOneField
字段实现。
创建模型
以男性作家与其妻子是一对一的关系为例:
class Author(models.Model): |
其模型关系示意图如下所示:

创建数据
# 无外键的模型,按正常方式创建 |
此时数据库中的内容如下图所示:
查询数据
-
正向查询,即直接通过外键属性查询,本例中是通过
Wife
类查找对应的Author
w = Wife.objects.get(name='wife')
# 获得对应的Author
au = w.author -
反向查询,即没有外键的一方,调用反向属性查询关联的另一方,即
Author
查找Wife
反向关联属性为
实例对象.引用类名的小写
,如作家对象.wife
au = Author.objects.get(name='test')
# 获得对应的Wife
wi = au.wife
一对多关系
在多的表上使用ForeignKey设置外键。
创建模型
以出版社和书籍是一对多的关系为例:
class Publisher(models.Model): |
其模型关系示意图如下所示:

创建数据
先创建“一”,再创建“多”。
p1 = Publisher.objects.create(name="Pub1") |
数据库中内容如下:
查询数据
-
正向查询,通过
Book
查Publisher
pub = Book.objects.get(name="b1").publisher
-
反向查询,通过
Publisher
查Book
,利用_set集合pub = Publisher.objects.get(name="Pub1")
# 通过book_set获取对应的多个Book对象,返回QuerySet
books = pub.book_set.all()
# 也可通过Book的filter方法获取
books = Book.objects.filter(publisher=pub)
多对多关系
在任意一个模型中使用ManyToManyField
字段。
创建模型
以作者和书籍为例,一本书可以被多个作者编写,一个作者也可以编写多本书。
如果手动创建这种关系,需要第三张表来记录两个模型之间的关系,而Django会自动帮助我们创建这第三张表,因此仍然只要定义两个模型即可。
class Author(models.Model): |
其模型关系示意图如下所示:

创建数据
两者创建没有先后顺序之分。
# 法1. 先创建Author再关联Book |
Book和Author数据库表中的数据如下所示:
两者的关系表中的数据如下所示,记录了两者的所有关系:
查询数据
-
正向查询,通过
Book
查Author
# 获取所有数据
b1.authors.all()
# 过滤数据
b1.authors.filter('...') -
反向查询,通过
Author
查Book
a1.book_set.all()
a1.book_set.filter('...')