django笔记
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)