十二周总结
django请求生命周期流程图
Django路由层
1.路由匹配
django1.X版本路由的第一个参数是正则表达式
django2.X及以上版本的则是path写什么就匹配什么
# 无论什么版本的django都自带加斜杠后缀的功能,我们可以把这个功能手动取消
取消自动加斜杠后缀功能
配置文件中加 APPEND_SLASH = False # 默认为True,且不在配置文件中显示
2.转换器
五种转换器
str:匹配除路径分隔符外所有的非空字符串
int:匹配0或者任意正整数
slug:匹配任意一个由字母或数字组成的字符串
uuid:匹配格式化之后的UUID
path:匹配完整的URL路径
# 这五种转换器中常用的就是str和int,除了这五种转化器我们还可以自定义转换器(就是自己写一个正则表达式)
eg:
path('index/<str:info>/', views.index_func),
# index_func(实参request对象,info='经过转换器转换过的匹配内容') info就相当于关键字传参
3.正则匹配
django2.X级以上版本有re_path,第一个参数是正则表达式
正则匹配的本质是只要第一个正则表达式能够从用户输入的路由中匹配到对应的数据就匹配成功(无论用户输入了什么),
会立刻停止匹配直接执行对应的视图函数
eg:
re_path('^test/$', views.test) # 只匹配路由为test/的路由
# django1.X路由匹配使用的是url(),功能与django2.X及以上的re_path()一致
4.正则匹配的无名有名分组
无名分组
re_path('^test/(\d{4})/', views.test) # 将括号内正则匹配到的内容当做位置参数传递给视图函数
有名分组
re_path('^test/(?P<year>\d{4})/', views.test) # 对括号内的正则表达式起一个别名
# 将括号内的正则匹配到的内容与别名组成关键字参数传递给视图函数,year='正则匹配到的内容'
注意: 无名分组与有名分组不能混合使用
反向解析
通过一个名字可以反向解析出一个结果,该结果可以访问到某个对应的路由
基本使用
1.路由匹配关系起别名
path('login_01', views.login, name='login_view')
2.反向解析语法
html页面模板语法 {% url 'login_view' %}
后端语法 reverse('login_view')
动态路由的反向解析
path('func1/<str:others>/', views.func1_func, name='func1_view')
html页面上模板语法 {% url 'func1_view' 'jason' %} # 需要一个参数与接收到的路由的第二个参数对应
后端语法 reverse('func1_view', args=('嘿嘿嘿',))
路由分发
django支持每个应用都可以有自己的路由层、静态文件、模板层,基于这个特性我们可以实现多人开发,每个人负责一个应用,之后通过路由分发整合到一起
简单来说路由分发就是 不同应用中有相同的路由可以通过路由分发来避免冲突
应用内的路由层我们可以称之为干路由,它是负责与视图函数的匹配
path('index/', views.index)
dajngo项目的路由层称之为总路由,负责分发路由,按照应用名分配匹配方向
path('app01/', include('app01.urls'))
名称空间
路由分发之后默认情况下相同的别名是不能直接反向解析识别出应用前缀的
如果想要能够识别有两种方式
方式一:名称空间 # 在总路由中添加不同的名称空间
干路由app01和app02中都有
path('index/', views.index, name='indes_view')
总路由
path('app01/', include('app01.urls', 'app01'), namespace='app01'),
path('app02/', include('app02.urls', 'app02'), namespace='app02')
反向解析
reverse('app01:index_view')
reverse('app02:index_view')
方式二:起不同的别名 # 可以使用应用名作为前缀
path('index/', views.index, name='app01_indes_view')
path('index/', views.index, name='app02_indes_view')
虚拟环境
在实际开发项目中我们只会给项目配备需要的环境,不需要的一律不配
正常情况下解释器只有一个,没办法满足实际需要,这个时候就有了虚拟环境,就是创建一个解释器的分身,该分身可以有自己独立的环境,这样我们就可以给不同的项目配备不同的环境
pycharm中可直接创建虚拟环境
在pycharm中为虚拟环境下载模块可能需要执行一下下面这个命令
pip install --index-url http://mirrors.aliyun.com/pypi/simple/ django==1.11.11 --trusted-host mirrors.aliyun.com
命令行创建虚拟环境:
python -m venv pyvenv38 # 这里不支持多版本解释器共存的操作,以环境变量中的先后顺序为准,谁在上面就是谁,最后一个pyvenv38是我们自己起的虚拟环境的名字
激活
activate # 进到虚拟环境目录下再执行
关闭
deacticate
Django视图层
三板斧
用来处理请求的视图函数都必须返回HttpResponse对象
1. return HttpResponse() # HttpResponse本身是一个类,类加括号产生对象
2. return render()
render的源码是一个函数,返回值是HttpResponse(content, content_type, status),产生的也是HttpResponse对象
3. return redirect()
redirect的源码是一个函数,里面写了一个三元表达式
redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
产生的结果都是HttpResponse,那么其返回值也是HttpResponse对象
JsonResponse对象
正常情况下我们要给浏览器返回一个json格式的字符串需要先把字符串转为json格式然后再发送给浏览器
django中有一个JsonResPonse对象它可以直接完成上面的操作
使用JsonResPonse的注意事项
1.使用JsonResponse传汉字,需要加一个json_dumps_params = {'ensure_ascii': False}
其它的一些可能需要使用到的参数如果没有默认的也可以使用json_dumps_params
2.针对非字典的其它可以被序列化的数据我们需要修改safe参数为False
request对象获取文件
form表单携带文件需要做到以下几点
1.method必须是post
2.enctype必须是multipart/form-data
# 在form表单中修改enctype参数为multipart/form-data,默认是application/x-www-form-urlencoded
django后端需要通过request.FILES来获取文件类型的数据,获取到的是一个文件对象
接收文件和GET、POST一样用get
file_obj = request.FILES.get('file')
file_obj.name # 获取文件名
# 文件对象支持for循环一行行读取文件内容
FBV与CBV
FBV 基于函数的视图
def index(request): return HttpResponse对象
CBV 基于类的视图
from diango import views
class MyLoginView(views.View):
def get(self, request):
return HttpResponse('from CBV get function')
def post(self, request):
return HttpResponse('from CBV post function')
path('login/', views.MyLoginView.as_view())
使用CBV会根据请求方法的不同自动匹配对应的方法并执行
Django模板层
模板语法传值
1.精准传值
return render(request, '01.html', {'a': name, 'b': age})
这种传值方式不浪费资源但是针对名字比较多的情况下比较麻烦
2.统一传值
return render(request, '01.html', locals())
可以将当前函数名称空间中的所有名字都传过去,但是在名字过多又不使用的情况下就比较浪费资源
补充:
1.{{}}:主要与数据值相关
2.{%%}:主要与逻辑相关
3.针对需要加括号调用的名字 django模板语法会自动加括号调用你只需要写名字就行
4.模板语法的注释前端浏览器是无法查看的 # {##}
模板语法传质特性
1.基本数据类型正常展示
2.文件对象也可以展示并调用方法
3.函数名会自动加括号执行并将返回值展示到页面上(不支持传参,只能无参函数)
4.类名也可以自动加括号调用,对象不会(默认情况下对象不能加括号调用)
针对可以自动加括号调用的模板语法都会自动加括号调用
模板语法之过滤器
1.default 默认值
{{ value|default:"nothing"}} # 如果value没有传值或值为空则显示nothing
2. length 返回值的长度,用于字符串和列表
3.filesizeformat 将数字格式化为文件大小
4.slice 切片
5.date 格式化时间
{{ value|date:"Y-m-d H:i:s"}}
6.safe
value = "<a href='#'>点我</a>"
{{ value|safe}} # 默认情况下不识别html标签和js语法,加上safe后才能识别
更多内置函数参考https://www.cnblogs.com/Dominic-Ji/articles/10982302.html
模板层标签
1.if条件语句
{% if 条件1 %}
<p>条件1成立执行</p>
{% elif 条件2 %}
<p>条件2成立执行</p>
{% else %}
<p>条件都不成立执行</p>
{% endif %} # 模板语法中if的结束语句
2.for循环
b = [1, 2, 3, 4, 5]
{% for i in b %}
<p>{{ i }}</p>
{% endfor %}
我们可以将p标签中的i替换为forloop来查看具体的执行步骤
3.取值操作
django模板语法中的取值操作只用句点符来操作,无论是按键取值还是索引取值都是用句点符操作的
a = {'name': 'jason', 'age': 18}
b = [1, 2, 3, 4, 5]
{{a.name}} # 取键name的值
{{b.2}} # 取索引为2的数据
复杂数据获取之后需要反复使用的可以做一个取别名的操作
{% with a.name as f %}
# 取别名的操作只能在当前标签下使用
自定义过滤器、标签、inclusion_tag
如果想要自定义模板语法需要先完成下面三步操作
1.在应用下创建一个名字为templatetags的目录
2.在templatetags目录下创建任意名称的py文件
3.在创建的py文件中先编写两行固定代码
from django import template
register = template.Library()
这三部操作完成以后就可以在该文件下编写自定义模板语法了
1.自定义过滤器(最大只能接收两个参数)
@register.filter(name='myadd')
def func1(a, b):
return a + b
html页面使用时需要先加载自定义过滤器
{% load 自定义的py文件名%}
过滤器加载完以后就可以使用了
{% i|myadd:1 %} # i为传的值
2.自定义标签(参数没有限制)
@register.simple_tag(name='mytag')
def func2(a, b, c, d):
return f'{a}-{b}-{c}-{d}'
{% load 自定义的py文件名 %}
{% mytag '1' '2' '3' '4' %} # 传值后会按照自定义的样式排列
3.自定义inclusion_tag(局部的html代码)
@register.inclusion_tag('label.html', name='mymenu')
def func3(n):
html = []
for i in range(n):
html.addend('<li>第%s页</li>'%i)
return locals()
{% load 自定义的py文件名 %}
{% mymenu 10 %} # 会在浏览器页面上显示从0到9的页码数
模板的继承与导入
1.模板的继承
当我们有多个页面有很多相似或相同地方时,可以复制粘贴,也可以使用模板继承
模板继承使用方法
1.在模板中使用block划定子版以后可以修改的区域
{% block 区域名称 %}
{% endblock %}
2.子版继承模板
{% extends '模板文件' %}
{% block 区域名称 %}
子版自己的东西
{% endblock %}
子版也可以继续使用模板的内容
{{ block.super }}
模板中至少应该有三个区域
页面内容区、css样式区、js代码区
2.模板的导入
我们将某个html页面的部分提前写好,之后其它html页面都可以使用
{% include '提前写好的部分的文件名(带后缀)' %}
Django模型层
1.django自带的sqlite3数据库对时间字段不够敏感,而且功能也少,所以我们习惯切换成常见的数据库
2.对于django,ORM不会自定帮我们创建库,所以需要我们提前准备好库
3.单独测试Django的某个功能层
Django默认是不允许单独测试某个py文件的,如果我们想要测试可以使用下面的两种方法
1.pycharm提供的python console 中写代码测试,但是这个是模拟终端的,写的代码不会保存,建议一些简单的功 能可以在这里测试
2.自己搭建一个测试环境,可以使用自带的test.py文件或者是自己重新创建一个
1.拷贝manage.py前四行代码
2.自己再加两行代码
import django
django.setup()
4.dajngo ORM的底层还是SQL语句,可以查看
1.如果我们拿到的是一个Queryset对象,那么我们可以直接通过点query的方式查看SQL语句
2.如果我们想查看所有ORM底层的SQL语句,可以在配置文件中添加日志记录,之后会将SQL语句打印到pycharm终端上
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
ORM常用关键字
1.create() 创建数据并获取当前创建的数据对象
create是有返回值的,返回的是用户对象
2.filter() 根据条件筛选数据,结果是Queryset [数据对象, 数据对象]
filter在括号内什么都不填的情况下默认查询所有
filter括号内支持多个条件,但是这些条件之间默认都是and关系
3.update() 更新数据(批量更新)
与filter搭配使用,filter查询出来几条数据,update就更改几条数据
4.delete() 删除数据(批量删除),用法与update差不多
5.first() 取第一个数据
Queryset支持索引取值,但是只支持正整数,而且ORM默认不建议使用索引取值
使用索引取值时,值不存在会报错,first不会报错,返回的是None
6.last() 取最后一个数据
7.all() 查询所有数据,结果是Queryset [数据对象,数据对象]
8.values() 根据指定字段获取数据,结果是Queryset [{},{}],列表套字典
9.values_list() 根据指定字段获取数据,结果是Queryset [(),()] 列表套元组
10.distinct() 去重,数据必须完全一致,如果有主键则用不了
11.order_by() 根据指定条件排序,默认是升序,字段前面加负号则是降序
12.get() 根据条件筛选数据,并直接获取数据对象,如果数据不存在则直接报错
13.exclude() 取反
14.reverse() 颠倒顺序,被操作的对象必须是已经做过排序的
15.count() 统计结果集中数据的个数
16.exists() 判断结果集中是否含有数据,如果有则返回True,否则返回False
# 在编程中无论关键字或方法有多好用,只要是容易报错的都尽量少用
ORM执行SQL语句
方式一:raw()方法执行原生SQL语句
models.User.objects.raw('SQL语句') # 使用raw在ORM中直接编写SQL语句,其返回值为Queryset对象
方式二:直接执行SQL语句
from django.db import connection
cursor = connection.cursor()
cursor.execute('SQL语句')
print(cursor.fetchall())
单表查询之双下划线查询
1.查询年龄大于18的用户数据
models.User_info.objects.filter(age__gt=18) # ORM中不支持比较运算符的操作,不能直接写比较运算符
2.查询年龄小于18的用户数据
models.User_info.objects.filter(age__lt=18)
3.大于小于/小于等于
models.User_info.objects.filter(age__gte=18) # 大于等于
models.User_info.objects.filter(age__lte=18) # 小于等于
4.查询年龄是18或者是28或者是38的数据
models.User_info.objects.filter(age__in(18, 28, 38))
5.查询年龄在18到38之内的数据
models.User_info.objects.filter(age__range=(18, 38)) # 包括18和38
6.查询名字中含有字母j的数据
models.User_info.objects.filter(name__contains='j') # 区分大小写,查询不包含大写的字母J
models.User_info.objects.filter(name__icontains='j') # 不区分大小写
7.查询注册年份是2022的数据
models.User_info.objects.filter(register_time__year=2022) # 时间的查询使用register_time,后面双下跟的是我们要查询的时间,如年、月、日等
ORM外键字段的建立
1.创建基础表(书籍表,出版社表,作者表,作者详情表)
2.确定外键关系
一对多: ORM与SQL一样外键字段健在多的一方
多对多:
1.ORM外键字段可以直接建在查询频率较高的表中,内部会自动帮我们创建第三张关系表
2.SQL则是我们自己创建第三张关系表并创建外键字段
一对一: ORM与SQL一致,外键字段建在查询频率较高的一方
3.ORM创建
书籍表与出版社表
一个书籍只能被一个出版社出版,一个出版社可以从出版多本书籍,为一对多关系,外键字段建在书籍表中
press = models.ForeignKey(to='Press', on_delete=models.CASCADE)
书籍表与作者表
一本书籍可以有多个作者,一个作者可以写多本书,为多对多关系,书籍表使用频率较高,外键字段建在书籍表中
authors = models.ManyToManyField(to='Author')
作者表与作者详情表
一名作者只能有一个详情,一个详情只能对应一名作者,为一对一关系,外键字段建在作者表中
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
外键字段的相关操作
1.普通表录入数据
# 建议录数据时先从没有外键字段的表开始
2.针对一对多
1.插入数据可以填写表中实际的字段
models.Book.objects.create(name='雪中悍刀行', price=68, press_id=4)
2.也可以填写表里面的类字段名
press_obj = models.Press.objects.filter(pk=4).first()
models.Book.objects.create(name='完美世界', price=66, press=press_obj)
3.针对多对多
1.直接绑定
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.add(1) # 在第三张关系表中给当前书籍绑定作者
2.绑定对象
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj1, author_obj2) # 也可以添加作者对象
3.修改关系
book_obj.authors.set((1,))
# set内使用列表也行,内部填写的表示当前绑定的作者,同样也可以使用作者对象
4.删除关系
book_obj.authors.remove(2, 3) # 在当前书籍绑定的作者中删除2,3 同样也可以使用作者对象
5.清空关系
book_obj.authors.clear() # 清空当前书籍绑定的所有作者
跨表查询
正反向查询的概念
正向查询
从有外键字段的表查询关联表的数据
反向查询
从没有外键字段的表查询关联表的数据
核心就是看外键字段
口诀
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询
1.查询主键为2的书籍对应的出版社名称
先根据条件获取对象
book_obj = models.Book.objects.filter(pk=2).first()
再判断正反向的概念
print(book_obj.press.name)
2.查询主键为2的书籍对应的作者姓名
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.authors) # Myapp.Author.None
print(book_obj.authors.all()) # 作者可能有多个所以需要加all
3.查询辰东的电话
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.author_detail.phone)
4.查询人民出版社出版过的书籍
press_obj = models.Press.objects.filter(name='人民出版社').first()
print(press_obj.book_set.all()) # 查询结果可能存在多个所以加all
5.查询辰东写过的书籍
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.book_set.all()) # 查询结果可能存在多个所以加all
6.查询电话是15855010170的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=15855010170).first()
print(author_detail_obj.author.name)
总结:
查询的结果可能为多个时加all()
基于双下划线的跨表查询
1.查询主键为2的书籍对应的出版社名称
res = models.Book.objects.filter(pk=2).values('press__name')
# values中直接写press表示进入到出版社表中,然后再使用双下name即可得到对应的出版社名称
print(res)
2.查询主键为2的书籍对应的作者姓名
res = models.Book.objects.filter(pk=2).values('authors__name')
print(res)
3.查询辰东的电话
res = models.Author.objects.filter(name='辰东').values('author_detail__phone')
print(res)
4.查询人民出版社出版过的书籍
res = models.Press.objects.filter(name='人民出版社').values('book__name')
print(res)
5.查询辰东写过的书籍
res = models.Author.objects.filter(name='辰东').values('book__name')
print(res)
6.查询电话是15855010170的作者姓名
res = models.AuthorDetail.objects.filter(phone=15855010170).values('author__name')
print(res)
进阶操作
1.查询主键为2的书籍对应的出版社名称
res = models.Press.objects.filter(book__pk=2).values('name')
# values之前的意思是查询出版了书籍主键为2的出版社
print(res)
2.查询主键为2的书籍对应的作者姓名
res = models.Author.objects.filter(book__pk=2).values('name')
print(res)
3.查询辰东的电话
res = models.AuthorDetail.objects.filter(author__name='辰东').values('phone')
print(res)
4.查询人民出版社出版过的书籍
res = models.Book.objects.filter(press__name='人民出版社').values('name')
print(res)
5.查询辰东写过的书籍
res = models.Book.objects.filter(author__name='辰东').values('name')
print(res)
6.查询电话是15855010170的作者姓名
res = models.Author.objects.filter(author_detail__phone=15855010170).values('name')
print(res)
7.查询主键为2的书籍对应的作者的电话
写法一:
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
print(res)
写法二:
res = models.AuthorDetail.objects.filter(author__book__pk=2).values('phone')
print(res)
写法三:
res = models.Author.objects.filter(book__pk=2).values('author_detail__phone')
print(res)
聚合查询
ORM中支持单独使用聚合函数,但是不能直接使用,需要用aggregate模块
我们在使用聚合函数之前需要先导入聚合函数
from django.db.models import Max, Min, Sum, Avg, Count
res = models.表名.onjects.aggregate(别名=Max('字段名'), 别名=Min('字段名'), 别名=Sum('字段名'), 别名=Avg('字段名'), 别名=Count('字段名'))
聚合查询也支持使用别名
分组查询
分组查询的关键字为annotate,默认是以表为分组单位
以之前导入的图书相关的四个表为例
1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk'))
# 以图书表为分组,统计每本书的作者数
print(res)
2.统计出每个出版社卖的最便宜的书的价格
res = models.Press.objects.annotate(min_price=Min('book__price')).values('min_price')
print(res)
3.统计不止一个作者的图书
res = models.Book.objects.annotate(book_authors=Count('authors__pk')).filter(book_authors__gt=1).values('name')
print(res)
4.查询每个作者出的书的总价格
res = models.Author.objects.annotate(all_price=Sum('book__price')).values('name', 'all_price')
print(res)
注意:
在执行ORM分组时可能会报错,并且有关键字sql_mode strict mode
需要移除sql_mode中的only_full_group_by
操作步骤
1.以管理员模式登录Mysql,进入到当前使用的库
2.执行set global sql_mode='strict_trans_tables';
F与Q查询
F查询为当前查询条件不明确,也需要从数据库中获取时使用的
Q查询为执行条件之间的关系为与或非时使用的
from django.db.models import F, Q
F查询
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(repertory__gt=F('sale'))
print(res)
2.将所有书的价格涨800
models.Book.objects.update(price=F('price') + 800) # update只能接收一个传值
3.将所有书的名称后面追加爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.filter(pk=10).update(name=Concat(F('name'), Value('(爆款)'))) # ORM中不能直接做字符串的拼接,需要使用Concat
Q查询
查询主键是2或者价格大于900的书籍
res = models.Book.objects.filter(Q(pk=2) | Q(price__gt=900))
for i in res:
print(i.name)
标签:总结,name,十二周,models,res,查询,filter,objects
From: https://www.cnblogs.com/zyg111/p/16990622.html