1. 简述Http协议?
- 超文本传输协议
- 特点:
- 无状态,请求响应之后,再次发起请求时,不认识。
- 短连接,一次请求和一次响应就断开连接。
- 格式:
- GET请求:输入地址回车:https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F
请求由两部分组成:请求头和请求体,请求头和请求体通过\r\n\r\n分割,请求头和请求头之间通过\r\n分割。
"""GET /new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F http1.1\r\nUser-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\r\nHost:jd.com\r\n\r\n"""
响应由两部分组成:响应头和响应体,
b'HTTP/1.1 200 OK\r\nDate: Mon, 05 Nov 2018 01:15:31 GMT\r\nServer: Apache\r\nLast-Modified: Tue, 12 Jan 2010 13:48:00 GMT\r\nETag: "51-47cf7e6ee8400"\r\nAccept-Ranges: bytes\r\nContent-Length: 81\r\nCache-Control: max-age=86400\r\nExpires: Tue, 06 Nov 2018 01:15:31 GMT\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n <html><head> .... </html>'
- POST请求:
请求由两部分组成:请求头和请求头
"""POST /new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F http1.1\r\nUser-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\r\nHost:jd.com\r\n\r\nusername=haoxu666&password=123"""
响应:
b'HTTP/1.1 200 OK\r\nDate: Mon, 05 Nov 2018 01:15:31 GMT\r\nServer: Apache\r\nLast-Modified: Tue, 12 Jan 2010 13:48:00 GMT\r\nETag: "51-47cf7e6ee8400"\r\nAccept-Ranges: bytes\r\nContent-Length: 81\r\nCache-Control: max-age=86400\r\nExpires: Tue, 06 Nov 2018 01:15:31 GMT\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n用户名或密码错误'
2. 你了解的请求头都有什么?
- User-Agent,设备信息。
- Host,当前访问的主机名称。
- referrer,做防盗链。
- Content-Type: ....
3. 你了解的请求方式有哪些?
- GET/POST/PUT/PATCH/DELETE/OPTIONS/HEAD/TRACE
4. django请求的生命周期/浏览器上输入 http://www.oldboyedu.com 地址回车发生了什么?
- 浏览器输入:http://www.oldboyedu.com 回车
- DNS解析,将域名解析成IP。
- 浏览器(socket客户端),根据IP和端口(80)创建连接,发送请求。
- 服务端接收请求
- 实现了wsgi协议的模块,如:wsgiref接收到用户请求。
- 然后将请求转交给django的中间件,执行中间件的process_request(process_view)。
- 路由系统进行路由匹配。
- 匹配成功执行视图函数,视图函数进行业务处理(ORM操作数据+模板渲染)
- 交给中间件的process_response方法
- wsigref的socket.send,将结果返回给浏览器。
- 断开socket连接。
- 浏览器断开连接。
详细:见django请求生命周期图
5. 什么是wsgi?
wsgi,web服务网关接口,他是一套协议。
实现wsgi协议有:
- wsgiref
- uwsgi
实现wsgi协议的所有的模块本质:socket服务端。
6. django中间件的作用?应用场景?
中间件,可以对所有请求进行批量操作。
应用场景:
- 自己玩
- 记录日志
- IP黑名单
- 权限系统中的权限校验
- 解决跨域:编写一个中间件,在中间件中定义一个process_response,添加一个响应头(CORS,跨站资源共享)
- 用户登录
- csrf_token验证(django内置功能)
细节:
- 5个方法:process_request/process_response + 3
- 执行流程
正常流程:
- 所有process_request
- 所有process_view
- 所有process_response
非正常流程:
- django 1.10及以后:平级返回
- django 1.10以前:找到最后的process_response
- 写代码时,如果忘记方法名称或方法参数个数,怎么办?
- 任意导入一个源码查看,如:
# from django.middleware.common import CommonMiddleware
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',
]
- 执行流程是如何实现的?
将中间件的相关方法添加到对应的 5个列表中,以后循环执行(顺序、倒序)
源码:
class BaseHandler(object):
def __init__(self):
self._request_middleware = None
self._view_middleware = None
self._template_response_middleware = None
self._response_middleware = None
self._exception_middleware = None
self._middleware_chain = None
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE (or the deprecated
MIDDLEWARE_CLASSES).
Must be called after the environment is fixed (see __call__ in subclasses).
"""
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
if settings.MIDDLEWARE is None:
warnings.warn(
"Old-style middleware using settings.MIDDLEWARE_CLASSES is "
"deprecated. Update your middleware and use settings.MIDDLEWARE "
"instead.", RemovedInDjango20Warning
)
handler = convert_exception_to_response(self._legacy_get_response)
for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if hasattr(mw_instance, 'process_request'):
self._request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)
else:
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if mw_instance is None:
raise ImproperlyConfigured(
'Middleware factory %s returned None.' % middleware_path
)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)
handler = convert_exception_to_response(mw_instance)
# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._middleware_chain = handler
- 根据字符串的形式导入模块 + 根据反射找到模块中的成员
总结:
特点:
- 所有请求都要通过中间件
- 5个方法
- 5个方法的执行流程
- 正常
- 不正常(版本区别)
应用场景:
- 自己玩:
- IP黑名单限制
- 日志
- 工作场景:
- 权限控制
- 跨域
- 登录
- CSRF
相关知识点:
- 流程实现原理:列表+列表翻转
- 根据字符串的形式导入模块+反射
作业:
1. django程序,用户请求到来后将用户用户的 User-Agent 写入到日志中(logging模块)
要求:
- 通过源码看其中有哪些方法和参数; # from django.middleware.common import CommonMiddleware
- 在request对象中找 User-Agent 请求头对应的值。
- logging模块写入日志。
7. 路由系统
本质:保存url和函数的对应关系。
相关知识点:
示例1:
url(r'^index/', views.index),
def index(request):
return HttpResponse('...')
示例2:
url(r'^user/edit/(\d+)/$', views.user_edit),
def user_edit(request,nid):
return HttpResponse('...')
示例3:
url(r'^crm/', include('app01.urls'))
from django.conf.urls import url,include
from app01 import views
urlpatterns = [
url(r'^order/', views.order),
url(r'^center/', views.center),
]
def order(request):
return HttpResponse('...')
def center(request):
return HttpResponse('...')
示例4:根据name别名反向生成URL
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index,name='index'),
url(r'^user/edit/(\d+)/$', views.user_edit,name='user_edit'),
url(r'^crm/', include('app01.urls')),
]
urlpatterns = [
url(r'^order/', views.order,name='order'),
url(r'^center/', views.center,name='center'),
]
反向生成:
index_url = reverse('index')
user_edit_url = reverse('user_edit',args=('999',))
index_url = reverse('order')
index_url = reverse('center')
示例5:根据 namespace + name 别名反向生成URL
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index,name='index'),
url(r'^user/edit/(\d+)/$', views.user_edit,name='user_edit'),
url(r'^crm/', include('app01.urls',namespace='crm')),
]
urlpatterns = [
url(r'^order/', views.order,name='order'),
url(r'^center/', views.center,name='center'),
]
视图中反向生成:
index_url = reverse('index')
user_edit_url = reverse('user_edit',args=('999',))
index_url = reverse('crm:order')
index_url = reverse('crm:center')
在模板中反向生成:
{% url 'index' %}
{% url 'user_edit' 999 %}
{% url 'crm:order' %}
{% url 'crm:center' %}
补充:公司项目从路由开始看,可能你看到的是这样。
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index,name='index'),
url(r'^user/edit/(\d+)/$', views.user_edit,name='user_edit'),
url(r'^crm/', include('app01.urls',namespace='crm')),
]
urlpatterns += [
url(r'^xxxx/', views.index),
]
8. 什么是MVC、MTV?
MVC, Model View Controller
MTV, Model Template View
9. FBV和CBV
FBV,写函数进行处理业务逻辑。
CBV,写类进行处理业务逻辑。
本质上FBV和CBV都是一样的,因为url对应都是一个函数。
url(r'^order/', views.order,name='order'), # 1. 对应order函数;2.一旦请求到来,立即执行order函数
url(r'^center/', views.CenterView.as_view(),name='center'), # 1. url对应 views.CenterView.as_view()会返回一个view函数;2. 请求到来之后,立即执行view函数,view由会触发dispatch方法、dispatch会根据method不同根据反射执行get/post/delete...的方法。
推荐:
业务逻辑,FBV(简单)。
restful api,CBV。
10. 现象:两个系统之间进行相互数据传输,李超向讲师机发送POST请求,但讲师机的request.POST中没有获取到数据,可能是因为什么?
1. csrf_token
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def api(request):
"""
为李超提供的API接口
:param request:
:return:
"""
print(request.POST)
return HttpResponse('...')
实例代码:
李超.py:
import requests
response = requests.post('http://127.0.0.1:8000/crm/api/',data={'user':'alex','pwd':'dsb'})
print(response.text)
Http请求格式:
"""POST /crm/api/ http\1.1\r\nhost:..\r\nContent-Type:application/x-www-form-urlencoded .....\r\n\r\nuser=alex&pwd=dsb"""
django服务端:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def api(request):
"""
为李超提供的API接口
:param request:
:return:
"""
print(request.POST)
return HttpResponse('...')
2. request.POST解析时,有限制。
李超.py
import requests
response = requests.post('http://127.0.0.1:8000/crm/api/',json={'user':'alex','pwd':'dsb'})
print(response.text)
Http请求格式:
"""POST /crm/api/ http\1.1\r\nhost:..\r\nContent-Type:application/json .....\r\n\r\n{'user':'alex','pwd':'dsb'}"""
django服务端:
@csrf_exempt
def api(request):
"""
为李超提供的API接口
:param request:
:return:
"""
print(request.body) # 原生的请求体格式,有数据;(自己读取body然后进行解析)
print(request.POST) # 将原生的请求体转换成 QueryDict对象,无数据。
return HttpResponse('...')
注意:
request.POST 将原生的请求体转换成 QueryDict对象,请求必须满足两个条件才能转换:
- Content-Type:application/x-www-form-urlencoded
- 数据格式: user=alex&pwd=dbs&xxx=123
如果不满足此条件,django获取请求体时需要自己去request.body中获取值。
总结:
django获取请求体 request.body
request.POST是将请求体的数据转换成了QueryDict对象。
11. 视图函数的返回值
HttpResponse
render
示例1:
视图:
def test(request):
"""
:param request:
:return:
"""
return render(request,'test.html',{'k1':123})
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<div>
<h1>{{ k1 }}</h1>
<script>
alert('{{ k1 }}');
</script>
</div>
</body>
</html>
示例2:
视图:
def test(request):
"""
:param request:
:return:
"""
return render(request,'test.html',{'k1':123})
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<div>
<h1>{{ k1 }}</h1> # k1会被替换
<script src='/static/commons.js'></script>
</div>
</body>
</html>
commons.js
alert('{{k1}}') # k1不会被替换,变量传给视图进行模板渲染,之后又向commons.js发了一次重定向请求,如果js代码写在html页面的代码中不进行重定向,是可以被替换的
redirect
将要重定向的地址通过响应头Location响应头返回给浏览器。
今日内容:
路由分发的另外一个方式:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index,name='index'),
url(r'^user/edit/(\d+)/$', views.user_edit,name='user_edit'),
url(r'^crm/', include('app01.urls',namespace='crm')),
url(r'^crm/', ([
url(r'^c1/', views.index),
url(r'^c2/', ([
url(r'^c3/', views.index,name='n3'), # n1:n2:n3
url(r'^c4/', views.index),
],None,'n2')),
],None,'n1')),
]
reverse('n3') # /crm/c2/n3/
reverse('n1:n3') # /crm/c2/n3/
1. 模板
a. 模板查找顺序
- 去根目录下的templates文件夹中寻找
- 根据app的注册顺序,去每个app的templates文件夹中寻找。
应用:
模板的替换
b. 模板继承
c. include引入小组件
注意:如果模板中存在继承和include,那么模板引擎会将所有的模板拼接到一起后再进行渲染(替换)。
d. 模板获取索引
列表: users.0
字典: users.key
e. 在模板中传递函数,自动加括号执行。
例如:
def func():
return "999"
def index(request):
return render(request,'index.html',{'func':func})
模板:
<h1>函数:{{ func }}</h1>
f. 模板中自定义函数
- simple_tag
@register.simple_tag
def func1(a1,a2,a3):
"""
一般用于给页面返回一个结果
:param a1:
:param a2:
:param a3:
:return:
"""
return a1 + a2 + a3
<h1>调用simple_tag:{% func1 '戴绿' '郝旭' '阿段' %}</h1>
- inclusion_tag
@register.inclusion_tag('func2.html')
def func2(a1,a2):
"""
用于给页面返回一个HTML代码块
:param a1:
:param a2:
:return:
"""
return {'data1':a1, 'data2':a2}
<h1>调用inclusion_tag:{% func2 '戴绿' '郝旭' %}</h1>
- filter
@register.filter
def func3(a1,a2):
"""
可以在if后面做条件,但是参数有限制(最多2个)。
:param a1:
:param a2:
:return:
"""
return a1 + a2
<h1>调用filter:{{ "戴绿"|func3:'郝旭' }}</h1>
{% if "戴绿"|func3:'郝旭' %}
<h1>asdf</h1>
{% endif %}
g. 模板中导入静态文件
{% load staticfiles %}
<img src="{% static '1.png' %}" alt="">
<img src="/static/1.png" alt=""> 禁止使用
赠送:1.10之前的版本模板路径需要
TEMPLATES = (
os.path.join(BASE_DIR,'templates'),
)
2. ORM
orm,关系对应映射。
类 -> 表
对象 -> 行
属性 -> 字段
UserInfo.object model表类,对应数据库中的表 <class 'django.db.models.manager.Manager'>
obj = UserInfo.object.all() 查询出来的queryset对象,里面是数据库对象,对应数据库中的行 <class 'django.db.models.query.QuerySet'>
obj.title
obj.外键字段 #这是外键正向查询
obj.小写表名_set.all() #这是外键反向查询
obj.多对多字段.all() #这是多对多关联对象的正向查询
obj.小写多对多表名_set.all() #这是多对多关联对象的反向查询,因为多对多的本质就是两个外键关系,所以是可以用外键反向查询的方法的
obj.一对一关联字段名 #这是一对一关联的正向查询 obj.小写一对一关联表名 #这是一对一关联的反向查询 推荐博客:https://blog.csdn.net/weixin_40475396/article/details/79539608
操作表:
单表
class UserInfo(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名', max_length=32)
FK
基本操作:
class Department(models.Model):
"""
部门表
"""
title = models.CharField(verbose_name='标题',max_length=32)
class UserInfo(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名', max_length=32)
depart = models.ForeignKey(verbose_name='所属部门',to="Department")
on_delete:
models.CASCADE,删除部门,则将改部门下的员工全部删除。 + 代码判断
models.DO_NOTHING,删除部门,引发错误IntegrityError
models.PROTECT,删除部门,引发错误ProtectedError
models.SET_NULL,删除部门,则将改部门下的员工所属部门ID设置为空。(将FK字段设置为null=True)
models.SET_DEFAULT,删除部门,则将改部门下的员工所属部门ID设置默认值。(将FK字段设置为default=2)
models.SET,删除部门,则将执行set对应的函数,函数的返回值就是要给改部门下员工设置的新的部门ID。
例如:
def func():
models.Users.......
return 10
class MyModel(models.Model):
user = models.ForeignKey(to="User",to_field="id"on_delete=models.SET(func),)
方法:
models.CASCADE, 删除逻辑时,通过代码判断当前 “部门” 下是否有用户。
models.SET_NULL,稳妥。
沟通之后在确定。
db_constraint:
depart = models.ForeignKey(verbose_name='所属部门',to="Department",db_constraint=False) # 无约束,但可以使用django orm的连表查询。
models.UserInfo.objects.filter(depart__title='xxx')
limit_choice_to
示例1:
from django.db import models
class Department(models.Model):
"""
部门表
ID 名称
1 教质部
2 Python学院
"""
title = models.CharField(verbose_name='标题',max_length=32)
class User(models.Model):
"""
员工表
ID name depart_id
1 小雪 1
2 冰冰 1
3 小雨 1
4 太亮 2
5 金菊 2
"""
name = models.CharField(verbose_name='员工名称',max_length=32)
depart = models.ForeignKey(to='Department')
class ClassList(models.Model):
"""
班级表
"""
title = models.CharField(verbose_name='班级名称', max_length=32)
bzr = models.ForeignKey(to=User,limit_choices_to={'id__lt':4})
teacher = models.ForeignKey(to=User,limit_choices_to={'id__gte':4})
示例2:
from django.db import models
class Department(models.Model):
"""
部门表
ID 名称
1 教质部
2 Python学院
"""
title = models.CharField(verbose_name='标题',max_length=32)
class User(models.Model):
"""
员工表
ID name depart_id
1 小雪 1
2 太亮 2
3 小雨 1
4 冰冰 1
5 金菊 2
"""
name = models.CharField(verbose_name='员工名称',max_length=32)
depart = models.ForeignKey(to='Department')
class ClassList(models.Model):
"""
班级表
"""
title = models.CharField(verbose_name='班级名称', max_length=32)
bzr = models.ForeignKey(to=User,limit_choices_to={'depart__title':'教质部','id__gt':9})
teacher = models.ForeignKey(to=User,limit_choices_to={'depart__title':'Python学院'})
related_name
反向查找的字段。
示例:
from django.db import models
class Department(models.Model):
"""
部门表
ID 名称
1 教质部
2 Python学院
"""
title = models.CharField(verbose_name='标题',max_length=32)
class User(models.Model):
"""
员工表
ID name depart_id
1 小雪 1
2 太亮 2
3 小雨 1
4 冰冰 1
5 金菊 2
"""
name = models.CharField(verbose_name='员工名称',max_length=32)
depart = models.ForeignKey(to='Department')
class ClassList(models.Model):
"""
班级表
"""
title = models.CharField(verbose_name='班级名称', max_length=32)
bzr = models.ForeignKey(to=User,related_name='x')
teacher = models.ForeignKey(to=User,related_name='y')
from app01 import models
# 找班主任小雪带的所有班级
obj = models.User.objects.filter(name='小雪').first()
class_list = obj.x.all()
for row in class_list:
print(row.title)
# 找老师金鑫带的所有班级
obj1 = models.User.objects.filter(name='金鑫').first()
class_list = obj1.y.all()
for row in class_list:
print(row.title)
补充:
对于FK,一般公司数据量和访问量不大时,创建FK做约束。
数据量和访问量巨大时,牺牲硬盘空间和程序员代码量,依次来提供用户访问速度。(连表查询速度会比单表查询速度慢)
M2M
自动创建第三张表(场景:关系表只有boy和girl的id):
class Boy(models.Model):
name = models.CharField(max_length=32)
class Girl(models.Model):
name = models.CharField(max_length=32)
boy = models.ManyToManyField('Boy')
手动创建第三张表(场景:除了boy和girl的id以外,还需要其他字段):
class Boy(models.Model):
name = models.CharField(max_length=32)
class Girl(models.Model):
name = models.CharField(max_length=32)
class Boy2Girl(models.Model):
b = models.ForeignKey(to='Boy')
g = models.ForeignKey(to='Girl')
class Meta:
unique_together = (
("b", "g"),
)
O2O
class UserInfo(models.Model):
"""
1 好虚
2 戴绿
"""
username = models.CharField(verbose_name='标题',max_length=32)
class Blog(Model.Model):
"""
1 好虚371 1
"""
title = models.CharField(verbose_name='标题',max_length=32)
a = models.OneToOneField(to='A')
应用场景:
class userinfo:
"""
老男孩所有员工 (130)
"""
name = 用户名
email = 邮箱
...
class Admin:
"""
给30个人开账号(30),可以登录教务系统
"""
username = 登录用户名
password ='密码'
user = o2o(userinfo)
补充:choices的应用场景。
例如:性别的数量不会随着时间的推移而发生个数的变化。
# 不推荐
class Gender(models.Model):
title = models.CharField(max_length=32)
class Customer(models.Model):
name = models.CharField(verbose_name='姓名',max_length=32)
gender = models.ForeignKey(to='Gender')
# 推荐
class Customer(models.Model):
name = models.CharField(verbose_name='姓名',max_length=32)
gender_choices = (
(1,'男'),
(2,'女'),
)
gender = models.IntegerField(choices=gender_choices)
数据库优化手段,将固定数据放入内存代替放入数据库。
操作数据:
增删改查
class Department(models.Model):
title = models.CharField(verbose_name='标题',max_length=32)
class UserInfo(models.Model):
name = models.CharField(verbose_name='员工名称',max_length=32)
depart = models.ForeignKey(to='Department')
roles = models.ManyToManyField(to="Role")
class Role(models.Model):
title = models.CharField(verbose_name='标题',max_length=32)
增加:
models.Department.objects.create(title='销售部')
models.Department.objects.create(**{'title':'销售部'})
models.UserInfo.objects.create(name='刘也',depart=models.Department.objects.get(id=1))
models.UserInfo.objects.create(name='刘也',depart_id=1)
obj = models.UserInfo.objects.filter(name='刘也').first()
obj.roles.add([1,2,3])
删除:
.delete()
修改:
models.UserInfo.objects.filter(id__gt=5).update(name='xx')
obj = models.UserInfo.objects.filter(name='刘也').first()
obj.roles.set([2,3,6,7])
查询:
models.UserInfo.objects.all()
models.UserInfo.objects.values('id','name')
models.UserInfo.objects.values_list('id','name')
常用操作:
- 排序
- 连表
- filter筛选条件
__gt
__gte
__lt
__contains
__in
...
高级操作:
F
Q
only
# Queryset[obj,obj,obj]
modes.UserInfo.objects.all().only('id','name') # select id,name from userinfo
# Queryset[{},{},{}]
modes.UserInfo.objects.all().values('id','name') # select id,name from userinfo
# Queryset[(),(),()]
modes.UserInfo.objects.all().values_list('id','name') # select id,name from userinfo
错错错:
result = modes.UserInfo.objects.all().only('id','name')
for obj in result:
print(obj.id,obj.name,obj.age)
defer
# Queryset[obj,obj,obj]
modes.UserInfo.objects.all().defer('name') # select id,age from userinfo
select_related
帮助开发者进行主动连表查询。
# SELECT "app01_user"."id", "app01_user"."name", "app01_user"."depart_id" FROM "app01_user"
result = models.User.objects.all()
# SELECT "app01_user"."id", "app01_user"."name", "app01_user"."depart_id", "app01_department"."id", "app01_department"."title" FROM "app01_user" INNER JOIN "app01_department" ON ("app01_user"."depart_id" = "app01_department"."id")
result = models.User.objects.all().select_related('depart')
注意:如果以后想要获取部门名称(跨表),一定要使用select_related进行主动跨表,这样在最开始获取数据时,将当前表和关联表的所有数据都获取到。
切记:错错错
result = models.User.objects.all()
for row in result:
print(row.name,row.depart_id,row.depart.title) # row.depart.title就会让性能大大降低
prefetch_related
# 先执行SQL: select * from user where id<100
# 在执行SQL: select * from depart where id in [11,20]
result = models.User.objects.filter(id__lt=100).prefetch_related('depart')
对比:
方式一:
result = models.User.objects.all() # 1次单表
for row in result:
print(row.id,row.name,row.depart.title) # 100次单表
方式二(小于4张表的连表操作): ***
result = models.User.objects.all().select_related('depart') # 1次连表查询
for row in result:
print(row.id,row.name,row.depart.title)
方式三(大于4张表连表操作):
# 先执行SQL: select * from user;
# 在执行SQL: select * from depart where id in [11,20]
result = models.User.objects.all().prefetch_related('depart') # 2次单表查询
for row in result:
print(row.id,row.name,row.depart.title)
执行原生SQL,场景:复杂SQL语句
from django.db import connection, connections
# cursor = connections['db1'].cursor()
cursor = connection.cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1,])
# row = cursor.fetchall() # 获取符合条件的所有数据,models.User.objects.all()
row = cursor.fetchone() # 获取符合条件的第一条数据,models.User.objects.all().first()
所有ORM操作:
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################
def all(self)
# 获取所有的数据对象
def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q
def exclude(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q
def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')
def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段')
from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
))
)
students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))
def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询
from django.db.models import Count, Avg, Max, Min, Sum
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo
注:只有在PostgreSQL中才能使用distinct进行去重
def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age')
def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询
UserInfo.objects.extra(where=['headline ? %s'], params=['Lennon'])
# select * from userinfo where headline > 'Lennon'
UserInfo.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# select * from userinfo where (foo='a' OR bar = 'a') and baz = 'a'
UserInfo.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
"""
select
id,
name,
(select col from sometable where othercol > 1) as new_id
"""
UserInfo.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序
def defer(self, *fields):
models.UserInfo.objects.defer('username','id')
或
models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据
def only(self, *fields):
#仅取某个表中的数据
models.UserInfo.objects.only('username','id')
或
models.UserInfo.objects.filter(...).only('username','id')
def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置)
models.UserInfo.objects.filter(id=5).using('db1')
##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################
def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo where id > 10 ')
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')
# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")
################### 原生SQL ###################
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)
def values(self, *fields):
# 获取每行数据为字典格式
def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖
def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日
models.DatePlus.objects.dates('ctime','day','DESC')
def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
"""
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
"""
def none(self):
# 空QuerySet对象
####################################
# METHODS THAT DO DATABASE QUERIES #
####################################
def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4}
def count(self):
# 获取个数
def get(self, *args, **kwargs):
# 获取单个对象
def create(self, **kwargs):
# 创建对象
def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)
def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
def first(self):
# 获取第一个
def last(self):
# 获取最后一个
def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list)
models.User.objects.filter(id__in=[11,21,31])
def delete(self):
# 删除
def update(self, **kwargs):
# 更新
def exists(self):
# 是否有结果
pass
3. Form、ModelForm、ModelFormSet (WTForms)
需求:
用户输入信息,获取用户输入的信息。
示例:超市进销存管理系统
Form & ModelForm ,本质帮助开发者对用户请求中的数据进行格式校验。
ModelFormSet ,本质帮助开发者对用户请求中的数据进行批量格式校验。
示例代码见:源码示例
赠送:单选下拉框变成radio框。
# 批量操作数据库
for course_obj in course_obj_list:
# 给当前课程 生成学生的学习记录
stu_list = course_obj.re_class.customer_set.all().filter(status='studying')
print(stu_list)
studury_record_list = []
for stu_obj in stu_list:
# models.StudyRecord.objects.create(student=stu_obj,course_record=course_obj) #普通的一个个添加
studury_record_list.append(models.StudyRecord(student=stu_obj, course_record=course_obj))
# 批量操作,一块创建
models.StudyRecord.objects.bulk_create(studury_record_list, batch_size=3)
if request.method == 'POST' and post_type == 'add':
add_formset = AddFormSet(request.POST)
if add_formset.is_valid():
permission_obj_list = [models.Permission(**i) for i in add_formset.cleaned_data]
query_list = models.Permission.objects.bulk_create(permission_obj_list)
# 创建成功后,在权限集合中加入新添加权限的别名
add_formset = AddFormSet()
for i in query_list:
permissions_name_set.add(i.name)