Django 简介
Django发布于2005年,是一个由 Python 编写的一个开放源代码的 Web 应用框架(源代码是开源的,遵守BSD版权)。使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务Django本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,也有很多人把它称MVT(MTV)模式。MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。
MTV 模型
Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:
- M 表示模型(Model):编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
- T 表示模板 (Template):负责如何把页面(html)展示给用户。
- V 表示视图(View):负责业务逻辑,并在适当时候调用 Model和 Template。
除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式
用户操作流程图:
解析:
用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:
- 绿色路线:如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。
- 红色路线:如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
Django版本
django各个版本对python的要求:
Django version | Python versions |
---|---|
1.11 | 2.7, 3.4, 3.5, 3.6 |
2.0 | 3.4, 3.5, 3.6, 3.7 |
2.1, 2.2 | 3.5, 3.6, 3.7 |
Django的安装
环境要求
- 操作系统: Windows
- python版本:>=3.5
- Django版本:2.2
pip命令安装
⾸先使用virtualenvwrapper-win建⽴⼀个虚拟开发环境,然后使用pip安装
1 | mkvirtualenv django-env # 新建虚拟环境 |
安装完毕后,测试⼀下是否安装成功
1 | #在虚拟开发环境中 |
⼿动安装
到django官⽹上下载:https://www.djangoproject.com/download/2.2.9/tarball/安装包,然后解压,到解压⽬录下,打开虚拟开发环境,执行以下命令:
1 | python -m pip install . |
Django 创建第一个项目
测试版本说明:
- Python 3.8.4
- Django 2.2
Django 管理工具
安装 Django 之后,您现在应该已经有了可用的管理工具 django-admin.py,Windows 如果没有配置环境变量可以用 django-admin。我们可以使用 django-admin.py 来创建一个项目:
1 | C:\Users\L15096000421>django-admin |
创建项目
使用 django-admin.py 来创建 HelloWorld 项目。先切换到指定⽬录,开启虚拟环境,然后用以下指令创建⼀个项⽬
1 | django-admin startproject HelloWorld |
创建完成后我们可以查看下项目的目录结构:
1 | $ cd HelloWorld/ |
目录说明:
- HelloWorld: 项目的容器。
- manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
- HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
- HelloWorld/settings.py: 该 Django 项目的设置/配置。
- HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。
- HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
接下来我们进入 HelloWorld 目录输入以下命令,启动服务器:
1 | python manage.py runserver 0.0.0.0:8000 |
0.0.0.0 让其它电脑可连接到开发服务器,8000 为端口号。如果不说明,那么端口号默认为 8000。
在浏览器输入你服务器的 ip(这里我们输入本机 IP 地址: 127.0.0.1:8000) 及端口号,如果正常启动,输出结果如下:
如果要让远程客户端连接需要修改配置⽂件,其中0.0.0.0:9000是可选的,0.0.0.0说明任何ip都可以访问。
1 | #修改setting.py中的这⼀行 |
如果需要让页面显示中文,需要在setting.py
中修改国际化的配置
1 | # 语言 国际化 |
建立应用
⼀个django项⽬中可以包含多个应用,和flask只是叫法上不同,在flask中说法为可以在一个app上建议多个模块
可以使用以下命令建⽴应用:
1 | #命令: python manager.py startapp 应用名称 |
创建应用后,项⽬的结构如下:
1 | ── App |
修改项⽬的配置⽂件setting.py
:
1 | INSTALLED_APPS = [ |
简单视图函数
在django中view代表视图函数,接收用户的请求,进行处理然后返回html源代码给客户端。在框架中,我们可以在应用(app)的views.py中写⾃⼰的视图函数, 进行相应的处理
视图函数只是⼀个普通的python函数,它的第⼀个参数必须是⼀个请求(request)对象,这是框架传入的,我们可以通过请求对象获取提交参数等内容。它的返回值必须是⼀个web响应对象,可以文本、html源代码、重定向、404等内容。下⾯我们写⼀个简单视图函数,返回“Hello Django”
1 | from django.http import HttpResponse |
其中HttpResponse函数需要引⼊模块django.http,我们可以直接写HttpResponse,然后通过快捷键alt+enter
,在下拉框中选择要导⼊的模块。
alt+enter 代码飘红、飘⻩、查看定义、引⼊包都可以使用。
基本路由
添加完视图函数后,还⽆法在浏览器中查看,必须添加路由。所谓路由就是将用户请求转换为访问对应的视图函数。项⽬路由在urls.py中
1 | from django.conf.urls import path |
匹配字符串匹配用户在地址栏中键⼊的url,当用户在地址栏中键⼊http://localhost:9000时会显示Hello World
简单模板
在上⾯我们已经能够展示简单的web⻚⾯,但要显示⼀个html⻚⾯,需要使用模板,在app下建⽴templates
⽬录,在templates
⽬录下建⽴⼀个index.html
⽂件
1 | <!DOCTYPE html> |
然后修改views
中视图函数index
1 | from django.shortcuts import render |
也可以在html中使用变量和流程控制结构,生成复杂的html
1 | <!DOCTYPE html> |
然后修改视图函数
1 | from django.shortcuts import render |
简单模型
django⾃带了⼀个sqlite数据库,可以用于测试,生产环境⼀般不用。在配置⽂件中已经设置好默认的sqlite数据库。
1 | DATABASES = { |
在pycharm左边栏中选—-database,然后点选”+”,弹出sqlite的配置窗⼝
将File改为项⽬中sqlite⽂件:db.sqlite3,然后点击测试,看看能否连接成功。第⼀次配置时,需要下载sqlite的驱动。完成后点击ok。
然后到app
中models.py
中创建⼀个User类:
1 | class User(models.Model): |
到命令行下,输⼊:
1 | python manage.py makemigrations #生成数据库迁移⽂件 |
然后到pycharm左边栏database中查看:
双击app_user添加数据:
到此为⽌,我们已经给app_user表添加了数据。然后给app添加⼀个模板:list.html
1 | <!DOCTYPE html> |
继续给app添加⼀个视图操作函数:
1 | from app.models import User |
在项⽬的路由表中添加路由:
1 | urlpatterns = [ |
到浏览器中访问:http://localhost:9000/list/
Django 模型(M)
模型使用步骤:
- 配置数据库
models.py
定义模型类- 激活模型
- 使用模型
Django默认使用的是sqlite,但在生产环境中⼀般会用mysql、postgrsql、oracle等关系型数据库。
数据库配置
在虚拟开发环境(django-env)中,安装mysql的数据库驱动mysqlclient
1 | pip install mysqlclient |
在项⽬的settings.py
⽂件中找到DATABASES
配置项,将其信息修改为:
1 | DATABASES = { |
ORM
对象关系映射(Oject Relational Mapping,简称ORM)模式是⼀种为了解决⾯向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,⾃动生成sql语句,将程序中的对象⾃动保存到关系数据库中。优点:
- 隐藏了数据库访问的细节,简化了sql的使用,提⾼了开发效率
- 解耦业务逻辑层(view)和数据处理层(model),简化了开发流程,提⾼了系统的可移植性
- 提⾼了安全性
缺点:
- 执行效率低
- 对复杂的sql无能为力
- 增加了学习成本
基本概念
面向对象概念 | ⾯向关系概念 |
---|---|
类 | 表 |
对象 | 记录(一行) |
属性 | 字段(属性,列) |
- ⼀个模型类对应⼀个表
- 每个模型类都必须继承django.db.models.Model
模型属性
模型中的属性和数据库表的字段对应,必须定义。模型的属性需要定义成类属性
属性定义语法为:
1 | 属性 = models.字段类型(选项) |
实例:
1 | from django.db import models |
属性命名规则:
不能是python的保留关键字
不允许使用连续的下划线,因为连续下划线在查询中会用到
定义属性时需要指定字段类型
主键⼀般不用⾃⼰定义,django会⾃动创建⾃增⻓主键列,如果你⾃⼰定义了主键,则django不会再⾃动生成主键
字段类型
字段名称 | 字段说明 | 参数 |
---|---|---|
AutoField | ⼀个根据实际Id⾃动增⻓的IntegerField(通常不指定 ⾃动⽣成) | |
CharField | 字符串,默认的表单样式是TextInput | max_length=字符⻓度 |
TextField | ⼤⽂本字段,⼀般超过4000使⽤,默认的表单控件是Textarea | |
IntegerField | 整数 | |
DecimalField | 使⽤python的Decimal实例表示的⼗进制浮点数 | max_digits总位数;decimal_places小数位数 |
FloatField | ⽤Python的float实例来表示的 浮点数 | |
BooleanField | true/false 字段,此字段的默认表单控制是CheckboxInput | |
NullBooleanField | 支持null,true,false三种值 | |
DateField | 使用Python的datatime.date实例表示的日期,该字段默认对应的表单控件是⼀个TextInput | auto_now和 auto_now_add、default这三个参数不能同时存在 |
TimeField | 使⽤Python的datetime.time实例表示的时间 | 参数同DateField |
DateTimeField | 使⽤Python的datetime.datetime实例表示的日期和时间 | 参数同DateField |
ImageField | 继承了FileField的所有属性和⽅法,但对上传的对象进⾏校验,确保它是个有效的image | 不能同时共存 |
- auto_now: 每次保存对象时,⾃动设置该字段为当前时间,用于”最后⼀次修改”的时间戳,它总是使用当前⽇期,默认为false
- auto_now_add: 当对象第⼀次被创建时⾃动设置当前时间,用于创建的时间戳,它总是使用当前⽇期,默认为false
字段选项
适用于任何字段,可以实现字段的约束,在生成字段时通过方法的关键字参数指定
可选参数 | 说明 |
---|---|
null | 如果 True ,Django将NULL在数据库中存储空值。默认 是 False 。不要在字符串字段上使⽤。null是数据库范畴的概念 |
blank | 如果 True ,该字段允许为空。默认是 False 。同null不同, 如果字段有 blank=True ,则表单验证将允许输⼊空值。如果字段有 blank=False,则需要该字段 |
db_column | ⽤于此字段的数据库列的名称。如果没有给出,Django将使⽤该字段的名称 |
db_index | 如果为True ,将为此字段创建数据库常规索引。 |
unique | 如果为True,该字段在整个表格中必须是唯一的 |
primary_key | 如果为True,此字段是模型的主键 |
default | 默认值,当前字段如果不给值则执行默认值 |
定义模型
我们可以在应用的models.py
中定义模型:
1 | from django.db import models |
数据库的表名等于:应用名_模型名,如果想指定表名,可以在Meta中使用db_table指定
如果没有指定主键,Django将⾃动给表创建⼀个⾃增⻓主键id
1
id = models.AutoField(primary_key=True)
Meta中常用设置:
名称 说明 db_table 数据库中的表名 abstract 当设置为True时,说明当前模型是抽象基类 managed 如果设置False,则迁移不会创建或删除表,默认是True ordering ⽤于记录排序,ordering = [‘pub_date’]或降序ordering = [‘- pub_date’] 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from django.db import models
from django.utils import timezone
#用户类
class User(models.Model):
uid = models.AutoField(primary_key=True) #⾃增主键
uname = models.CharField(max_length=60)
password = models.CharField(max_length=32)
user_type = ((1,'超级管理员'),(2,'普通用户'))
type = models.IntegerField(default=2,choices=user_type)
regtime = models.DateTimeField(default=timezone.now) #缺省值是当前时间
ip = models.IntegerField(null=True)
login_type = ((1,'允许登录'),(2,'禁⽌登录')) #用户⾃定义类型对应mysql的enum类型
allowed = models.IntegerField(default=1,choices=login_type)
email = models.CharField(max_length=100,null=True)
memo = models.CharField(max_length=1000,null=True)
class Meta:
db_table = 'user' #表名
激活模型
创建迁移⽂件 (此刻表并没有创建到库中)
1
python manage.py makemigrations
执行迁移 (将模型创建到库中)
1
python manage.py migrate
然后在应用的migrations⽬录中应该生成了迁移⽂件
1 | ├── app |
生成的表结构如下
1 | CREATE TABLE `user` ( |
注意:任何对字段或表的修改都需要重新迁移
反向迁移:可以根据数据库中表⾃动创建模型
1
python manage.py inspectdb > App/models.py
模型的使用
增
1 | from app.models.User import User #导⼊User模型 |
删
1 | u1 = User.objects.get(pk=1) |
数据的逻辑删除
对于重要数据,⼀般不会直接删除,会在表中增加⼀个字段⽐如:
is_deleted
,如果删除的话,将这个字段置为True,以后查询的时候不在查询,这种操作称为逻辑删除
改
1 | u2 = User.objects.get(pk=2) |
数据查询
要从数据库检索数据,⾸先要获取⼀个查询集(QuerySet),查询集表示从数据库获取的对象集合,它可以有零个,⼀个或多个过滤器。返回查询集的方法,称为过滤器,过滤器根据给定的参数缩⼩查询结果范围,相当于sql语句中where或limit。
- 在管理器上调用过滤器方法会返回查询集
- 查询集经过过滤器筛选后返回新的查询集,因此可以写成链式过滤
- 惰性执行:创建查询集不会带来任何数据库的访问,直到调用数据时,才会访 问数据库
- 以下对查询集求值:迭代、切⽚、序列化、与if合用、repr()/print()/len()/list()/bool()
管理器的方法 | 返回类型 | 说明 |
---|---|---|
模型类.objects.all() | QuerySet | 返回表中所有数据 |
模型类.objects.filter() | QuerySet | 返回符合条件的数据 |
模型类.objects.exclude() | QuerySet | 返回不符合条件的数据 |
模型类.objects.order_by() | QuerySet | 对查询结果集进⾏排序 |
模型类.objects.values() | QuerySet | 返回⼀个Queryset,其中每个对象为⼀个 字典 |
模型类.objects.values_list() | QuerySet | 和values()基本相同,但每个对象是⼀个元 组 |
模型类.objects.reverse() | QuerySet | 对排序的结果反转 |
模型类.objects.only(字 段) | QuerySet | 只显示指定字段 |
模型类.objects.defer(字 段) | QuerySet | 去除指定字段 |
模型类.objects.get() | 模型对象 | 返回⼀个满⾜条件的对象;如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常;如果找到多个,会引发模型类.MultiObjectsReturned 异常 |
模型类.objects.first() | 模型对象 | 返回第⼀条数据 |
模型类.objects.last() | 模型对象 | 返回最后⼀条数据 |
模型类.objects.earliest() | 模型对象 | 根据指定字段返回最早增加的记录 |
模型类.objects.latest(field) | 模型对象 | 根据field字段返回最近增加记录 |
模型类.objects.exists() | bool | 判断查询的数据是否存在 |
模型类.objects.count() | int | 返回查询集中对象的数⽬ |
返回查询集
all()
1
2# 获取所有数据,对应SQL:select * from User
User.objects.all()filter(**kwargs) 返回QuerySet包含与给定查找参数匹配的新查询集
1
2
3
4
5
6
7
8
9
10
11#等价sql:select * from User
User.objects.filter()
#等价sql:select * from User where uname = 'admin'
User.objects.filter(uname='admin')
#等级sql:select * from User where uid > 1 and type = 2
User.objects.filter(uid__gt=1,type=2)
#链式调用,等价于User.objects.filter(uid__gt=1,type=2)
User.objects.filter(uid__gt=1).filter(type=2)exclude(**kwargs)
1
2# 不匹配,对应SQL:select * from User where name != 'admin'
User.objects.exclude(name='admin')order_by(*fields)
- 参数是字段名,可以有多个字段名,默认是升序
- 如果要按某个字段降序,在字段名前加’-‘; “-uid”表示按uid降序排列
1
2
3
4
5
6#按uid升序排列 等价于 order by uid
User.objects().order_by('uid')
#按uid降序排列 等价于 order by uid desc
User.objects.order_by('-uid')
#多列排序 等价于 order by password,uid desc
User.objects.order_by('password','-uid')values()
1
2
3
4
5
6
7
8
9
10
11#返回所有字段
User.objects.values()
#返回数据:
[{'uid': 2, 'uname': '⼩甜甜', 'password':
'59f2443a4317918ce29ad28a14e1bdb7'type': '普通用户', 'regtime':
None, 'ip': None, 'allowed': '允许登录', 'email': None, None},...]
#返回指定字段
User.objects.values('uname','password')
[{'uname': '⼩甜甜', 'password':'59f2443a4317918ce29ad28a14e1bdb7'},...]reverse()
1
2User.objects.order_by('id').reverse() # 降序
User.objects.order_by('-id').reverse() # 升序distinct() 去重
1
User.objects.values('password').distinct()
返回单个值
下⾯这些方法后⾯不能再跟其他过滤方法,因为他们不返回查询集
get() 只匹配一条数据
1
2
3u = User.objects.get(pk=1) # 正常
u = User.objects.get(uid__gt=20)#MultipleObjectsReturned 匹配到了多条数据
u = USer.objects.get(uid__lt=1) #DoesNotExist 匹配失败first()和last()
1
2User.objects.all().first() #返回结果集中第⼀条数据
User.objects.all().last() #返回结果集中最后⼀条数据count()
- 返回结果集记录数⽬,等价于select count(*)
- 不会返回整个结果集,相⽐len方法更有效
1
User.objects.count()
exists()
判断查询集中是否有记录,有返回True,否则返回False
1
User.objects.filter(uid=3).exists()
查询集限制
查询集类似列表,可以使用下标进行限制,类似sql语句中的limit子句。但索引不能是负数
- 索引
- 切片
1 | User.objects.all()[0] #等同于:limit 0,1 |
字段查询
相当于sql语句中where子句,它可以为filter、exclude和get方法提供参数。
1 | 属性名称__比较运算符=值 #是两个下划线 |
操作符 | 说明 | 事例 |
---|---|---|
exact | 精确判等 | uname = ‘admin’uname__exact = 'admin' uname__exact = None #uname is null |
iexact | 不区分大小写判等 | name__iexact = ‘admin’ |
contains | 模糊查询,等价like ‘% 值%’ | uname__contains = ‘admin’ |
startswith | 以..开头 | uname__startswith = ‘a’ |
istartswith | (不区分大小写)以… 开头 | uname__istartswith = ‘a’ |
endswith | 以…结尾 | uname__endswith = ‘m’ |
iendswith | (不区分大小写)以…结尾 | uname__iendswith = ‘m’ |
isnull | 判空(等价 = None) | uname__isnull = True #等价于 uname is null |
in | 包含 | uid__in = [1,2,4] # in后面必须是可迭代对象 |
range | 范围测试(相当 between and) | uid__range = [2,5] #uid>=2 and uid <=5 |
gt/gte | 大于/大于等于 | uid__gt = 2 |
lt/lte | 小于/小于等于 | uid__lte = 2 |
regex | 正值匹配 | uname__regex= r’^a’ |
iregex | 不区分大小写正则匹配 | uname__iregex= r’^a’ |
in后面可以跟⼀个子查询,但要求子查询只能返回⼀个字段
1
2
3
4User.objects.filter(uid__in = (2,3,4))
User.objects.filter(uid__in = [2,3,4])
res = User.objects.filter(uid__gt = 1).values('uid') #查询集中只有⼀个字段uid
User.objects.filter(uid__in = res)日期查询
year、month、day、week_day、hour、minute、second
1
2等价sql: select * from User where year(regtime) = 2018
User.objects.filter(regtime__year = 2018)
统计查询
需要先导⼊模块:
1 | from django.db.models import Max,Min,Sum,Avg,Count |
聚合查询:对多行查询结果的⼀列进行操作
1
2
3
4#统计记录总数: select count(*) from user
User.objects.aggregate(Count('uid')) #{'uid__count': 4}
User.objects.aggregate(Max('uid')) #{'uid__max': 5}
User.objects.aggregate(Min('uid')) # {'uid__min':2}分组统计
1
2
3
4
5
6
7
8#等价sql: select type,count(*) from user group by type
res = User.objects.values('type').annotate(Count('uid'))
#统计结果: [{'type': '普通用户', 'uid__count': 3}, {'type': '超级管理员', 'uid__count': 1}]
#查看生成的sql语句
print(res.query)
#SELECT `user`.`type`, COUNT(`user`.`uid`) AS `uid__count` FROM `user` GROUP BY `user`.`type` ORDER BY NULL
Q对象和F对象
需要先导⼊模块:
1 | from django.db.models import Q,F |
Q对象可以对关键字参数进行封装,从而更好的应用多个查询,可以组合&(and)、|(or)、~(not)操作符。
1
2
3
4#原生sql:select * from user where uid = 2 or uid = 3
User.objects.filter(Q(uid=2)|Q(uid=3))
User.objects.filter(Q(uid__gte=2) & Q(uid__lte=3))
User.objects.filter(~Q(uid__gte=2))F对象:用于比较表中两个字段
1
2#等价sql:select * from user where uid < type
User.objects.filter(uid__lte = F('type'))原始sql
您可以使用它 Manager.raw()
来执行原始查询并返回模型实例,或者您可以完全避免模型层并直接执行⾃定义SQL。
raw
1
2
3
4
5
6
7
8
9users = User.objects.raw("select * from user")
# 会造成SQL
users = User.objects.raw("select * from user where username='{}'".format('单强'))
print("select * from user where username='{}'".format('单强'))
users = User.objects.raw("select * from user where username='{}'".format('单强'))
# 原生SQL使用下面的写法,可以防止SQL注入
users = User.objects.raw("select * from user where username=%s",["sddfsdf' or '1"])
print(list(users))
print(type(users))自定义sql,聚合使用下面的
1
2
3
4
5
6
7
8
9
10
11
12from django.db import connection
# with语句相当与cursor= connection.cursor() 和 cursor.close(),简化了语
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s",[para])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [para2])
row = cursor.fetchone()
# 返回列表套字典
with connection.cursor() as cursor:
cursor.execute("select * from publisher")
columns = [col[0] for col in cursor.description]
res = [dict(zip(columns, row)) for row in cursor.fetchall()]
print(res)
模型成员
模型类和数据库中表对应,模型类的对象和记录对象,模型类本身没有数据库访问功能,但模型类中有⼀个Manager类的对象,通过管理器对象可以实现和数据库的访问。
当我们没有为模型类定义管理器时,Django会为模型类生成⼀个名为objects的管理器,⾃定义管理器后,Django不再生成默认管理器objects
管理器是Django的模型进行数据库操作的接⼝,Django应用的每个模型都拥有⾄少⼀个管理器。Django⽀持⾃定义管理器类,继承⾃models.Manager
⾃定义管理器类主要用于两种情况:
- 修改原始查询集
- 向管理器类中添加额外的方法,如向数据库中插⼊数据。
重命名管理器
在模型类中⾃定义⼀个新的管理器,则原有的objects管理器不会存在,以后对数据库的操作使用⾃⼰定义的管理器
模型类
1 |
|
视图views
1 | def article_get(request): |
⾃定义管理器
修改原始查询集(由
all()
获取的查询集)- 修改管理器的
get_queryset
方法,可以改变all方法返回的原始查询集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#⾸先⾃定义Manager的子类
class ArticleManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(ispublished=True) #获取已发表的⽂章
#模型类
class Artical(models.Model):
....
objects = models.Manager() #可以有多个管理器
publishManager = ArticleManager()
# 视图views
def article_publish(request):
published = Artical.publishManager.all()
return HttpResponse("已经发表{}".format(published[0].title))- 修改管理器的
给管理器类中添加额外的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class ArticleManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(ispublished=True)
#新增⼀个创建对象的方法
def create(self,title,content,publishingdate,comments,likenum,ispublished):
article = Artical()
article.title = title
article.content = content
article.publishingdate = publishingdate
article.comments = comments
article.likenum = likenum
article.ispublished = ispublished
article.save()
return article
#views.py中
def article_add(request):
# art = Artical(title='⼩时代',content="混乱的逻辑")
# art.save()
Artical.publishManager.create('中美贸易战','川朴⼀⾖⽐','2018-10-8',0,0,1)
return HttpResponse("")
模型对应关系
关系数据库最强⼤的地方在于“关系”,也即表和表之间是有关联的,这种关联有三种类型
- ⼀对⼀
- ⼀对多
- 多对多
⼀对⼀
⼀个学生有⼀个档案,⼀个档案属于⼀个学生,那么学生表和档案表就是⼀对⼀关系。学生表是主表,档案表是从表,从表中有⼀个外键和学生表关联,并且要求外键取值唯⼀。对应关键字为:OneToOneField
模型类Student
1 |
|
模型类Archives
1 | class Archives(models.Model): |
增加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def addstudent(request):
student = Student()
student.sno = '180502'
student.sname = '杨康'
student.sage = 22
student.save()
return HttpResponse("增加了⼀个学生")
def addarchives(request):
stu = Student.objects.get(pk='180503')
arc = Archives()
arc.idcard = '130098384893838953'
arc.student = stu #学生对象必须已经保存到数据库,否则错误
arc.save()
return HttpResponse("增加档案")删除数据
1
2
3
4def deletestudent(request):
student = Student.objects.get(pk='180503')
student.delete()
return HttpResponse("删除学生")正向查询
1
2
3
4
5
6
7
8def findstudent(request):
# 获取学生信息
student = Student.objects.first()
print(student)
# 通过学生对象获取档案信息
archive = student.archives
print(archive)
return HttpResponse(student)反向查询
1
2
3
4
5
6def findarchives(request):
#获取档案记录
archive = Archives.objects.first()
#通过档案获取关联学生信息
student = archive.student
return HttpResponse(student)跨关系查询
1
2
3
4
5
6
7def lookup(request):
#根据档案查学生
# student = Student.objects.get(archives__pk=1)
student =Student.objects.get(archives__idcard='13009488384383838')
#根据学生查档案
archive = Archives.objects.get(student__sno='180501')
return HttpResponse(archive)on_delete
- CASECADE 默认,默认级联删除数据
- PROTECT 保护模式,当从表中存在级联记录的时候,删除主表记录会抛出保护异常,从表中不存在级联数据的时候.是允许删除的
- SET_XXX
- NULL 外键字段本身必须允许为空
- DEFAULT 外键字段本身有默认值
- DO_NOTHING 什么都不做
⼀对多
⼀个出版社可以出版多本书,⼀本书只能被⼀个出版社出版。出版社和图书表属于⼀对多,⼀对多⼀般将主表中的主键并到从表中做外键。在模型中用ForeignKey
表示多对⼀
1 | class Publisher(models.Model): |
增加
1
2
3
4
5
6pub = Publisher.objects.first()
pub.books.create(bname='红岩')
book = Book.objects.get(pk=1)
pub.books.add(book) #book必须已经保存到数据库
books = Book.objects.filter(pk__lt=5)
pub.books.bulk_create(list(books))删除和更新
1
2
3pub = Publisher.objects.first()
pub.books.all().delete() #删除出版社出版的所有图书
pub.books.all().update(bname='ddd')正向查询
1
2
3
4
5
6
7def findpublisher(req):
pub = Publisher.objects.first()
print(pub)
# pub = Publisher()
book = pub.book_set.all()
print(book)
return HttpResponse("查询出版社")反向查询
1
2
3def findbook(req):
book = Book.objects.first()
return HttpResponse(book.publisher.pname)跨关系查询
1
2
3
4
5
6
7
8def loopup(req):
# 根据图书获取出版社
pub = Publisher.objects.get(book__bname='花样年华927937')
print(pub)
# 根据出版社获取图书
books = Book.objects.filter(publisher__pname='科技出版社5829')
print(books)
return HttpResponse("跨关系查询")
多对多
⼀个买家可以购买多件商品,⼀件商品可以被多个买家购买,买家和商品之间构成多对多关系,多对多关系必然会生成⼀张中间表:买家-商品表,记录商品和买家的关系,该表包含商品表主键和买家表的主键
1 | from django.db import models |
购买商品
1
2
3
4
5def sellgoods(req):
goods = Goods.objects.get(pk=randint(1,Goods.objects.count())
goods.buyer.add(Buyer.objects.get(pk=randint(1,Buyer.objects.count())))
goods.save()
return HttpResponse("剁⼿成功")删除商品
1
2
3
4buyer = Buyer.objects.get(pk=13)
goods = Goods.objects.filter(pk__lt=10)
buyer.goods_set.clear() #删除所有商品
buyer.goods_set.remove(Goods.objects.get(pk=2)) #删除指定商品正向查询
1
2
3
4
5def findgoods_by_buyer(req):
buyer = Buyer.objects.get(pk=13)
res = buyer.goods_set.all()
print(res)
return HttpResponse("由买家查询商品")反向查询
1
2
3
4
5def findbuyer_by_goods(request):
goods = Goods.objects.last()
buyer = goods.buyer.all()
print(buyer)
return HttpResponse("由商品查询买家")
模型继承
django中的数据库模块提供了⼀个⾮常不错的功能,就是⽀持models的⾯向对象,可以在models中添加Meta,指定是否抽象,然后进行继承
1 | class Animal(models.Model): |
默认模型就是允许继承的,但是默认的继承处理方式不是很合理:
- 默认在⽗类中定义的字段会存在⽗类的表中,子类的数据通用部分会存在⽗表中,子类特有数据会在子表中,子类通过外键进行级联
- 默认方式比较垃圾,效率⽐较低
开发中,需要将⽗类抽象化,在元信息中使用abstract=True
- 抽象化的父类不会再数据库生成表了
- 子类会将父类中的通用数据,复制到子表中
Django 模板(T)
模板用于快速生成动态⻚⾯返回给客户端,模板是⼀个⽂本,用于分离⽂档的表现形式和内容。 模板定义了占位符以及各种用于规范⽂档该如何显示的模板标签。模板通常用于产生HTML,但是Django的模板也能产生任何基于⽂本格式的⽂档。
模板包含两部分:
- html代码
- 模板标签
一、模板位置
在应用中建⽴templates⽬录,好处不需要注册,不好的地方,有多个应用的时候不能复用⻚⾯
第⼆种是放在⼯程的⽬录下,好处是如果有多个应用,可以调用相同的⻚⾯,需要注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16TEMPLATES = [
{
'BACKEND':
'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], #模板绝对路径
'APP_DIRS': True, #是否在应用⽬录下查找模板⽂件
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Django 模板查找机制: Django 查找模板的过程是在每个 app 的 templates⽂件夹中找(行不只是当前 app 中的代码只在当前的 app 的 templates ⽂件夹中找)。各个 app 的 templates 形成⼀个⽂件夹列表,Django 遍历这个列
表,⼀个个⽂件夹进行查找,当在某⼀个⽂件夹找到的时候就停⽌,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found (过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是⼀个app可以用另⼀个app的模板⽂件,弊是有可能会找错了。所以我们使用的时候在templates 中建⽴⼀个 app 同名的⽂件夹,这样就好了。
二、模板的渲染
loader加载
好处是可以加载⼀次模板,然后进行多次渲染
1 | from django.template import loader #导⼊loader |
render加载
1 | from django.shortcuts import render |
##三、模板语法
django模板中包括两部分:变量和内置标签。变量会在模板渲染时被其值代替,内置标签负责逻辑控制
变量
变量在模板中的表示为:,变量名就是render中context中的键。变量可以基本类型中的数值、字符串、布尔,也可以是字典、对象、列表等。django提供了点号来访问复杂数据结构。
- 列表、元组的元素可以使用索引引用,不能使用负索引,语法:变量.索引
- 字典: 字典变量.key
- 对象: 对象.属性 对象.方法名(方法不能有参数)
当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找
字典类型查找
属性查找
方法调用
列表类型索引
如果模板中引用变量未传值,则会被置为空,不会报错,除⾮你对其进行了操作
1 | def handle_var(request): |
过滤器
过滤器是在变量显示之前修改它的值的⼀个方法,过滤器使用管道符。过滤器可以串联调用
1 | {{ 变量|方法}} |
常见的过滤器方法:
1 | | 方法名 | 作用 | 示例 | |
⾃定义过滤器
内置过滤器功能有限,如果不能满足需求,可以自己定义过滤器。
在app里创建⼀个包:templatetags
在包里创建⼀个py⽂件
1
2
3
4
5
6
7
8
9
10
11
12from django import template
# 实例化⾃定义过滤器注册对象
register = template.Library()
# name代表在模板中使用的过滤器的名称
@register.filter(name='hello')
def hello(value,arg):
"""
:param value: 传给hello过滤的值
:param arg: hello自带的参数
:return:
"""
return value + str(arg)在模板中使用
1
2
3
4
5
6
7
8
9
10
11{% load customfilter %} #加载自定义过滤器的模块
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ name |hello:' how are you' }} #使用自定义过滤器
</body>
</html>
内置标签
语法:
1 | {% tag %} |
if标签
1 | {% if express1 %} |
- if表达式中使用以下运算符(优先级从高到低):
- < >= <= == !=
- in 、not in
- is、is not
- not
- and
- or
- 不要在表达式中使用(),可以使用if嵌套实现功能
- 不⽀持 if 3 < b < 5这种写法
for
遍历可迭代对象
1 | {% for x in y %} |
反向迭代(reversed)
1
2
3{% for value in c [1,2,3,4,5] reversed %}
<span>{{ value }}---</span>
{% endfor %}empty 当可迭代对象为空或不存在时执行,否则不执行
1
2
3
4
5{% for value in c %}
<span>{{ value }}---</span>
{% empty %}
数据不存在
{% endfor %}字典迭代
1
2
3
4# e = {'a1':20,'b1':40}
{% for k,v in e.items %}
<div>{{ k }}---{{ v }}</div>
{% endfor%}获取for循环迭代的状态
变量名称 变量说明 forloop.counter 获取迭代的索引 从1开始 forloop.counter0 获取迭代的索引 从0开始 forloop.revcounter 迭代的索引从最⼤递减到1 forloop.revcounter0 迭代的索引从最⼤递减到0 forloop.first 是否为第⼀次迭代 forloop.last 是否为最后⼀次迭代 forloop.parentloop 获取上层的迭代对象 1
2
3
4
5
6
7
8{% for i in c %}
<li>{{ forloop.first }}</li>
<li>{{ forloop.last }}</li>
<li>{{ forloop.counter }}</li>
<li>{{ forloop.counter0 }}</li>
<li>{{ forloop.revcounter }}</li>
<li>{{ forloop.revcounter0 }}</li>
{% endfor %}
ifequal/ifnotequal
用于判断两个值相等或不等的
1 | {% ifequal var var %} |
注释
单行注释
1 | {# 注释的内容 #} |
多行注释
1 | {% comment %} |
跨站请求伪造 csrf
防⽌⽹站受第三方服务器的恶意攻击(确定表单到底是不是本⽹站的表单传递过来的)。csrf相当于在表达中增加了⼀个隐藏的input框,用于向服务器提交⼀个唯⼀的随机字符串用于服务器验证表单是否是本服务器的表单。
使用:
settings.py
1 | MIDDLEWARE = [ |
表单⾥
1 | <form action="" method="post"> |
全局禁用csrf
1
2
3
4
5
6
7
8
9
10#在settings中设置
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]局部禁用csrf
1
2
3
4
5#在不想检验csrf的视图函数前添加装饰器@csrf_exempt。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def csrf1(request):
passajax验证csrf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Ajax提交数据时候,携带CSRF:
a. 放置在data中携带
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="username" type="text" name="username" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function submitForm(){
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
data: { "user":user,'csrfmiddlewaretoken': csrf},
success:function(arg){
console.log(arg);
}
})
}
</script>注意:csrf的意义在于给每⼀个表单都设置⼀个唯⼀的csrf的值并且cookie也存储⼀份当提交表单过来的时候判断cookie中的值和csrf_token中的值是否都为本⽹站生成的如果验证通过则提交否则403
模板导⼊标签( include)
可以把指定html⽂件代码导⼊到当前⽂件,实现模板代码的复用/重用。语法格 式
1 | {% include '路径/xxx.html' %} |
url标签
在模板中url标签可用于反向解析
1 | <h2><a href="{% url 'App:index' %}">动态生成路由地址不带参的跳转</a></h2> |
四、模板继承
在整个⽹站中,如何减少共用⻚⾯区域(比如站点导航)所引起的重复和冗余代码?Django 解决此类问题的⾸选方法是使用⼀种优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造⼀个基础框架模板,行后在其子模板中对它所包含站点公用部分和定义块进行重载
1 | {% extends %}继承父模板 |
使用继承的⼀种常⻅方式是下⾯的三层法
- 创建base.html模板,在其中定义站点的主要外观感受。这些都是不常修改甚⾄从不修改的部分。
- 为每种类型的⻚⾯创建独⽴的模板,例如论坛⻚⾯或者图⽚库。这些模板拓展相应的区域模板
- ⾃⼰的⻚⾯继承⾃模板,覆盖⽗模板中指定block
注意事项:
1 | 1. 如果在模板中使用 {% extends %} ,必须保证其为模板中的第⼀个模板标记。否则,模板继承将不起作用 |
五、静态资源配置
什么是静态资源:css、js、images 需要从外部导⼊的资源
创建static⽂件夹(通常放在根⽬录下)
需要在settings注册
1
2
3
4STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]在模板中使用静态资源
1
2
3{% load static %} #放置到模板开头
<img src="/static/img/img.jpeg" alt=""> #硬编码
<img src="{% static 'img/img.jpeg' %}" alt=""> #动态写法,建议用这种
六、jinja2模板引擎配置
安装jinja2模板引擎
1
pip install jinja2
设置jinja2环境变量
1
2
3
4
5
6
7
8
9
10
11# 在应用⽬录下,创建jinja2_env.py配置⽂件
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env配置(setting.py)
独⽴使用jinja2,不使用Django模板引擎
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#独⽴使用jinja2
INSTALLED_APPS = [
#'django.contrib.admin', # 注释了admin
.....
]
#模板配置
TEMPLATES = [{
'BACKEND':'django.template.backends.jinja2.Jinja2',#jinja2模版
'DIRS': [
os.path.join(BASE_DIR, 'templates'),#模版⽂件位置
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'environment': 'App.jinja2_env.environment', # 配置环境,jinja的配置⽂件位置
},
},
]两个同时使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#模板配置
TEMPLATES = [{
'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模版
'DIRS': [
os.path.join(BASE_DIR, 'templates2'),#修改模版⽂件位置
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'environment': 'App.jinja2_env.environment', # 配置环境,jinja的配置⽂件位置
},
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
静态资源和url
1
2
3
4<p>
<a href="{{ url('app:index') }}">dddd</a>
</p>
<img src="{{ static('images/1.jpg') }}" alt="">
Django 视图(request,response对象)和路由(V)
Django中的视图主要用来接受Web请求,并做出响应。此响应可以是⽹⻚,重定向或404错误,XML⽂档或图像等的HTML内容。在mvt模式中,视图负责从模型中获取数据,然后展示在模板中,是联系模型和模板的桥梁,是业务逻辑层
视图响应的过程:
当用户从浏览器发起⼀次请求时,⾸先django获取用户的请求,然后通过路由(urls)将请求分配到指定的是视图函数。视图函数负责进行相应的业务处理,处理完毕后把结果(可能是json、html等)浏览器
路由
当用户在您的Web应用程序上发出⻚⾯请求时,Django会获取url中请求路径(端⼝之后的部分),然后通过urls.py⽂件查找与路径相匹配的视图,然后返回HTML响应或404未找到的错误(如果未找到)。在urls.py中,最重要的是 “urlpatterns”列表。这是您定义URL和视图之间映射的地方。映射是URL模式中的path对象,例如
1 | from django.conf.urls import patterns, include |
path对象有四个参数
- 模式串:匹配用户请求路径的字符串(和flflask⼀样)
- 视图函数:匹配上用户指定请求路径后调用的是视图函数名
- kwargs:可选参数,需要额外传递的参数,是⼀个字典
- 名称(name):给路由命名,在代码中可以使用name进行反向解析(由name获取用户请求路劲)
另外,如果path中模式串如果不能满⾜你路由规则,还可以使用re_path对象,re_path对象中模式串是正则表达式,其他三个参数和path对象⼀致
模式
path中的模式串是⼀个普通字符串,用于匹配用户的请求路径。如果这种方式无法满⾜复杂路由请求的话,可以使用re_path
完成路由映射,re_path
中模式包含了⼀个上尖括号(^)和⼀个美元符号($)。这些都是正则符号,上尖括号表示从字符串开头匹配。美元符号表示匹配字符串的结尾,这两个符号和到⼀起就表示模式必须完全匹配路径,行不是包含⼀部分。⽐如对于模式:
r'^hello/$'
如果不包含美元符,也就是r'^hello/'
,任何以/hello/的url都可以匹配,例如:/hello/world/、/hello/1/2/等,同样如果不以上尖括号开头,则任何以hello/做结尾的url都可以匹配。
Django检查url模式之前会移除模式前的/,所以url模式前⾯的/可以不写,但如果在地址栏⾥请求的时候不带尾斜杠,则会引起重定向,重定向到带尾斜杠的地址, 所以请求的时候要带尾斜杠
模式匹配的时候要注意:
- Django从上往下进行模式匹配,一旦匹配成功就不会往下继续匹配了
- ⼀个视图函数可以有多个模式匹配
- 模式前⾯不需要加/
- 如果匹配不上,则会引起异常,Django会调用错误处理视图处理(关闭调试模式)
URL配置
在setting中指定根级url配置⽂件,对应的属性ROOT_URLCONF
urlpatterns:⼀个url实例的列表,全在根配置中搞定
导⼊其它url配置: 在应用中创建
urls.py
⽂件,编写匹配规则,在⼯程urls.py
中进行导⼊包含1
2from django.urls import include
urlpatterns = [ path('xxx/',include('App.urls')) ]
Django的请求处理流程:
Django获取用户发出请求
Django在⼯程配置⽂件settings.py中通过ROOT URLCOF配置来确定根URLconf
Django在URLconf中的所有URL模式中,查找第⼀个配的条⽬。
如果找到匹配,将调用相应的视图函数,并将HttpReuest对象传⼊
视图函数回⼀个HttpResponse响应
动态url
前⾯写的url都是静态的,⽆法给视图函数传递数据,要想给视图函数传参数,url必须是动态,带有参数,但Django主张漂亮简介的url,所以不会使用查询字符串(/hello?name=tom),行是采用通配符:
1 | path("hello/<name>/",views.hello) |
在path中使用<参数名>表示所传参数,视图函数中的参数名必须和<>中参数名⼀致。参数可以是以下类型:
- str:如果没有指定参数类型,默认是字符串类型。字符串参数可以匹配除/和空字符外的其他字符串
- int:匹配0和正整数,视图函数的参数将得到⼀个整型值
- slug:匹配由数字、字⺟、-和_组成的字符串参数
- path:匹配任何⾮空字符串,包括/。
在re_path
中,()部分是正则的组, django在进行url匹配时,就会⾃动把匹配成功的内容,作为参数传递给视图函数
位置参数:url中的正则表达式组,和视图函数中的参数⼀⼀对应,函数中的参数名可以随意指定。
1
2
3
4
5#位置参数:hello/name/age
re_path(r'^hello/(\w+)/(\d{1,2})/$',views.hello)
#视图函数
def hello(req,value1,value2): #参数名字随意,但从左往右和url中分组对应
return HttpResponse("哈喽:" + value1 + " age= {}".format(value2))关键字参数:对正则表达式分组进行命名
1
2
3
4
5?P<组名>
url(r'^hello/(?P<name>\w+)/(?P<age>\d{1,2})/$',views.hello),
#视图函数
def hello(req,name,age): #参数名字必须是name和age,和url中命名⼀致,但顺序随意
return HttpResponse("哈喽:" + name + " age= {}".format(age))
匹配/分组算法:
在⼀个匹配模式中要么使用命名分组,要么使用⽆命名分组,不能同时使用
请求的URL被看做是⼀个普通的Python字符串, URLconf在其上查找并匹配。进行匹配时将不包括GET或POST请求方式的参数以及域名。换句话讲,对同⼀个URL的⽆论是POST请求、GET请求、或HEAD请求方法等等 —— 都将路由到相同的函数。
每个捕获的参数都作为⼀个普通的Python字符串传递给视图,⽆论正则表达
式使用的是什么匹配方式
每个捕获的参数都作为⼀个普通的Python字符串传递给视图,⽆论正则表达式使用的是什么匹配方式
视图
视图本质上是⼀个函数(类)。这个函数第⼀个参数的类型是HttpReuest
;它返回⼀个 HttpResponse
实例。为了使⼀个Python的函数成为⼀个Django可识别的视图,它必须 ⾜这两个条件。
作用:接收并处理请求,调用模型和模板,响应请求(返回HttpResponse
或其子类)
- 响应模板
- 重定向
- 直接响应字符串
- 响应错误模板
- json数据
HttpRequest
HttpRequest
是从web服务器传递过来的请求对象,经过Django框架封装产生的,封装了原始的Http请求
- 服务器接收到http请求后,django框架会⾃动根据服务器传递的环境变量创建HttpRequest对象
- 视图的第⼀个参数必须是HttpRequest类型的对象
- 在django.http模块中定义了HttpRequest对象的API
- 使用HttpRequest对象的不同属性值,可以获取请求中多种信息
属性 | 说明 |
---|---|
content-type | 请求的mime类型 |
GET |
⼀个类似于字典的QueryDict对象,包含get请求⽅式的所有参 数,也就是“?”后⾯的内容 |
POST |
⼀个类似于字典的QueryDict对象,包含post请求方式的所有参 数 |
COOKIES | ⼀个标准的Python字典,包含所有的cookie,键和值都为字符串 |
SESSION | ⼀个类似于字典的对象,表示当前的会话,只有当Django启⽤会 话的⽀持时才可⽤ |
PATH | ⼀个字符串,表示请求的⻚⾯的完整路径,不包含域名 |
method | ⼀个字符串,表示请求使⽤的HTTP⽅法,常⽤值包括:GET、 POST, |
FILES | ⼀个类似于字典的QueryDict对象,包含所有的上传⽂件 |
META | 请求的请求头的源信息(请求头中的键值对) |
encoding | 字符编码 |
scheme | 协议 |
META中常用的键:
键 | 说明 |
---|---|
HTTP_REFERER | 来源⻚⾯ |
REMOTE_ADDR | 客户端ip |
REMOTE_HOST | 客户端主机 |
下⾯是常用的方法
方法名 | 说明 |
---|---|
get_host() | 获取主机名+端⼝ |
get_full_path() | 获取请求路径+查询字符串 |
is_ajax() | 如果是ajax请求返回True |
build_absolute_uri() | 完整的url |
QueryDict
QueryDict
是Dict的子类,和Dict最⼤的不同是,值都是列表。用于存储从请求中传递过来的参数,例如对于表单中select、checkbox等多值参数,QueryDict⾮常合适。get、post、files请求都对应⼀个QueryDict
- HttpRequest中QueryDict是不可变的,只能获取值,不能修改
- QueryDict键值对都是字符串
- QueryDict中⼀个键可以对应多个值
1 | #QueryDict |
常用操作
判断get或post请求中包含指定键
1
2if 'name' in req.GET:
pass获取get或post请求中指定键对应的单⼀值
1
2
3print(req.GET.get('name','⽆名'))
print(req.GET['name'])
# 两种方式的区别,如果用下标,键不存在,则包keyerror,get方法则会得到默认值,如果没有默认值则返回None获取指定键对应的多个值(列表)
1
print(req.GET.getlist('name'))
获取所有键值对
1
2for x in req.GET.lists():
print(x)
HttpResponse
每⼀个视图函数必须返回⼀个响应对象,HttResponse对象由程序员创建并返回。
属性 | 说明 |
---|---|
content | 字节字符串 |
charset | 字符编码 |
status_code | http状态码 |
content_type | 指定输出的MIME类型 |
不调用模板,直接返回内容
1 | def hello(req): |
调用模板返回
⼀般用render
函数返回,render
只是HttpResponse的包装,还是会返回⼀个HttpResponse对象
1 | def render_to_response(template_name, context=None, content_type=None,status=None, using=None): |
- template_name : 模板名称。
- context: ⼀组字典的值添加到模板中。默认情况下,这是⼀个空的字典。
- content_type :MIME类型用于生成⽂档
- status :为响应状态代码。默认值为200
1 | def studentlist(req): |
常用方法:
- write(content) 设置内容 == obj.content
- set_cookie() 设置cookie
- delete_cookie() 删除cookie
JsonResponse
JsonResponse
是HttpResponse的子类,用于向客户端返回json
数据。⼀般用于ajax请求。它的content-type
缺省值(默认值)为:application/json
1 | from django.http import JsonResponse #导包 |
参数:
- data:可以是字典(或者内置对象),如果safe设置为False,也可以是json序列化对象
- encoder:编码格式,缺省是django.core.serializers.json.DjangoJSONEncoder
- safe:默认是True,如果传递非字典给data,则报TypeError
系统模块⾃带的json和JsonResponse
1 | from django.http import JsonResponse |
json.dumps
给ajax返回数据以后 js代码中需要将json数据使用Json.parse()
进行解析成js对象
而JsonResponse会⾃动转换为js对象(少了异步Json.parse()的转换)
QuerySet转Json
1 | def courselist(req): |
模型转Json
1 | class Course(models.Model): |
重定向
当浏览器向server发送⼀个请求,要求获取⼀个资源时,在server接收到这个请求后发现请求的这个资源实际存放在另⼀个位置,于是server在返回的response中写⼊那个请求资源的正确的URL,并设置reponse的状态码为301(表示这是⼀个要求浏览器重定向的response),当client接受到这个response后就会根据新的URL重新发起请求。重定向有⼀个典型的特症,即:当⼀个请求被重定向以后,最终浏览器上显示的URL往往不再是开始时请求的那个URL了。这就是重定向的由来。
HttpResponseRedirect是HttpResponse的子类,可以完成⻚⾯的重定向,当执行完特定动作或出现错误时,我们会希望执行的⻚⾯,如果判定用户没有登录则转到登录⻚⾯。重定向可以使用
- HttpResponseRedirect
- redirect(是HttpResponseRedirect的简写)
HttpResponseRedirect只⽀持硬编码url,不能直接使用命名url,在使用URL命名时,我们需要先通过URL反向解析方法reverse先对命名URL进行解析,然后再使用HttpReponseRedirect定向。
不带参数重定向
1 | #urls.py |
带参数重定向
1 | #urls.py |
反向解析
根据namespace 和 name 查找真实路径:
- namespace 在根url 包含子url时指定
- name 在具体的函数后 指定,子url指定
1 | # urls.py中 |
错误视图
Django内置了处理HTTP错误的视图(在django.views.defaults包下),主要错误及视图包括:
- 403错误:permission_denied (权限拒绝)
- 404错误:page_not_found (找不到指定⽂件)
- 500错误:server_error (服务器内部错误)
404错误及视图
url匹配失败后,django会调用内置的 django.views.defaults.page_not_found()函数,该视图函数会调用 404.html的模板进行显示。开发阶段可以开启调试模式,但产品上线后,要关闭调试模式。关闭调试模式后,会显示⼀个标准的错误⻚⾯。
1 | # 在项⽬的settings配置⽂件中设置 |
404错误界⾯可以⾃定义: 在项⽬templates⽬录⾯创建404.html,django找不到界⾯时,就会显示该界⾯了。缺省会传递参数request_path,就是出错的url
1 | <!DOCTYPE html> |
500错误及视图
若是在执行视图函数时出现运行时错误,Django会默认会调用django.views.defaults.server_error 视图,加载并显示 500.html 模板,可以在项⽬的templates⽬录下,创建500.html来⾃定义该界面
Django分页
Paginator 分页器
Paginator
用于分页,但Paginator
并不具体管理具体的页的处理,而是使用Page
对象管理具体页⾯
创建分页器对象
格式: Paginator(<query_set查询集>,每也显示数据的条数)
对象的属性
count
分页对象的个数num_pages
总页数page_range
页码的列表per_page
每页显示的条数
方法
page(num)
返回page对象 如果给定的页码不存在 则抛出异常
page 对象
page
对象具体负责每页的处理,包括每页的数据,当前页的页码,是否有上⼀页或下⼀页等
类别 | 名称 | 说明 |
---|---|---|
属性 | object_list | 当前页面上的所有数据 |
属性 | number | 当前页码值 |
属性 | paginator | 返回Paginator对象 |
方法 | has_next | 是否有下一页 |
方法 | has_previous | 是否有上一页 |
方法 | has_other_pages | 是否有上一页或下一页 |
方法 | next_page_number | 返回下一页的页码 |
方法 | previous_page_number | 返回上⼀页的页码 |
方法 | len | 返回当前页数据的个数 |
实例:
步骤1. models.py中添加模型
1 | class User(models.Model): |
步骤2. 在应用App中urls.py中添加路由
1 | from App02 import views |
步骤3. 在views.py中添加视图
1 | def fenye(request,page=1): |
步骤4. 在userlist.html页面中展示
1 | <!DOCTYPE html> |
Django中Cookie和Session
HTTP被设计为”无状态”,也就是俗称“脸盲”。 这⼀次请求和下⼀次请求之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,⽤户代理等)来识别来⾃同一人的连续请求。实现状态保持的方式:在客户端或服务器端存储与会话有关的数据 (客户端与服务器端的⼀次通信,就是⼀次会话)
- cookie
- session
不同的请求者之间不会共享这些数据,cookie和session与请求者一一对应
cookie
cookies 是浏览器为 Web 服务器存的一小段信息。 每次浏览器从某个服务器请求页面时,都会自动带上以前收到的cookie。cookie保存在客户端,安全性较差,注意不要保存敏感信息。典型应用:
- 网站登录
- 购物车
设置cookie
1 | HttpResponse.set_cookie(key, value='', max_age=None, expires=None,path='/', domain=None, secure=None, httponly=False) |
参数:
- key: cookie的名称(*)
- value: cookie的值,默认是空字符
- max_age:cookies的持续有效时间(以秒计),如果设置为 None,cookies 在浏览器关闭的时候就失效了。
- expires:cookies的过期时间,格式:”Wdy, DD-Mth-YY HH:MM:SS GMT” 如果设置这个参数,它将覆盖max_age。
- path: cookie⽣效的路径前缀,浏览器只会把cookie回传给带有该路径的页面,这样你可以避免将cookie传给站点中的其他的应⽤。/ 表示根路径,特殊的:根路径的cookie可以被任何url的⻚⾯访问
- domain: cookie⽣效的站点。你可⽤这个参数来构造⼀个跨站cookie。如,domain=”.example.com” 所构造的cookie对下⾯这些站点都是可读的: www.example.com 、www2.example.com。如果该参数设置为None,cookie只能由设置它的站点读取。
- secure: 如果设置为 True ,浏览器将通过HTTPS来回传cookie。
- httponly: 仅http传输 不能使⽤js获取cookie
同set_cookie
,不同点在于设置salt,即加盐,加密存储cookie数据
HttpResponse.set_signed_cookie(key, value, salt=’’, max_age=None,expires=None, path=’/‘, domain=None, secure=None, httponly=False)
获取cookie
1 | HttpRequest.COOKIES.get(key) |
删除cookie
1 | HttpResponse.delete_cookie(key, path='/', domain=None) |
Session
cookie看似解决了HTTP(短连接、⽆状态)的会话保持问题,但把全部⽤户数据保存在客户端,存在安全隐患,于是session出现了。我们可以把关于用户的数据保存在服务端,在客户端cookie里加⼀个sessionID(随机字符串)。其⼯作流程:
当用户来访问服务端时,服务端会生成⼀个随机字符串;
当用户登录成功后 把 {sessionID :随机字符串} 组织成键值对加到cookie里发送给用户;
服务器以发送给客户端 cookie中的随机字符串做键,⽤户信息做值,保存用户信息
再访问服务器时客户端会带上sessionid,服务器根据sessionid来确认用户是否访问过网站
session配置
步骤1. 首先在settings.py中有如下配置(系统默认已经设置了
)
1 | INSTALLED_APPS = [ |
步骤2. 进行数据迁移,⽣成session使⽤的数据库表
session操作
session设置
1 | def doregister(request): |
session获取
1 | def index(request): |
session删除
1 | def logout(request): |
clear() 清空所有session 但是不会将session表中的数据删除
flush() 清空所有 并删除表中的数据
logout() 退出登录 清除所有 并删除表中的数据
del req.session[‘key’] 删除某⼀个session的值
session过期时间
1 | req.session.set_expiry(5) |
cookie和session的区别与联系
区别:
- session将数据存储与服务器端 cookie存储在客户端
- cookie 存储在客户端,不安全,session存储在服务器端,客户端只存sesseionid,安全
- cookie在客户端存储值有⼤⼩的限制,⼤约⼏kb。session没有限制
联系:
- session 基于cookie
Django 使用阿里云短信服务
短信服务的使用场景:
- APP、网站注册账号,向手机下发验证码;
- 登录账户、异地登录时的安全提醒;
- 找回密码时的安全验证;
- ⽀付认证、身份校验、⼿机绑定等。
步骤1. 安装阿里云SDK核心库
1 | pip install aliyun-python-sdk-core-v3 |
发送步骤:
创建Client实例。在创建Client实例时,您需要获取Region ID、AccessKey ID和AccessKey Secret
创建API请求并设置参数
发起请求并处理应答或异常
工具类tool.py
:
1 | from aliyunsdkcore.client import AcsClient |
views.py
视图中调用
1 | def send_sms(request): |
Django From表单验证
概要
通常情况下,我们需要真的手动在HTML页面中,编写form标签和其内的其它元素。但这费时费⼒,行且有可能写得不太恰当,数据验证也⽐较麻烦。有鉴于此, Django在内部集成了⼀个表单模块,专门帮助我们快速处理表单相关的内容。Django的表单模块给我们提供了下面三个主要功能:
- 准备和重构数据⽤于⻚⾯渲染
- 为数据创建HTML表单元素
- 接收和处理用户从表单发送过来的数据
Form相关的对象包括
Widget:⽤来渲染成HTML元素的部件,如:forms.Textarea对应HTML中的
<textarea>
标签Field:Form对象中的⼀个字段,如:EmailField表示email字段,如果这个字段不是有效的Email地址格式,就会产⽣错误。
Form:⼀系列Field对象的集合,负责验证和显示HTML元素
Form Media:⽤来渲染表单的CSS和JavaScript资源。
基本使用
Form对象封装了⼀系列Field和验证规则,Form类都必须直接或间接继承⾃django.forms.Form,定义Form有两种方式:
直接继承Form
1
2class XXXForm(forms.Form):
pass结合Model,继承django.forms.ModelForm
1
2
3
4
5
6
7class XXX(models.Model):
字段 = models.CharField(max_length=30)
字段 = models.CharField(max_length=20)
class XXXForm(ModelForm):
class Meta:
model = XXX
field = ('字段', '字段') # 只显示model中指定的字段,显示所有是⽤'__all__'
示例代码:
需求: 注册验证
步骤1. models
1 | class User(models.Model): |
步骤2. RegisterForm
1 | class RegisterForm(forms.Form): |
步骤3. views.py
1 | # From表单模块 |
步骤4. 在模板templates
中 register.html
使用
建议:尽量使用自己定义的文本框信息,等于Form表单对象只渲染错误信息,不使用渲染的表单信息
1 |
|
常用的field类
核心字段参数
参数名 | 说明 |
---|---|
require | 给字段添加必填属性,不能空着,若要表示⼀个字段不是必需的,设置required=False |
label | label参数⽤来给字段添加‘⼈类友好’的提示信息。如果没有设置这个参数,那么就⽤字段的⾸字⺟⼤写名字 |
label_suffix | Django默认为上⾯的label参数后⾯加个冒号后缀,如果想⾃定义,可以使⽤ label_suffix 参数 |
initial | 为HTML⻚⾯中表单元素定义初始值。也就是input元素的value参数的值 |
widget | 指定渲染Widget时使⽤的widget类,也就是这个form字段在HTML⻚⾯中是显示为⽂本输⼊框?密码输⼊框?单选按钮?多选框?还是别的 |
help_text | 该参数⽤于设置字段的辅助描述文本 |
error_messages | 该参数允许你覆盖字段引发异常时的默认信息。 传递的是⼀个字典,其值为你想覆盖的错误信息 |
validators | 指定⼀个列表,其中包含了为字段进⾏验证的函数 |
localize | localize参数帮助实现表单数据输⼊的本地化。 |
disabled | 设置有该属性的字段在前端⻚⾯中将显示为不可编辑状态 |
核心字段
BooleanField
1 | 默认的Widget:CheckboxInput |
IntegerField
1 | 默认的Widget:当Field.localize是False时为NumberInput,否则 |
CharField
1 | 默认的Widget:TextInput |
ChoiceField
1 | 默认的Widget:Select |
DateField
1 | 默认的Widget:DateInput |
DateTimeField
1 | 默认的Widget: DateInput |
DecimalField
1 | 默认的Widget:当Field.localize是False时为NumberInput,否则 |
FloatField
1 | 默认的Widget:当Field.localize是False时为NumberInput,否则 |
EmailField
1 | 默认的Widget:EmailInput |
ImageField
1 | 默认Widget: ClearableFileInput |
FileField
1 | 默认Widget: ClearableFileInput |
ModelChoiceField
1 | 默认的Widget:Select |
Form常用的属性和方法
名称 | 说明 | 示例 |
---|---|---|
cleaned_data(字典) | 表单中验证通过的⼲净数据 | form.cleaned_data form.cleaned_data.get(‘username’) |
changed_data | 有变化的字段的列表 | form.changed_data |
fields(字典) | 表单中的字段属性 | form.fiedls[‘username’] |
is_bound | 表单是否绑定数据 | form.is_bound |
errors(字典) | 错误信息 | form.errors |
is_valid() | 表单中数据是否验证通过,通过返回True,否则返回False | form.is_valid() |
has_changed() | 检查表单数据是否已从初始数据更改 | form.has_changed() |
errors.as_json(escape_html=False) | 返回JSON序列化后的错误信息字典 | form.errors.as_json() |
表单渲染的选项
对于
1 | 1. {{ form.as_table }} 以表格的形式将它们渲染在 <tr> 标签中 |
注意,你必须自己提供
1 | 有⽤的属性包括:{{ field }} |
form.as_ul
1 | <form action="/add/" method="post"> |
手动渲染
1 | <form action="/add/" method="post"> |
循环渲染
1 | {% for field in form %} |
循环隐藏和可⻅字段
1 | {# Include the hidden fields #} |
可重⽤的表单模板
1 | # In your form template: |
Django 用户认证
概要
auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理。auth可以和admin模块配合使用, 快速建立网站的管理系统。在INSTALLED_APPS中添加’django.contrib.auth’使⽤该APP, auth模块默认启⽤。主要的操作包括:
create_user
创建用户authenticate
验证登录login
记住⽤户的登录状态logout
退出登录is_authenticated
判断⽤户是否登录@login_required
判断⽤户是否登录的装饰器
前期配置
说明
Django 在新建⼯程时已经为使⽤⽤户认证系统做好了全部必要的配置。不过有可能你并⾮使⽤ django-admin 命令新建的⼯程,或者你使⽤的是⼀个正在开发中的项⽬,因此最好再检查⼀下 settings.py ⽂件中是否已经做好了全部必要配置。
配置
在setting.py的INSTALLED_APPS
1
2
3
4
5INSTALLED_APPS = [
'django.contrib.auth',
# 用户权限处理部分依赖的应⽤
'django.contrib.contenttypes',
]在setting.py的MIDDLEWARE
1
2
3
4
5
6MIDDLEWARE = [
# 会话⽀持中间件
'django.contrib.sessions.middleware.SessionMiddleware',
# 认证⽀持中间件
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
user对象
属性
属性 | 说明 | 备注 |
---|---|---|
username | 小于等于30个字符。 ⽤户名可以包含字⺟、 数字、_、@、+、.和- 字符 | 必选 |
password | 密码的哈希及元数据。(Django 不保存原始密码)。原始密码可以⽆限⻓⽽且可以包含任意字符。参⻅密码相关的⽂档 | 必选 |
is_active | 布尔值。指示⽤户的账号是否激活,缺省值为True | 必选 |
first_name | 少于等于30个字符 | 可选 |
last_name | 少于30个字符 | 可选 |
邮箱地址 | 可选 | |
groups | 与Group 之间的多对多关系 | 可选 |
user_permissions | 与Permission之间的多对多关系 | 可选 |
is_staff | 布尔值,指示⽤户是否可以访问Admin 站点 | 可选 |
is_superuser | 布尔值。只是这个⽤户拥有所有的权限⽽不需要给他们分配明确的权限。 | 可选 |
last_login | ⽤户最后⼀次登录的时间 | 默认值 |
date_joined | 账户创建的时间。当账号创建时,默认设置为当前的date/time | 默认值 |
说明
- User 对象属性:username, password(必填项)password用哈希算法保存到数据库
- is_staff: 用户是否拥有网站的管理权限.
- is_active : 是否允许用户登录, 设置为 False ,可以不用删除⽤户来禁止用户登录
拓展 User 模型
说明
用户可能还包含有头像、昵称、介绍等等其它属性,因此仅仅使⽤ Django 内置的 User 模型是不够。所有有些时候我们必须使⽤在系统的User上进⾏拓展
继承AbstractUser
说明
推荐方式、django.contrib.auth.models.User 也是继承⾃ AbstractUser 抽象基类,而且仅仅就是继承了 AbstractUser ,没有对 AbstractUser 做任何的拓展
在app的models.py中
1 | class User(AbstractUser): |
注意:
为了让 Django ⽤户认证系统使⽤我们自定义的用户模型,必须在 settings.py
里通过 AUTH_USER_MODEL 指定⾃定义用户模型所在的位置
1 | AUTH_USER_MODEL = 'app名字.User' |
迁移
1 | python manage.py makemigrations |
常用操作
验证登录
当用户登录的时候用 authenticate(username=username,password=password) 验证⽤户是否登录,如果数据库中存在用户输⼊的账号和密码,返回⼀个user对象,否则返回None。底层将password⽤hash算法加密后和数据库中password进⾏对比
示例代码
1 | def myauthenticate(request): |
注册操作
当⽤户注册的时候⽤ create_user(username,password,email) 默认情况下is_active=True,is_staff=False,is_superuser=False 。底层将password⽤hash算法加密之后存储到数据库中
示例代码
1 | def register_view(request): |
登录操作
当⽤户登录的时候⽤ login(request,user) 来记住⽤户的登录状态该函数接受⼀个HttpRequest对象,以及⼀个认证了的User对象此函数使⽤django的session框架给某个已认证的⽤户附加上session id等信息
示例代码
1 | def login_view(request): |
登出操作
当⽤户注销的时候⽤ logout(request) ,只需要⼀个参数request
示例代码
1 | from django.contrib.auth import logout |
修改密码
第一种: 自动签名,使用set_password
修改,底层最终还是使用make_password
1 | user = auth.authenticate(username=username,password=old_password) |
第二种: 手动签名,使用make_password
1 | def test_pass(request): |
如果报错需要在setting.py中添加密码配置
1 | PASSWORD_HASHERS = [ |
路由保护
@login_required 修饰器修饰的view函数会先通过session key检查是否登录,已登录⽤户可以正常的执⾏操作, 未登录⽤户将被重定向到login_url指定的位置. 若未指定login_url参数, 则重定向到settings.LOGIN_URL
示例代码
1 | # settings 配置 |
业务中修改密码
1 | def change_password(request): |
前端验证登录
如果是真正的 User 对象,返回值恒为 True 。 ⽤于检查⽤户是否已经通过了认证。 通过认证并不意味着⽤户拥有任何权限,甚⾄也不检查该⽤户是否处于激活状态,这只是表明⽤户成功的通过了认证。 这个方法很重要, 在后台⽤request.user.is_authenticated()判断⽤户是否已经登录,如果true则可以向前台展示request.user.name
示例代码
1 | # 在后台的视图函数⾥可以⽤request.user.is_authenticated()判断⽤户是否登录在前端页面中可以用 |
Django 图形验证码
安装django-simple-captcha库
在⽹站开发的登录⻚⾯中,经常会需要使⽤到图形验证码来验证。在Django中,django-simple-captcha库包提供了图形验证码的使⽤。
1 | pip install django-simple-captcha |
setting配置
步骤一. settings.py中安装应用,在INSTALL_APPS 添加如下代码
1 | INSTALLED_APPS = [ |
步骤二. settings.py中设置验证码样式
1 | # 图形验证码配置 |
步骤三. urls.py中设置路由
1 | urlpatterns = [ |
步骤四. 迁移数据库
1 | python manage.py migrate |
建立表单
1 | # forms.py |
##实现
步骤一. 在应用的urls.py
中添加路由
1 | # 应⽤的urls.py |
步骤二. 前端页面
1 | # 前端⻚⾯ |
步骤三. views.py视图
1 | import json |
Django 发送邮件
setting配置
网易邮箱发送配置
1 | # smtp服务的邮箱服务器 |
QQ邮箱发送配置:
1 | # smtp服务的邮箱服务器 |
发送邮件
1 | def mail_send(request): |
邮箱验证激活
说明
- 处理用户注册数据,存入数据库,is_active字段设置为False,用户认证之前不允许登录
- 产⽣token,⽣成验证连接URL
- 发送验证邮箱
- 用户通过认证邮箱点击验证链接,设置is_active字段为True,可以登录
- 若验证链接过期,删除用户在数据库中的注册信息,允许用户重新注册(username,email字段具有唯一性)
验证邮箱链接
- 产生token,发送邮件
- 处理验证链接,这里采用base64加密,及itsdangerous第三方库序列化(自带时间戳)
- itsdangerous需要下载:
pip install itsdangerous
- itsdangerous需要下载:
1 | from itsdangerous import URLSafeTimedSerializer as utsr |
注册发送邮箱
模型:models.py
1 | class User(models.Model): |
路由:urls.py
1 | app_name='App' |
模板: register.html
1 |
|
active.html
1 |
|
视图: views.py
1 | def check_user(request): |
激活用户
1 | def active_user(request,token): |
Django 富文本编辑器
⼀般⽤于写⽂章 编辑内容⾃带样式
安装
1 | pip install django-tinymce |
配置settings⽂件
在INSTALL_APPS 添加如下代码
1 | INSTALLED_APPS = [ |
在settings.py下添加如下代码
1 | #富⽂本编辑器的配置 |
添加视图函数
1 | def index(req): |
前台模板的展示
1 |
|
Django 文件上传
表单注意
在settings.py⽂件下添加如下代码
1 | #设置上传⽂件路径 |
文件上传对象的属性和方法
名称 | 说明 |
---|---|
file.name | 获取上传的名称 |
file.size | 获取上传⽂件的大小(字节) |
file.read() | 读取全部(适用于小文件) |
file.chunks() | 按块来返回文件 通过for循环进⾏迭代,可以将大⽂件按照块来写入到服务器 |
file.multiple_chunks() | 判断文件是否大于2.5M 返回True或者False |
创建上传文件的表单
前端模板
1 | <!DOCTYPE html> |
views.py
1 | from django.conf import settings |
封装文件上传的类
可以⾃定义⼀个类实现⽂件上传,⽂件上传类可以:
- 检查文件类型
- 检查文件大小
- 是否生成随机文件名
1 | class FileUpload: |
Django 站点管理
配置admin应用
默认django添加这个应用
1 | INSTALLED_APPS = [ |
创建管理员用户
1 | python manage.py createsuperuser |
依次输入用户名->邮箱->密码->确认密码
汉化
1 | LANGUAGE_CODE = 'zh-Hans' |
在App/admin.py 里面注册自己的模型类
1 | from .models import Grade,Students |
配置后台页面和添加数据的展示
1 | #配置数据的展示 |
关联对象
1 | #TabularInline 横着展示添加学⽣的布局 |
bool值的显示男女
models.py
1 | def ssex(self): |
在admin.py中
1 | list_display = ['pk','sname',ssex,'sage','grade'] |
Django 中间件
中间件其实就是⼀个类,是介于request与response处理之间的⼀道处理过程(类似装饰器),相对比较轻量级,每个中间件都会负责⼀个功能,例如,AuthenticationMiddleware,与sessions处理相关,中间件,在请求到来和结束后,django会根据⾃⼰的规则在合适的时机执行中间件中相应的方法并且在全局上改变django的输⼊与输出。因为改变的是全局,所以需要谨慎使用,用不好会影响到性能
Django 中间件作用和可实现功能
作用
- 修改请求,即传送到 view 中的 HttpRequest 对象。
- 修改响应,即 view 返回的 HttpResponse 对象。
可实现功能
- 统计
- 黑名单
- 白名单
- 界面友好化(捕获异常)
Django 默认的中间件配置:
1 | # 中间件重上往下执行 |
中间件执行过程
用户请求到达中间件之后,先按照正序执行每个注册中间件,的process_request
方法,process_request
方法返回的值是None
,就依次执行,如果返回的值是HttpResponse
对象,不再执行后面的process_request
方法,而是执行当前对应中间件的process_response
方法,将HttpResponse
对象返回给浏览器。(下图红色路线进行)也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了⼀个HttpResponse
对象,那么第4,5,6中间件的process_request
和process_response
方法都不执行,顺序执行3,2,1中间件的process_response
方法。
process_request
方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view
方法,process_view
方法返回 None,继续按顺序执行,所有process_view
方法执行完后执行视图函数。假如中间件3 的process_view
方法返回了HttpResponse
对象,则4,5,6的process_view
以及视图函数都不执行,直接从最后⼀个中间件,也就是中间件6的process_response方法开始倒序执行。
Django中的中间件方法
process_request
在执行路由前被调⽤,每个请求上都会调⽤,不主动进行返回或返回HttpResponse对象
1 | process_request(self, request) |
参数:
- request,是⼀个HttpRequest请求对象
返回值:
返回None会继续调用下⼀个中间件的process_request方法,返回HttpResponse,则执行自己process_response
process_view
调用视图之前执行,每个请求都会调用,不主动进行返回或返回HttpResponse对象
1 | process_view(self,request,view_func,view_args,view_kwargs) |
参数:
- request:HttpRequest对象
- view_func:是⼀个即将调⽤的视图函数,不是字符串函数名
- view_args:传递给视图函数的位置参数
- view_kwargs:传递给视图函数的关键字参数
返回值:
如果返回None,会继续执行处理此请求,然后调⽤下⼀个中间件的process_view,直⾄执行视图函数;如果返回HttpResponse,则直接执行最后⼀个中间件的process_response
process_template_response
在视图刚好执⾏完后进⾏调⽤,只要视图返回⼀个render⽅法返回的对象,就会调⽤process_template_response,不主动进⾏返回或返回HttpResponse对象
1 | process_template_response(self, request, response) |
参数:
- request HttpRequest对象
- response 是⼀个由Django view或者中间件返回的
- TemplateResponse 对象
返回值:
必须返回⼀个render⽅法执⾏后的response对象,它可以修改view中返回的 response.template_name 和 response.context_data,或者为view返回的模板增加⼀个商标等等。你不需要明确的渲染响应,当所有的template响应中间件处理完成后会被⾃动渲染
process_response
所有响应返回浏览器之前调用,每个请求都会调用,返回HttpResponse对象
1 | process_response(self,request,response) |
参数:
- request :HttpRequest对象
- response : HttpResponse对象
返回值:必须是HttpResponse对象
process_exception
当视图抛出异常时调⽤,返回None或返回HttpResponse对象
1 | process_exception(self,request,exception) |
参数:
- request: HttpRequest 对象
- exception:view函数中raise的Exception对象,当view 函数raise⼀个exception的时候调⽤process_exception
自定义中间件
App下Mymiddleware.py
1 | from django.utils.deprecation import MiddlewareMixin |
启用中间件
在settings中进⾏配置,MIDDLEWARE中添加:模块名.Mymiddleware.类名
1 | MIDDLEWARE = [ |
Django 缓存
缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。
在Django中,当用户请求到达视图后,视图会先从数据库提取数据放到模板中进行动态渲染,渲染后的结果就是用户看到的网页。如果用户每次请求都从数据库提取数据并渲染,将极大降低性能,不仅服务器压力大,而且客户端也无法即时获得响应。如果能将渲染后的结果放到速度更快的缓存中,每次有请求过来,先检查缓存中是否有对应的资源,如果有,直接从缓存中取出来返回响应,节省取数据和渲染的时间,不仅能大大提高系统性能,还能提高用户体验。
缓存使用场景:缓存主要适用于对⻚面实时性要求不高的⻚面。存放在缓存的数据,通常是频繁访问的,而不会经常修改的数据。
缓存方式:
- 数据库
- 文件
- 内存
- redis等
缓存配置
数据库缓存
settings.py
1 | CACHES = { |
生成缓存表
1 | python manage.py createcachetable |
文件缓存
1 | CACHES = { |
redis缓存
需要安装:pip install django-redis
1 | CACHES = { |
缓存使用
视图函数
1 | from django.views.decorators.cache import cache_page |
模板局部缓存
1 | {% load cache %} |
全站缓存
1 | MIDDLEWARE = [ |
手动设置缓存
- 设置缓存:
cache.set(key,value,timeout)
- 获取缓存:
cache.get(key)
1 | def cache_data1(request): |
Django 异步任务队列
Celery简介
Celery 是一个基于python开发的异步任务队列/基于分布式消息传递的作业队列, 通过它可以轻松的实现任务的异步处理。它侧重于实时操作,但对调度支持也很好。Celery用于生产系统每天处理数以百万计的任务。Celery是用Python编写的, 但该协议可以在任何语言实现。它也可以与其他语言通过webhooks实现。Celery建议的消息队列是RabbitMQ
,但提供支持Redis, Beanstalk, MongoDB, CouchDB, 和数据库(使用SQLAlchemy的或Django的 ORM) 。Celery是易于集成Django, Pylons 和 Flask,使用 django-celery, celery-pylons and Flask-Celery 附加包即可。它的特点:
方便查看定时任务的执行情况, 如 是否成功, 当前状态, 执行任务花费的时间等.
使用功能齐备的管理后台或命令行添加,更新,删除任务
方便把任务和配置管理相关联
可选多进程, Eventlet 和 Gevent 三种模型并发执行
提供错误处理机制.
提供多种任务原语, 方便实现任务分组,拆分,和调用链
支持多种消息代理和存储后端.
Celery 是语言无关的,它提供了python 等常⻅语言的接口支持
Celery的相关概念
celery架构图
- task 就是任务,包括异步任务和定时任务
- 中间人,接收生产者发来的消息即Task,将任务存入队列。任务的消费者是Worker。Celery本身不提供队列服务,推荐用
Redis
或RabbitMQ
实现 队列服务。 - worker 执行任务的单元,它实时监控消息队列,如果有任务就获取任务并执行它
- backend 用于存储任务的执行结果。Celery支持以不同方式存储任务的结果, 包括AMQP, redis,memcached, mongodb,SQLAlchemy, Django ORM, Apache Cassandra, IronCache 等。
- beat 定时任务调度器,根据配置定时将任务发送给Broler。
应用场景
- 异步调用:那些用户不关心的但是又存在在我们API里面的操作 我们就可以用异步调用的方式来优化(发送邮件或者上传头像)
- 定时任务:定期去统计日志,数据备份,或者其他的统计任务
Celery的安装
安装
1 | pip install celery |
配置
在 settings.py文件中设置
1 | INSTALLED_APPS = ( |
创建celery实例
在settings.py的同级目录下新建celery.py
1 | from __future__ import absolute_import #绝对路径导入 |
在settings.py同级目录下的init.py加入
1 | from future import absolute_import |
Celery的使用
创建任务
在需要使用异步任务的APP目录下新建tasks.py
1 | # 异步发送邮件 |
调用
在views.py内的调用
1 | # 任务函数名.delay(参数,,,,) |
生成数据库表
1 | python manage.py migrate django_celery_results |
启动worker
1 | celery -A 你的工程名 worker -l info |
注意:修改tasks.py的内容后 要重启celery的服务
获取任务执行结果
异步任务执行完毕后,会自动触发信号:
- before_task_publish
- after_task_publish
- task_prerun
- task_postrun
- task_success
- task_failure
- task_revoked
1 | from celery.signals import task_success |
定时任务和计划任务
定时任务
1 | celery -A 你的工程名称 beat -l info |
在settings.py文件添加
1 | # 定时任务 |
计划任务时间
1 | from celery.schedules import crontab |
注意: 我们启动定时任务服务时 也要先开启worker。如果只开启定时服务 没有开启worker服务 那么定时任务会被放入任务队列,但是由于没有干活儿的worker 那么任务是不会被执行,当worker服务被启动后 会立刻去任务队列领任务并执行
你的任务一定要确保是可以正常执行的
其它
查看异步任务情况
Celery提供了一个工具flower,将各个任务的执行情况、各个worker的健康 状态进行监控并以可视化的方式展现
安装flower
1
pip install flower
启动flower(默认会启动一个webserver,端口为5555):
1
celery flower --broker=redis://localhost:6379/5
即可查看
1
http://localhost:5555
内存泄漏
说明:⻓时间运行Celery有可能发生内存泄露,可以像下面这样设置
CELERYD_MAX_TASKS_PER_CHILD = 1000 # 每个worker执行了多少任务就会死掉
常用配置清单
1 | #from kombu import Queue, Exchange |
Django LOG日志
Log简介
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:
通过log的分析,可以⽅便⽤户了解系统或软件、应⽤的运⾏情况;如果你的应⽤log⾜够丰富,也可以分析以往⽤户的操作⾏为、类型喜好、地域分布或其他更多信息;如果⼀个应⽤的log同时也分了多个级别,那么可以很轻易地分析得到该应⽤的健康状况,及时发现问题并快速定位、解决问题,补救损失
Log的用途
不管是使⽤何种编程语⾔,⽇志输出⼏乎⽆处不再。总结起来,⽇志⼤致有以下⼏种⽤途:
问题追踪:通过⽇志不仅仅包括我们程序的⼀些bug,也可以在安装配置时, 通过⽇志可以发现问题
状态监控:通过实时分析⽇志,可以监控系统的运⾏状态,做到早发现问题、 早处理问题
安全审计:审计主要体现在安全上,通过对⽇志进⾏分析,可以发现是否存在非授权的操作
Log的等级
DEBUG最详细的⽇志信息,典型应⽤场景是 问题诊断
INFO信息详细程度仅次于DEBUG,通常只记录关键节点信息,⽤于确认⼀切都是按照我们预期的那样进⾏⼯作
WARNING当某些不期望的事情发⽣时记录的信息(如,磁盘可⽤空间较低),但是此时应⽤程序还是正常运⾏的
ERROR由于⼀个更严重的问题导致某些功能不能正常运⾏时记录的信息 如IO操作失败或者连接问题
CRITICAL当发⽣严重错误,导致应⽤程序不能继续运⾏时记录的信息
Log的四大组件
Loggers
提供应⽤程序代码直接使⽤的接⼝
Handlers
⽤于将⽇志记录发送到指定的⽬的位置
1 | FileHandler:logging.FileHandler;⽇志输出到⽂件 |
Filters
提供更细粒度的⽇志过滤功能,⽤于决定哪些⽇志记录将会被输出(其它的⽇志记录将会被忽略)
Formatters
⽤于控制⽇志信息的最终输出格式
1 | %(levelno)s:打印⽇志级别的数值 |
示例
1 | import logging |
Django中的配置
1 | # 日志配置 |
基于类的视图
视图是可调用的,它接收请求并返回响应。这可能不仅仅是一个函数,Django提供了一些可用作视图的类的示
例。这些允许您通过利用继承和mixin来构建视图并重用代码
基于类的视图(Class-based views)提供了另一种将视图实现为Python对象而不是函数的方法。它们不替换基于
函数的视图,但与基于函数的视图相比具有一定的差异和优势:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
Function Based View FBV
1 | # Create your views here. |
CBV Class Based View 基于类的视图
1 | # CBV Class Based View 基于类的视图 |
内建的基于类的视图的层次结构:
- 基本视图:view 、TemplateView、RedirectView
- 通用显示视图:DetailView、ListView
- 通用编辑视图:FormView、CreateView、 UpdateView、DeleteView
- 通用日期视图: ArchiveIndexView、YearArchiveView、 MonthArchiveView、 WeekArchiveView、DayArchiveView、TodayArchiveView、DateDetailView
- 基于类的视图mixins
- 简单的mixins:ContextMixin、TemplateResponseMixin
- 单个对象mixins:SingleObjectMixin、SingleObjectTemplateResponseMixin
- 多个对象混合:MultipleObjectMixin、MultipleObjectTemplateResponseMixin
类视图的基本使用
所有类视图都继承自Django提供的父类View,可以使用from django.views import View或from django.views.generic import View来导入父类View。
1 | #views.py |
路由注册:
1 | urlpatterns = [ |
基本视图
根视图View类
提供适合各种应用程序的基本视图类。所有视图都继承自 View 该类,该类处理将视图链接到URL,HTTP方法调度 和其他简单功能。
View类核心代码在as_view
和dispatch
方法中,其中as_view
是类方法(@classonlymethod
),只能通过类调用,不能通过对象调用,它是类视图的入口点。注意这里调用的时候是通过类名.as_view()调用的。 其中,as_view方法主要执行逻辑:
1 |
|
整个as_view方法是一个装饰器方法,它返回内部函数view,所以as_view()执行其实就是内部函数view执行。内部 函数view主要逻辑就是:as_view()=>view()=>dispatch()=>相应的http方法
- 实例化本类对象
- 接受请求对象和参数(setup)
- 调用dispatch方法进行派发
调用as_view方法可以传递参数,但要注意:
- 不能使用请求方法的名字作为参数的名字
- 只能接受视图类已经存在的属性对应的参数
dispatch
方法是实例方法,它的主要代码:
1 | def dispatch(self, request, *args, **kwargs): |
dispatch主要完成http请求方法的派发,调用视图类对应实例方法处理用户请求,所有用户需要定义和http请求方 法同名的实例方法完成功能,所以一般CBV的模块写法是:
1 | from django.views import View |
TemplateView
TemplateView可以根据上下文渲染指定模板,返回响应对象。它继承了ContentMixin、View、TemplateResponseMix
- ContentMixin用于获取渲染模板的变量。你可以重写get_context_data方法返回模板渲染的参数
- TemplateResponseMixin 用于渲染模板
- template_name模板文名(必须设置)
- template_engine模板引擎(有默认值)
- response_class模板渲染类,默认是TemplateResponse
- content_type内容类型,默认是text/html
- get_template_names你可以重写这个方法返回模板名称
1 | #路由 |
注意as_view方法参数只能是template_name、template_engine、response_class、content_type
RedirectView
重定向的指定url
get_redirect_url用于构造重定向的目标URL,可以重写。默认实现url用作起始字符串,并%使用URL中捕获的命名组在该字符串中执行命名参数的扩展。如果url未设置,则get_redirect_url()尝试反转 pattern_name使用URL中捕获的内容(使用已命名和未命名的组)。如果请求query_string,它还会将查询字符串附加到生成的URL。子类可以实现他们希望的任何行为,只要该方法 返回可重定向的URL字符串即可
通用显示视图
本类视图主要用户数据展示,包括ListView显示对象列表信息和DetailView显示对象详细信息
ListView
MultipleObjectTemplateResponseMixin
- 提供了模板文件名
- 如果没有指定模板文件名,则默认模板文件名规则是:应用名/模型名_list.html
MultipleObjectMixin
核心类,提供了渲染模板所有需要的模型或查询结果集(不一定是QuerySet,可以是对象列表),分页。
queryset属性用于渲染模板所需对象列表,也可以重写get_queryset方法获取
model,如果没指定queryset,则根据指定model获取对象列表
context_object_name模板中对象列表的名称,如果不指定,则根据model获取对象列表名称:model_list
paginate_by指定分页每页的记录个数,默认是None,不分页
page_kwarg指定分页请求路径中命名分组名或get传参中键的名称,默认是page
paginate_orphans是指分页最后一页如果记录不满一页的处理方式,默认是0,和前一页合并显示,如果不为0,则单独显示一页
分页具体实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.pop('object_list', self.object_list)
page_size = self.get_paginate_by(queryset)
context_object_name = self.get_context_object_name(queryset)
if page_size:
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset,
page_size)
context = {
'paginator': paginator, #在模板中可以使用分页器
'page_obj': page, #分页对象
'is_paginated': is_paginated,
'object_list': queryset #当前页数据
}
else:
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset
}
if context_object_name is not None: #如果context_object_name不为空
context[context_object_name] = queryset
context.update(kwargs)
return super(MultipleObjectMixin, self).get_context_data(**context)BaseListView默认实现get请求
DetailView
显示对象的详细信息
- SingleObjectMixin
- pk_url_kwarg 默认值pk,从请求路径中获取主键的值,请求路径中参数必须是命名组,组名必须和 pk_url_kwarg的值一样
- slug_url_kwarg默认值是slug,从请求路径中获取查询参数sug的值,请求路径中参数必须是命名组,组名必须和slug_url_kwarg的值一样,如果参数中有pk_url_kwarg的值,则slug_url_kwarg不起作用
- slug_fifield查询字段的名称
- context_object_name模板中引用对象的名称,默认模板中对象名称是object
通用编辑视图
本类视图主要完成对象的增删改。包括FormView、CreateView、 UpdateView、DeleteView
FormView
显示表单的视图。出错时,重新显示带有验证错误的表单; 成功时,重定向到新的URL。
- FormMixin
- form_class 表单类名
- success_url表单验证成功后调整的url
- form_valid() 验证数据成功后的处理
- form_invalid() 验证不成功的处理
- ProcessFormView
- get渲染表单
- post表单提交
- put创建或修改对象
CreateView
显示用于创建对象的表单的视图,使用验证错误(如果有)重新显示表单并保存对象。
重要属性:
- template_name 模板文件名
- fields指定的字段列表
- model关联模型名
- form_class表单类,如果没有设置会默认是模型名
UpdateView
UpdateView的用法和CreateView基本一样
DeleteView
删除指定对象的视图
类视图使用装饰器
为类视图添加装饰器,可以使用两种方法。
为了理解方便,我们先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被装饰对象),及一个要被装饰的类视图
1 | def my_decorator(func): |
在类视图中使用为函数视图准备的装饰器时,不能直接添加装饰器,需要使用method_decorator将其转换为适用 于类视图方法的装饰器
method_decorator装饰器使用name参数指明被装饰的方法
1 | # 为全部请求方法添加装饰器 |
如果需要为类视图的多个方法添加装饰器,但又不是所有的方法(为所有方法添加装饰器参考上面例子),可以直 接在需要添加装饰器的方法上使用method_decorator,如下所示
1 | from django.utils.decorators import method_decorator |
Django-rest-framework
什么是RESTful
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
RESTful API设计
API与用户的通信协议,总是使用HTTPs协议。
域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
版本
- URL,如:https://api.example.com/v1/
- 请求头跨域时,引发发送多次请求
路径,视网络上任何东西都是资源,均使用名词表示(可复数)
method
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
过滤,通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
状态码
1
2
3
4
5
6
7
8
9
10
11
12200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。错误处理,状态码是4xx时,应返回错误信息,error当做key。
1
2
3{
error: "Invalid API key"
}返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
1
2
3
4
5
6GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
1
2
3
4
5
6{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
基于Django实现
路由系统:
1 | urlpatterns = [ |
CBV视图:
1 | from django.views import View |
基于Django Rest Framework框架实现
Django Rest Framework(DRF)是一个强大且灵活的工具包,用以构建Web API。DRF可以在Django的基础上迅速实现API,并且自身带有WEB的测试页面,可以方便的测试自己的API
安装:
1 | pip install djangorestframework |
url.py
1 | from django.conf.urls import url, include |
views.py
1 | from rest_framework.views import APIView |
Django上线部署
安装
在线上服务器安装虚拟开发环境
安装
nginx
安装
mysql
创建虚拟环境
在虚拟开发环境中安装
django、pymysql、pillow
安装
uwsgi
1
pip install uwsgi
上传项⽬
在项⽬中根⽬录下创建
uconfifig.ini
的⽂件 代码在下⽅1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16[uwsgi]
# 外部访问地址,可以指定多种协议,⽤socket #
socket = 0.0.0.0:8000 # uwsgi的监听端⼝
# 指向项⽬根⽬录
chdir = /var/www/online
# wsgi.py所在位置
wsgi-file = day09/wsgi.py
module = day09.wsgi
# 虚拟开发环境位置
virtualenv = /home/python/.pyenv/versions/env3.6.6
#plugins = python
master = true
# 处理器数
processes = 1
# 线程数
threads = 2更改nginx的default⽂件代码在下方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16server {
listen 80;
server_name www.blog.com;
location / {
# 转发端⼝必须和uconfig.ini中socket端⼝⼀致
uwsgi_pass 127.0.0.1:8000;
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT online.wsgi;
# 项⽬的根⽬录
uwsgi_param UWSGI_CHDIR /var/www/online;
}
# 静态资源所在位置
location /static {
alias /var/www/online/static/;
}
}重启nginx
回到虚拟环境⽬录启动 uwsgi
1
uwsgi uconfig
关闭uwsgi
1
uwsgi —stop uconfig.ini
如果关闭不掉杀死进程
1
2ps -ef | grep uwsgi
Sudo kill 进程号
问题描述:
django admin没有样式 admin管理⻚⾯找不到base.css,dashboard.css⽂件
解决办法:
在settings⽂件中设置STATIC_ROOT⽬录,该⽬录不能在STATICFILES_DIRS中. 然后, 执⾏命令
1 | python manage.py collectstatic |
执⾏后,django会将STATICFILES_DIRS下的所有⽂件以及admin所需要⽤到的js,css,image⽂件全都放到STATIC_ROOT⽬录下.例如, 像下⾯这样写:
1 | STATIC_URL = '/static/' |
简单描述⼀下这几个变量的意思 STATIC_URL: 当访问什么样的⽹址时, 按照访问静态⽂件的⽅式去查找⽂件. STATICFILES_DIRS: 当访问静态⽂件是, 会在每个app中的static⽬录中查找, 然后再从STATICFILES_DIRS设置的路径列表中逐⼀查找. STATIC_ROOT: 当执⾏ python manage.py collectstatic 时, 收集的静态⽂件放在该⽬录下.
此刻项⽬下就会多出⼀个collectstatic的静态资源⽂件⽬录
default代码更改为
1 | server{ |
访问可能会出现403没有权限的问题
解决办法:
找到nginx.conf⽂件的位置,将第⼀⾏的代码进⾏修改
1 | #user www-data; |
此刻就可以访问了
10.0.11.11/static/img/home.img