Django 进阶
本节内容:
1、Cookie
2、Session
3、Django From
4、Django Model
5、Django ModelFrom
6、中间件
7、CSRF
8、缓存
9、信号
一、Cookie
什么是Cookie:保存在客户端浏览器的,一些键值对,设置是在;服务器端设置的Cookie。它是在客户端浏览器上,硬盘的某个位置,这个Cookie,客户端每次发来请求都携带着。
为什么要有呢?(因为http是无状态的,链接上一下就又断开了,断开以后就没有状态了,如果没有cookie,一个人登录成功页面,其他人不要登录,直接输入网址就可以进去了,还有一个就是,每个人登录看自己的信息,如果没有一个特殊的东西,做用户认证做不了,我不知道是谁登录了。)
获取Cookie
request.COOKIES['key']

设置Cookie
rep=redicect("/index.html") //登录成功后返回的页面
rep.set_cookie(key,value,...) //生成字符串下次来记得
return rep

返回的内容里面要包点Cookie,我们就可以先换成 ,先创建个变量,然后接受一下那个返回值得内容,这个对象具有set_cookie方法,然后设置上之后 然后再return rep,这样就携带的Cookie就过去了。这个就保存咋客户端了 还可以通过参数的形式来规定,建值对的生命周期
参数:
key, 键
value='', 值
max_age=None, 超时时间
expires=None, 也是超时时间,可以设置取到现在时间,在加当前时间在加多久 用变量接收,然后变量写在=后面
path='/', Cookie生效的路径,默认/ 是在全局生效, 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问,如果你设置上路径,就表示这个cookie只有在你,你设置的那个URL才能读取的到
domain=None, Cookie生效的域名,就是设置cookie时候,这cookie是那个域名下的
secure=False, https传输 有的请求是需要证书的,如果你是基于https访问的,加s一般就是加证书访问,你就得设置成true 就OK了
你如果以证书形式访问,设置cookie和获取cookie 等等操作的时候,把他设置成true(加密的)
httponly=False 是来做安全用的,但是不能做到绝对的安全,只能做相对的安全,只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)抓包就网络请求,可以把你的那个值获取到,抓到以后也可以把你的值修改了。所有就公共网络转账 有危险的
cookie :
1、服务端可以操作cookie
2、客户端也可以操作cookie
3、cookie保存在客户端的浏览器上
cookie特性:我可以设置在他电脑上,他下次来的时候,还会带着这个来,由于他有这种特性,我可以基于cookie做一个用户登录验证
cookie应用:
登录验证,当登录完了之后,就把cookie写在客户端了,他下次来的时候,还会带着这个来,就不用每次都去数据库里取数据了 ,但是如果里面有敏感信息,不适合直接放在cookie里面直接搞过来,如果非得有cookie做,就把用户名放那,每次来的时候去数据库里取一下代价就是频繁的操作数据库
有加密的cookie 设置时 rep.set_signed_cookie(key,value,) 获取时 .get_signed_cookie
但是加密可能被破解
所以cookie做认证时,将不敏感的信息放在cookie中,频繁的取操作数据库
cookie的整个流程:
首先服务器端,假设最开始运行起来了,我们浏览器跟服务器端,还没有任何交互,接下来我要点个URL,发起一次http请求,当我发起http请求的时候,他会给我发个东西过去,发个请求头,发一个请求的内容过去,服务器端拿到请求头,拿到内容,可以分析请求头,分析内容这是第一次。分析完了,我要给你返回一个cookie,要给他写cookie了,我给他设置一个响应头。因为响应的内容就包含了,响应头和响应内容,把cookie放在响应里面,就响应头,内容 还有cookie,发给客户端,客户端拿到之后,把cookie放在某个位置就行了,响应头放在这个位置,内容凡在那个位置,这次请求响应就完成了【其实cookie是放在响应头里面的给发过过来的】所以服务端给响应过来的时候就两部分,一部分是响应头其中包含了cookie,还有响应内容,发到浏览器上之后,浏览器取解析他的响应头,把他响应头里的cookie找到,写在自己的文件里面,然后响应的内容显示出来就行了,那接下来发第三次请求了,第三次请求,本地已近有cookie了,就下来就应该把cookie拿过来,发给服务端,那你在去发的时候 ,还是发两部分的内容,他会把cookie封装在请求头里面,以及在加上请求内容,发送给服务器端,服务器端会去他的请求头里面解析他的cookie,如果再想设置,还是在响应头里面设置
还有另外一种用户认证:
在用户那里,不会给他任何数据,只会给他一个随机字符串,这个字符串没有任何含义,给了你以后,你就带着随机字符串来,把敏感信息放在我的服务器端,内存里面,或我的缓存里面,放在我的文件里面都可以,这样,就保证用户永远看不到敏感信息。他只有一个什么都不是的字符串(随机字符串)这个叫Session。session会基于cookie来做
二、Session
session 是服务器端的一个键值对
session 默认内部机制依赖于cookie
def index(request): # 获取、设置、删除Session中数据 request.session['k1'] 获取值通过索引,如果没有k1就报错 通过get设置默认值 也能通过索引直接获取,通过get直接设置 request.session.get('k1',None) request.session['k1'] = 123这是设置,直接设置上了你不管k1存不存在都会设置, request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1']删除某一个 # 所有字典的 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() ite就是迭代器不一次性全拿到,只有for循环迭代的时候一个一个的 拿 request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
简单介绍

代码

用了session,cookie就不用自己操作了。
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
总结:登录页面了,cookie会做什么,session会做什么?
单讲cookie来说,他和session本质上,cookie这个东西和session没有关系。但是session和cookie有关系,所以说cookie是单独存在的,他就是放在咱们这个客户端,浏览器上的一些键值队。
对服务器的session来说,他要依赖于cookie来实现,而还有一点咱们知道,cookie发送到服务端的时候就是依附在请求头里面,回来的时候依附在响应头里面,接下来咱们说整个流程是怎样的。
我们去服务器端给他发消息,服务器端生成一个随机字符串,然后把这个随机字符串,写到我们的额客户端,并且同时,在他的服务器端,再给我搞一个字典,或者是搞一个什么东西,把这个随机字符串当做key,后面当做vilue,这么一个键值对,这么放就行了,每一个人会生成一个键值对,都是以随机字符串作为key的。
三、Django From
Django From是用来帮咱们在后台,做用户请求数据验证的,不管用户传过来的是from请求还是ajax请求,我们在后台都得做验证,并且验证后的错误信息,要传到前端让页面看到。他是验证用户请求数据的合法性的一个组件,如果没有他,我们的验证得通过反复的正则表达式来验证。
以后要想对用户提交过来的数据,进行表单验证,分以下几步:
1、创建模板
模板是用来,当用户发来数据之后,我模板里面有几个字段(元素)我就验证几个。
创建模板有三个关键字: 一个类(自己命名) 一个字段(字段名是你前端input框的name的名字) 一个插件(这是jango内部定义的,)
字段是用来验证,用户请求数据的,只验证(某个字段) 插件就是:写什么插件页面出什么效果
创建模板先引入forms
form django import forms
class 自己命名(forms.Form): #继承
字段名 = forms.CharFied(里面可以写,是否必填,长短指定等,插件也是在这里面) #CharFied就是类型,有很多种类型,有邮箱格式的 ,密码格式的等
你得保证你的字段名和前端提交过来input框的name名字一样,要不就验证不到了 出错了
模板创建完之后
2、接收用户请求,然后传给模板(创建个对象)
现在还没有检测
3、检测

如果想要自己设定错误信息,得在字段后面添加参数

总结一下:
1、创建模板
2、接收用户请求,然后传给模板(创建对象)
3、调用is_valid检测是否成功,(这个方法就是他在内部,把你设置的那两个字段,两条验证规则全部执行一遍,就是个for循环,在执行过程中,全部成功了,返回一个true,有一个失败就是false)
调用clean_data,获取正确的值(返回的是字典)
调用errors来获取所有的错误信息

获取错误信息
errors方法直接写在前端HTML
后端打印出来的

前端获取,如果是别的框架,就会报错,因为get请求后面没传值,就去取信息了,jango不报错是,jango取不到就认为是空jango帮你做的,其他的框架不传就报错了

就说form提交数据时候,具有刷新页面的特性,再显示时,值和错误信息要想都有,就在get请求里也创建对象,让他自动生成HTML标签,保留上一次提交数据

HTML

插件:就是生成什么标签,默认是Text input标签,可以改成密码,邮箱 等

视图函数

HTML

页面

还有一种是,统一的操作._as



创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
1 Field(父级) 2 required=True, 是否必填 3 initial=None, 初始值,默认值 4 validators=[], 自定义验证规则 5 error_messages=None, 错误信息 后面跟字典{'required': '不能为空', 'invalid': '格式错误'} 6 7 8 9 10 11 widget=None, HTML插件 12 label=None, 用于生成Label标签或显示内容,通过前端点就可以获取,后台定义前端使用 13 show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 14 localize=False, 是否支持本地化 15 disabled=False, 是否可以编辑,改成true就不能输入了 16 label_suffix=None Label内容后缀 17 18 19 20 21 22 23 24 25 CharField(Field) 26 max_length=None, 最大长度 27 min_length=None, 最小长度 28 strip=True 是否移除用户输入空白 29 30 IntegerField(Field) 31 max_value=None, 最大值 32 min_value=None, 最小值 33 34 35 EmailField(CharField) 邮箱 36 ... 37 38 URLField(Field) URL 39 ... 40 41 SlugField(CharField) 只允许用户输入 数字,字母,下划线,减号(连字符) 42 ... 43 44 GenericIPAddressField 验证IP 45 protocol='both', both,ipv4,ipv6支持的IP格式 46 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 47 48 49 50 ChoiceField(Field) 下拉框 51 ... 52 choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) 53 required=True, 是否必填 54 widget=None, 插件,默认select插件 55 label=None, Label内容 56 initial=None, 初始值 57 help_text='', 帮助提示 58 59 60 MultipleChoiceField(ChoiceField) 多选 61 initial=[1,2] 多选默认就得是可迭代的 62 choices=[(1,"刘能"),(2,"赵四"),(3,"谢广坤")] 63 ... 64 65 RegexField(CharField) 66 regex, 自定制正则表达式 67 max_length=None, 最大长度 68 min_length=None, 最小长度 69 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} 70 71 72 73 74 75 76 77 78 79 80 81 FloatField(IntegerField) 继承上面的 82 ... 83 84 DecimalField(IntegerField) 十进制的小数 85 max_value=None, 最大值 86 min_value=None, 最小值 87 max_digits=None, 总长度 88 decimal_places=None, 小数位长度 89 90 BaseTemporalField(Field) 91 input_formats=None 时间格式化 92 93 DateField(BaseTemporalField) 格式:2015-09-01 94 TimeField(BaseTemporalField) 格式:11:12 95 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 96 97 DurationField(Field) 时间间隔:%d %H:%M:%S.%f 98 ... 99 100 101 102 103 104 FileField(Field) 105 allow_empty_file=False 是否允许空文件 106 107 ImageField(FileField) 108 ... 109 注:需要PIL模块,pip3 install Pillow 110 以上两个字典使用时,需要注意两点: 111 - form表单中 enctype="multipart/form-data" 112 - view函数中 obj = MyForm(request.POST, request.FILES) 113 114 115 116 117 BooleanField(Field) 布尔值 118 ... 119 120 NullBooleanField(BooleanField) 121 ... 122 123 124 125 126 127 128 129 130 ModelChoiceField(ChoiceField) 131 ... django.forms.models.ModelChoiceField 132 queryset, # 查询数据库中的数据 133 empty_label="---------", # 默认空显示内容 134 to_field_name=None, # HTML中value的值对应的字段 135 limit_choices_to=None # ModelForm中对queryset二次筛选 136 137 ModelMultipleChoiceField(ModelChoiceField) 138 ... django.forms.models.ModelMultipleChoiceField 139 140 141 142 TypedChoiceField(ChoiceField) 类型转换 143 coerce = lambda val: val 对选中的值进行一次转换 144 empty_value= '' 空值的默认值 145 146 147 148 TypedMultipleChoiceField(MultipleChoiceField) 149 coerce = lambda val: val 对选中的每一个值进行一次转换 150 empty_value= '' 空值的默认值 151 152 ComboField(Field)比如你之前写了很多字段,这个字段要满足以上几个,使用这个,把之前的Field可以组合起来用 153 fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 154 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) 155 156 MultiValueField(Field) 可扩展 157 PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 158 159 SplitDateTimeField(MultiValueField) 160 input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] 161 input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] 162 163 FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 164 path, 文件夹路径 165 match=None, 正则匹配 166 recursive=False, 递归下面的文件夹 167 allow_files=True, 允许文件 168 allow_folders=False, 允许文件夹 169 required=True, 170 widget=None, 171 label=None, 172 initial=None, 173 help_text='' 174 175 176 177 178 179 UUIDField(CharField) 只支持uuid输入的东西,UUID是根据你的网卡,根据当前时间,根据很多机器码,根据很多东西生成的随机字符串 180 ...
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
# 单radio,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 单radio,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.RadioSelect # ) # 单select,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) # ) # 单select,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.Select # ) # 多选select,值为列表 # user = fields.MultipleChoiceField( # choices=((1,'上海'),(2,'北京'),), # initial=[1,], # widget=widgets.SelectMultiple # ) # 单checkbox # user = fields.CharField( # widget=widgets.CheckboxInput() # ) # 多选checkbox,值为列表 # user = fields.MultipleChoiceField( # initial=[2, ], # choices=((1, '上海'), (2, '北京'),), # widget=widgets.CheckboxSelectMultiple # )
以后想对某一个字段,在错误级别,做更详细的定制的时候:
一个字段级别的

在执行,验证完正则以后才会执行这个方法,这是验证自定义每个字段级别的。 通过正则的验证以后,正确的值就保存在clean_data(clean方法的原码) 可以赋给一个变量,取到值,做些验证 如果value==“root”我才让通过, 让通过的时候你得做一件事,返回一个值,你返回的值是什么,那个clean_data里面的值就是什么(更换了) 返回的是value,因为只有是root时候才让通过 否则:不满足我的条件时,我就让他报错,原码有捕捉错误的得调用模块。 from django.core.exceotions import ValidationError else: raise ValidationError("你想填的内容")
整体级别的有两个方法 clean _post_Clean

_post_Clean


1、用户验证,首先用户发来一大堆的请求,请求里面有各种各样的数据,假设发来的数据都是字典,有很多,那接下来在我的后端,得把那些大字典传给我的from。 2、所以咱们创建from对象的时候,obj=类名(request.POST) 这个时候只是把整体的数据,传给obj了,还没有任何的操作,也没有做验证。 3、所以在执行obj=is_valid()的时候,在这,他有好多级别的验证;第一步他会循环From对象中所有的字段,根据字段名(也是字典的键),去用户请求中获取数据,获取到数据后,接下来得听过正则表达式的验证,这些正则表达式是封装在Fied对象里的,得去内部去做验证,调用字段的clean方法进行正则表达式的验证。到这,咱们自定义的方法还没执行,所以默认情况下只执行正则表达式的。 当正则表达式执行完之后,紧接着会执行另外一个操作,当前字段是有名字的,他在会调用一下当前字段前面有一个clean_name (name代指的就是字段名),他就会执行这个方法,那在这个方法的内部会对这个单独的字段进行操作clean_name,是调用的self所以self.clean_name 此时的self是From的对象obj。 此时的self.clean_name这就是调用你自定义验证的那一个字段,也就是self.clean_username ,验证单一字段的结束 4、还有整体级别的验证方法,可以自定义clean方法。 clean方法是支持抛出异常,因为在他外面有try except 方法,可以接收里面的异常,一但有异常就会加到error里面,当主动抛出异常时,它是不属于某一个,单一字段的,他的异常会保存在__all__里面,表示整体错误,如果你的clean方法里抛出异常了,内部就消化了,不会报错,只是把错误信息放到里面,整个程序不终止继续执行。 5、自定义的_post_clean方法里面,自己没有包含任何的代码,我如果继承了他,他会调用我,调用我的时候,我里面不能出错,如果出错,整个程序就崩溃了,所以在我们自定义的_post_clean时候,不允许出现异常,不允许出现异常,但是那个错误还是要捕捉到,所以我们会选择执行一个add_error,把错误放里面就行了
对下拉框进行操作时候 ,做到实时更新(就是数据库加进去数据直接就显示不用刷新页面)
方法1,自己设定__init__

方法 2 不用init方法 但是数据库那边 做个操作

数据库加字段名加str方法

还有一个多选框

四、Django Model
Model 主要是强大的数据库操作(也能和from自定义验证字段),弱小的数据验证(ORM)【增、改】
对数据库的表、行 进行操作。
表:
类级别:
创建类········>自动生成表
执行命令:python manage.py makemigrations python manage.py migrate
python manage.py migrate --fake 用来解决数库,跟类同步(删除表时候)
字段级别:分、字符串,数字、时间、二进制、还有文件 关系:(一对一)、(一对多)、(多对多) 参数:用于指定生成库列的信息,用于验证(admin、ModelForm)
字符串:在jango里面原生字符串,再加一个带正则表达式的字符串
数字:就是在数据库级别,他的长度是多少,比如说有些数字的长度,就应该是11位有些就应该是4位,范围不同而已,创建数字时候,指定长度是没有用的,因为他本质是上在内存里面,存的时候,他自己会按照自己的长度去存,你给他设置了也是不生效的。
时间:和字符串级别一样
二进制:存二进制数据
AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767 IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647 BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 BooleanField(Field) - 布尔值类型 NullBooleanField(Field): - 可以为空的布尔值 CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度 TextField(Field) - 文本类型 EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both" URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) - 浮点型 DecimalField(Field) - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField(Field) - 二进制类型
null 数据库中字段是否可以为空 db_column 数据库中字段的列名 default 数据库中字段的默认值 primary_key 数据库中字段是否为主键 db_index 数据库中字段是否可以建立索引 unique 数据库中字段是否可以建立唯一索引 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称 blank Admin中是否允许用户输入为空 editable Admin中是否可以编辑 help_text Admin中该字段的提示信息 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] )
ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 - models.CASCADE,当删除关联数据,与之关联也删除 - models.DO_NOTHING,当删除关联数据,引发错误IntegrityError - models.PROTECT,当删除关联数据,引发错误ProtectedError - models.SET_NULL,当删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) - models.SET_DEFAULT,当删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) - models.SET,删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id" on_delete=models.SET(func),) related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') db_constraint=True # 是否在数据库中创建 外键 约束 不约束了,但是还可以连表操作 parent_link=False # 在Admin中是否显示关联数据 OneToOneField(ForeignKey) to, # 要进行关联的表名 to_field=None # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 ###### 对于一对一 ###### # 1. 一对一其实就是 一对多 + 唯一索引 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 # 如下会在A表中额外增加一个c_ptr_id列且唯一: class C(models.Model): nid = models.AutoField(primary_key=True) part = models.CharField(max_length=12) class A(C): id = models.AutoField(primary_key=True) code = models.CharField(max_length=1) ManyToManyField(RelatedField) to, # 要进行关联的表名 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5}nid大于5 的取到 - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 # 做如下操作时,不同的symmetrical会有不同的可选字段 models.BB.objects.filter(...) # 可选字段有:code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True) # 可选字段有: bb, code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定义第三张表时,使用字段用于指定关系表 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64) db_constraint=True, # 是否在数据库中创建 外键 约束 不约束了,但是还可以连表操作 db_table=None, # 默认创建第三张表时,数据库中表的名称(自动生成第三张表时候)
#自己创建多对多 class Book2Author(models.Model) book= models.Foreignkey("Book") author=models.Foreignkey("Author") #建立联合唯一 class Meta: unique_together = ["book","author"] #建立联合索引 class Meta: index_together = ["book","author"] #字段索引 username = model.CharField(max_lenght=32,db_index=True) #普通索引 unique=True #唯一索引
行:
增、删、改、查、(ORM操作)
# 增 # models.表名.objects.create(**{"name":"alex"}) #create(**字段对象) 列表一个* # 查 # # models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) # models.Tb1.objects.all() # 获取全部 # models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 # models.Tb1.objects.exclude(name='seven') # 获取指定条件的数据 # 删 # # models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据 # 改 # 表名.objects.filter(name='wangkuan').update(name='wk') #update 不能用get filter,渠道的是一个集合,就把所有等于wangkuan的替换成wk #get 只会取到一个对象,没有就报错,他不能调用update
增删改查 在上篇博客有详细介绍
# 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于 # # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull #获取这个条件pub_date他为空的东西 # Entry.objects.filter(pub_date__isnull=True) # contains 模糊比配 # # models.Tb1.objects.filter(name__contains="ven") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range 范围查找 # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似 # # startswith,istartswith, endswith, iendswith, # order by 排序 # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc # group by .annotate生成having时前面写上filter表示就是where条件,如果在.annotate,后面写filter就表示是having了 # # from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset 索引 # # models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写 # # Entry.objects.get(title__regex=r'^(An?|The) +') # Entry.objects.get(title__iregex=r'^(an?|the) +') ==============对时间进行处理的 # date # # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year # # Entry.objects.filter(pub_date__year=2005) # Entry.objects.filter(pub_date__year__gte=2005) # month # # Entry.objects.filter(pub_date__month=12) # Entry.objects.filter(pub_date__month__gte=6) # day # # Entry.objects.filter(pub_date__day=3) # Entry.objects.filter(pub_date__day__gte=3) # week_day # # Entry.objects.filter(pub_date__week_day=2) # Entry.objects.filter(pub_date__week_day__gte=2) # hour # # Event.objects.filter(timestamp__hour=23) # Event.objects.filter(time__hour=5) # Event.objects.filter(timestamp__hour__gte=12) # minute # # Event.objects.filter(timestamp__minute=29) # Event.objects.filter(time__minute=46) # Event.objects.filter(timestamp__minute__gte=29) # second # # Event.objects.filter(timestamp__second=31) # Event.objects.filter(time__second=2) # Event.objects.filter(timestamp__second__gte=31)
# extra 子查询还是有局限性的 select where不能写一起 # # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) select=None select_params=None 这两个是对应的 where=None, params=None, 这两个是对应的 # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) new_id 这一列支持 "select col from sometable where othercol 这个查询结果 # Entry.objects.extra(where=['headline=%s'], params=['Lennon']) # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) where条件,通过Q也是可以的 逗号就是and条件 # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) # F # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q # # 方式一: # Q(nid__gt=10) # Q(nid=8) | Q(nid__gt=10) # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') # 方式二: # con = Q() # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # 执行原生SQL #写原生SQL时候不用去链接数据库了,jango配置文件已近写好了 # from django.db import connection, connections #connection这就是数据库链接 # cursor = connection.cursor() 创建游标 链接 # cursor = connections['default'].cursor() # cursor.execute("""SELECT * from auth_user where id = %s""", [1]) # row = cursor.fetchone()
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) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.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中的设置) 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) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.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中的设置)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) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.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中的设置) 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) def delete(self): # 删除 def update(self, **kwargs): # 更新 def exists(self): # 是否有结果 def count(self): # 获取个数 def get(self, *args, **kwargs): # 获取单个对象 def create(self, **kwargs): # 创建对象
五、Django ModelFrom
ModelFrom适中的数据库操作,强大的数据验证 用于数据处理和用户请求的验证。
适用场景;你写的程序是小程序,小到什么程度呢?你的后台和前台没有做分离,让ORM跟你的,操作views都放在一起的时候,这个东西就适合了。适合到什么程度呢!他可以利用数据库的字段,以及利用你自己的From, 又可以利用你的数据库,操作这些东西,全部都可以在页面上显示出来,并且给你做验证,并且保存数据,以及修改数据。
ModelFrom 创建

接下来的操作和from是一样的,如果表中有关联表,一对多,多对多,前端直接 obj.as_p 下拉框也就都出来了 但是可以将用户提交的数据添加到数据库,但是不能做删除

.save()内部其实还做了几件事情,你也可以把它拆开 save里面有个参数commit 默认是true

还可以对提交过来的数据进行修改

1、nid去接受户输入的数据, 2、如果是get请求,根据nid获取对应的数据库对象。(model_obj) 拿到对象以后我们要把 model_obj后台获取的数据传给前端,显示在页面上 3、创建对象obj ,=后面就是创建modelfrom的那个类名里面有一个方法instance,通过他来传 4、返回一个页面模板的obj就是,前端要生成input框,nid是传递给HTML,from表单提交的路径的,得获取当前的nid,要不URL那有正则 过不去 5、修改原先数据在提交就是post请求了,其他的没变,传数据时候后面多了 request.POST。这是要回去用户输入过来的数据。后面可以不加instance=model_obj,但是那就会认为是,数据是增加的而不受修改的 6、同样可以做验证然后保存
ModelFrom参数
ModelForm a. class Meta: model, # 对应Model的 fields=None, # 字段,里面可以是列表["name"],就显示那个model的name字段 exclude=None, # 排除哪些字段也是列表的形式 labels=None, # 提示信息 help_texts=None, # 帮助提示信息 widgets=None, # 自定义插件 error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) field_classes=None # 自定义字段类 (也可以自定义字段) localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 如: 数据库中 2016-12-27 04:10:57 setting中的配置 TIME_ZONE = 'Asia/Shanghai' USE_TZ = True 则显示: 2016-12-27 12:10:57 b. 验证执行过程 is_valid -> full_clean -> 钩子 -> 整体错误 c. 字典字段验证 def clean_字段名(self): # 可以抛出异常 # from django.core.exceptions import ValidationError return "新值" d. 用于验证 model_form_obj = XXOOModelForm() model_form_obj.is_valid() model_form_obj.errors.as_json() model_form_obj.clean() model_form_obj.cleaned_data e. 用于创建 model_form_obj = XXOOModelForm(request.POST) #### 页面显示,并提交 ##### # 默认保存多对多 obj = form.save(commit=True) # 不做任何操作,内部定义 save_m2m(用于保存多对多) obj = form.save(commit=False) obj.save() # 保存单表信息 obj.save_m2m() # 保存关联多对多信息 f. 用于更新和初始化 obj = model.tb.objects.get(id=1) model_form_obj = XXOOModelForm(request.POST,instance=obj) ... PS: 单纯初始化 model_form_obj = XXOOModelForm(initial={...})
两个中间还可以定义一个字段

这个From字段用于页面显示,以及用户验证,from字段跟数据库储存字段没关系,如果和数据库字段重复,页面上就根据from字段显示,还是from优先。验证也有from验证,数据库直接存就好了,以及验证过了。
如果和数据库不重名,页面上会对增加一个字段,就是让在页面上写了,和保存数据库没关系。
多写这个有什么用呢?就像有的登录页面会显示,一个月内登录。这条数据不是存储在数据库的,在session里做判断就行了,保存cookie的时候加个时间一个月就行了,所以页面上可以多出字段,但是他和数据库没关系,只是在咱们业务代码级别,我可以拿到这个值,根据这个值在做一些其他的额外的操作。
所以就是可以定制,页面上是可以显示额外的一些数据或者是把原来的那些数据,重新给他覆盖
六、中间件
先说下生命周期:请求来,我发起请求到你给我结果。
Django的生命周期:请求来先经过中间件,然后到URL······>视图函数······>数据库(模板)拿数据,再经过渲染得到一个字符串·······>再路过中间件返回给用户
中间件(管道)就是一个类,类里面有方法
在django项目的settings模块中,有一个 MIDDLEWARE 变量,其中每一个元素就是一个中间件,如下图。

原理就像这样

自己也可以自定义这些中间件 ,

固定的方法 不能改

打印的结果

建议自己定义使用的


中间件中可以定义五个方法,分别是:
- process_request(self,request) 请求
- process_view(self, request, callback, callback_args, callback_kwargs) 请求都走完 不走视图函数,再返回到有view方法的中间件 先把view走完
- process_exception(self, request, exception) ( 异常处理) 把上面的view走完,视图函数走完,会走这个异常,走完异常然后返回到最后在一个个的走response,异常处理只会对返回的视图函数有异常会处理
- process_response(self, request, response) 返回
- process_template_response(self,request,response)

七、CSRF
跨站伪造请求:他的存在,只能做一部分的,安全性的验证,不能把它作为所有安全的一个准则
首先你发送get请求,是不会遇到这个阻止,发post时候才有阻止,就是说咱们发post请求的时候,他会要求我额外的在提供一点东西,而这个东西是怎么来的呢?是你第一步先发一个get请求,拿来的,所以 这个csrf,如果你想把它携带上,那你必须发两次http请求。
第一次来先要你给我一登录页面,其实咱们在页面上看到的不只是登录页面,其实在你的HTML表单里有一大堆东西接下来csrf的原理是:
我第一次来你这访问发一个get请求,然后你给我发一段东西(证书似的),不仅把页面给我,还给我盖个章,(再给我兜里放个东西【在cookie里】)我要向往你这提交,我可以带着这个章去,你已近给过我验证了,直接去就好了。
第二次,你发的就是post请求了,我们对post才做这种验证,所以第二次来的时候带着这个去了,去了之后,我会先验证一下你这个东西,如果你那个不过,后面不让你做任何操作,这属于from表单提交
做到一部分的防护就是:你直接给我发post请求,不能直接,接受请求。如果说你先发一个get请求,把这个字符串获取到,再发一个post请求这个就是可以的
对于ajax提交:cookie中提取的 随机字符串名字就叫csrftoken对应的值设置请求头XXOO cookie中取到的值 设置请求头 之后,给他发请求的时候,带着这个请求头,后台直接就让他通过了
请求头叫什么得自己测
from提交 后端render页面渲染 调用 前端{% csrf_token %}
ajax当前页面的所有ajax做一个同意的配置
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> {% csrf_token %} <input type="button" onclick="Do();" value="Do it"/> <script src="/static/plugin/jquery/jquery-1.8.0.js"></script> <script src="/static/plugin/jquery/jquery.cookie.js"></script> <script type="text/javascript"> ; ajaxSetup当前页面的所有ajax做一个同意的配置 beforeSend发送前前执行的操作 $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken",$.cookie('csrftoken')); } } }); function Do(){ $.ajax({ url:"/app01/test/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } </script> </body> </html>
还可以


局部:
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。(其他的都不用CSRF,就给个函数用)
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。(其他的都用CSRF,就给个函数不用用)
注:
引入:from django.views.decorators.csrf import csrf_exempt,csrf_protect
八、缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache(别的机器的内存)中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
最后两种是一种
1、配置(直接复制在setting文件下面)
a、开发调试
复制代码
# 此为开始调试用,实际内部不做任何操作
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', #BACKEND指的就是 引擎 到底用哪种
=======================下面这些 对下面对的那些配置都适用===================
'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300, # 最大缓存个数(默认300)就像一个网站1000个网页,不用全缓存啊,实时性不高的 访问量特别打大的 适合做缓存
'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,把缓存里的老的剔除掉,3 表示三分之一 即:1/CULL_FREQUENCY(默认3)
},
------------下面这三个不写也行--------
'KEY_PREFIX': '', # 缓存key的前缀(默认空)
'VERSION': 1, # 缓存key的版本(默认1)
'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
}
}
# 自定义key
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Constructs the key used by all other methods. By default it prepends
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return '%s:%s:%s' % (key_prefix, version, key)
def get_key_func(key_func):
"""
Function to decide which key function to use.
Defaults to ``default_key_func``.
"""
if key_func is not None:
if callable(key_func):
return key_func
else:
return import_string(key_func)
return default_key_func
b、内存
# 此缓存将内容保存至内存的变量中
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', #主要的改zhe一个
'LOCATION': 'unique-snowflake', #这是一个唯一的名称,可以任意指定,但是必须是唯一的(就相当于在内存里创建一个变量,变量名就指向内存的地址)
}
}
# 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', #改这个
'LOCATION': '/var/tmp/django_cache', 缓存文件的路径,你要把你的东西缓存到哪个位置,放到那,以后再去拿的时候,直接拿开给用户就行了,文件路径下面会生成多个文件
}
}
# 注:其他配置同开发调试版本
d、数据库
# 此缓存将内容保存至数据库
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表
}
}
# 注:执行创建表命令 python manage.py createcachetable执行一下这个命令,执行完这个命令,他就自动帮你在数据库生成一张表,这个表就是专门用来放置缓存的
e、Memcache缓存(python-memcached模块)这是另外一台机器,有IP有端口
# 此缓存使用python-memcached模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', 连上那台机器,然后去他的内存里面拿,就行了,指定一下那个机器那个端口
}
}
这个软件装上之后,我连上他给他写点东西,我写的时候必须加 键:值,并且还可以加个超时时间,所以我给他写个缓存的时候,我可以给他指定 建是多少,值是多少,超时时间是多少,就写这台机器上了,他就在这台机器是上保存着,怎么保存的呢,他就是普通的字符串,并且保存的时候,值是字符串,键也是字符串而整体的memcache就是一个大字典,每一个键值对就是一个缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock', 这个就相当于做了一个文件,这个文件就包含了cocket,而你就不用写id写端口了,搞一个文件,通过这个文件给了他他就自动在Unix里面他自动能识别出来,但是这个有限制,只能在本机上面连
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]加了个列表,列表里面加了多个,这就类似于分布式的了,比如我们有一千个缓存,如果写了他之后,就会把这一千个缓存,在a机器上放一点,在b机器上放一点,两人就分开了,不在放在单机器上了,因为放在单台机器上,可能出现问题,如果这台机器挂了,所有缓存全完了,缓存完蛋了都去数据库拿了,一下数据库压力就变大了,雪崩了 数据库永远起不来了,因为请求一直来,数据库还体不起来,拿到数据量特别大,你的内存肯也不够,你给不了别人,所以就做多台机器
后面可以写成元祖( '172.19.26.240:11211',10)('172.19.26.242:11211',20) 设置权重还是这台机器,别的机器得通过这台机器进行缓存分配,通过这台机器去存的时候给给我一个键 个值 ,你们给了我键之后我会,第一件是把键转化为数字,细算转化成数字,我会根据这个数字,计算出来我到底放在哪个机器上,这个模块它是通过求余数,假如是999除三 是0 我就让他放在这个列表的第0个元素,放到第一台机器上了,但是用户发过来的键是没准生成多少,也许全都命中第一台机器了,别的机器都可少,如果出现这种情况,分布不均了,你要想调整这个平衡,你可以选择设置一些权重,就后面那个值越大,权重越高,怎么设置呢在列表里面多写就行 想20 就复制20遍
}
}
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', #也是memcache模块不一样
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
本质上其实都是一样的,放在内存里面的是一个字典对应的键:值,一条键值队就是一个缓存
文件里的,有文件夹,文件夹里一个个的文件,每个文件就是一个缓存
放在数据库里的,一个表那就是所有的缓存,一条数据就是一条缓存
http://www.cnblogs.com/wupeiqi/articles/5132791.html
2、应用
a、单独视图缓存
from django.views.decorators.cache import cache_page 先引入一个模块 @cache_page(10) 表示我对这个函数进行缓存了,加装饰器 可以设置缓存时间 10 就是10秒 def my_view(request): ...
b、局部视图使用
a. 引入TemplateTag {% load cache %} 这个标签写最上面 b. 使用缓存 {% cache 10 缓存key %} 缓存内容 {% endcache %} 10 就是19秒 缓存key就是自己起个名字,当键 就比商城里物品的数量不该做缓存,商品介绍就可以做缓存,局部的,就少了页面渲染和数据库请求了
c. 全站使用
请求来先走中间件,回的时候也要只走中间件,那我们可以先去缓存里看看有没有缓存,如果有我去缓存里面拿到,直接给你返回就行了,就不会进过视图了,但是要放到最后一个中间件,因为前面的中间件都通过以后代表正确的请求,是按规范操作的。
我们要想让全站使用缓存得写两个中间件,一个中间件放在最前面,一个放在最后面,请求来要想获取去最后一个获取。而第一个中间件在返回的时候,第一个中间件再去放,所以第一个中间件只有一个 process_response 最后一个中间件也是就一个process_view所以一个中间件里也可以只有一个方法。
请求来 第一个中间件只有response跳过,最后一个中间去缓存里看,有直接返回,没有就走视图函数,函数走完 跳过view 到前面那个中间有返回,然后给缓存 那个响应给用户
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 特殊的中间件 jango写好的,放那就能应运上 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] 这个缓存走的时候就是默认值了,默认300秒,剔除时候剔除三方之一 这个就已近挺牛逼了
九、信号
Django(预留的钩子)在钩子那可以写自己的功能,然后jango在执行的过程中就可以执行咱们写的功能
这个钩子有多少个呢?
Model signals
这是钩子的名称,不能改变的
pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发 model.userinfo(name="wangk") 或 model.userinfo.objects.create(....) 在内部就是创建构造方法在执行个save
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发 就是save之前和之后
pre_delete # django的modal对象删除前,自动触发
post_delete # django的modal对象删除后,自动触发
m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 在加载每一个类的时候也会触发相应的函数
==================就是Django预留了很多地方让咱们操作,我们还没自定义,就是先找预留了那些==================
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
Request/response signals
request_started # 请求到来前,自动触发
request_finished # 请求结束后,自动触发 就是HttpResponse前后
got_request_exception # 请求异常后,自动触发
Test signals
setting_changed # 使用test测试修改配置文件时,自动触发
template_rendered # 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created # 创建数据库连接时,自动触发
对于Django内部定义的信号,在指定的位置,注入指定的操作(注册这个钩子一定要在程序第一次运行起来的时候注册上,在setting文件上的__init__.py里就行。如果文件太大,在这个文件里在导入到其他文件)
from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created def callback(sender, **kwargs): print("xxoo_callback") print(sender,kwargs) xxoo.connect(callback) # xxoo指上述导入的内容 # xxoo指上述导入的内容比如pre_init.connect(callback) callback就是你定义的那个函数,注册一个钩子 sender是指谁触发的,是jango内部触发的,那这个sender里面包含了jango给咱们提供的一些参数,比如说你当前是对那个model进行操作的,执行model操作的时候,给他传了几个参数,也是都封装在这个参数里面了
自定义信号
a. 定义信号
import django.dispatch pizza_done = django.dispatch.Signal(providing_args=["s", "y"]) 这就表示定义了一个信号 你要往我这个信号里注入函数的时候得有两个参数 s y
b. 注册信号
def callback(sender, **kwargs):
print("自定义信号callback")
print(sender,kwargs)
pizza_done.connect(callback)
#执行这个方法,也把函数注入到这个信号里面了,这是不会触发的,因为Django不知道这个信号的存在,Django也不帮你触发,我们得主动的去触发
c. 触发信号
from 路径 import pizza_done 路径找到你的信号 pizza_done.send(sender='seven',s=123, y=456) #pizza_done.send 信号.send 就表示要触发这个信号了 #sender 表示给他传个值,表示谁触发的 后面可以给他加额外的参数 #触发得写在对应的视图函数那
什么时候用自定义信号呢?比如你设计到某一个地方的时候,这个地方以后可能会有各种各样的扩展(比如以前提醒消息,邮箱短信,后来又有了微信,其他)
浙公网安备 33010602011771号