一。多对多建表关系之手动添加。html
1。全自动前端
像以前讲过的同样,咱们能够经过manytomanyField的字段来创建多对多关系:python
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
优势:不须要你手动建立第三张表,并且可使用外键查询表。git
缺点。因为第三张表不是本身手动建立的,因此第三张表的字段是固定的没法增长字段。数据库
2.纯手动。django
再多对都的建立中,也能够手动建立第三张表,而后使用外键链接到两个表之间。后端
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') create_time = models.DateField(auto_now_add=True)
优势:能够任意添加扩展的字段。浏览器
缺点:不能使用orm中的外键查询。缓存
3.半自动服务器
在建立第三张表的状况下,建立1,2张表的manytomany到该表中
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = models.ManyToManyField (to='Author',through='Book2Author',through_fields=('book','author')) # through 告诉django orm 书籍表和做者表的多对多关系是经过Book2Author来记录的 # through_fields 告诉django orm记录关系时用过Book2Author表中的book字段
和author字段来记录的 class Author(models.Model): name = models.CharField(max_length=32) # books = models.ManyToManyField
(to='Book', through='Book2Author', through_fields=('author', 'book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') create_time = models.DateField(auto_now_add=True)
manytomany字段中的各个参数的意义是:
to:关联的那张表。
through:绑定的第三张表。
throuth_fields:记录关系时用到的字段,前面时本表查询他表时通过的字段
这种方法定义的表不适合使用如下查询或设置函数:
add set remove chear。
二。forms组件。
在前端输入用户名和密码时,会有提示你的密码过于短等,这些都是forms的做用,用来限制你输入的内容并报错,咱们能够手动实现一个简单的版本:
def login(request): errors = {'username':'','password':''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if '很差的' in username: errors['username'] = '不符合名字' if len(password) < 3: errors['password'] = '过短了' return render(request,'login.html',locals())
<form action="" method="post"> <p>username:<input type="text" name="username"><span>{{ errors.username }}</span></p> <p>password:<input type="password" name="password"><span>{{ errors.password }}</span></p> <p><input type="submit" value="提交"></p> </form>
刚刚的代码种会出现如下步骤:
1.前端页面搭建。
2。将数据传输到后端作校验。
3.展现错误信息。
而使用forms组件会出现如下步骤:
1.渲染前端页面
2.校验数据是否合法
3.展现错误信息
1.基本的forms用法:
这个组件须要导入模块,语法和orm差很少:
from django import forms class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3) # 用户名最长八位最短三位 password = forms.CharField(max_length=8,min_length=5) # 密码最长八位最短五位 email = forms.EmailField() # email必须是邮箱格式
在测试这些代码时,能够在python中的终端中测试,可是须要先导入相关模块,本地路劲是当前项目的路径。
1.若是须要校验数据,就须要以字典 的方式传递给自定义的类,它会产生对象,经过该对象的一些方法就能够实现检测:
form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'})
2.使用is_valid()方法就能够判断该字典中的数据是否都符合要求。
form_obj.is_valid() # 只有全部的数据都符合要求 才会是True False
3.查看错误缘由,当使用errors时,会返回一个字典加列表的数据,其中就有该数据的错误缘由:
form_obj.errors {'password': ['Ensure this value has at least 5 characters (it has 3).'], 'email': ['Enter a valid email address.'] }
4.如何查看经过校验的数据:cleaned_data方法
form_obj.cleaned_data {'username': 'jason'}
在使用forms字段时:
1.当输入的字典中有其余自定义类中的数据时,不会出错,forms也不会处理。
2.当输入的字典中没有自定义的类中的一些字段时,会出错,is_valid()会时false。
3。具体流程就是将字典中的字段和类中的字段一一匹配,再比较限定条件。
2.渲染页面的方法:
再页面中,能够直接将该类生成的对象返回给前端,前端段能够直接进行渲染。
1.:过分封装的渲染方法:
第一种渲染页面的方式(封装程度过高 通常只用于本地测试 一般不适用):
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
as_p就是将结果变成p格式的
as_ul就是变成列表形式的。
as_table就是普通格式的。
2.扩展性较高的方法,
这种方法书写麻烦,每一个对象中点出其中的label和其中的名字。
<p>{{ form_obj.username.label }}{{ form_obj.username }}</p> <p>{{ form_obj.password.label }}{{ form_obj.password }}</p> <p>{{ form_obj.email.label }}{{ form_obj.email }}</p>
3.可批量的渲染:
将该对象for循环能够得出其中的全部字段信息。
{% for foo in form_obj %} <p>{{ foo.label }}{{ foo }}
<span>{{ foo.errors.0 }}</span>
</p>
{% endfor %}
3.错误信息展现。
使用foo.errors能够将错误信息以列表的形式展出,可是会改变原来的板块,因此须要再后面.0取出其中的原本的错误信息。
提交按钮不会帮你添加,须要手动添加。
当form中的label没有指定的时候,就是该字段名的首字母大写。label表示的是这个字段表明的内容名字
在前端,这些限制已经有了做用,可是这些限制在爬虫的对抗上没有什么用,因此须要在前端和 后端都加上限制。为了取消前端的限制,测试后端限制,须要给表单加上关键字novalidate
在普通字段中,须要显示中文的错误提示,须要对其error_messages进行设置。其中的值表明的是:
1.max_length,最大的。。这些都是在定义字段是本身定义的。
2.required表明为空是报的错。
3.invalid表明邮箱的格式是否正确。
例子:
password = forms.CharField(max_length=8,min_length=5,label='密码',error_messages={ 'max_length':'密码最大八位', 'min_length':'密码最小五位', 'required':'密码不能为空' },)
在字段中也有可使用正则匹配的限制条件,关键字是RegexValidator,后面是错误信息:
class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
也能够将限制条件写成函数使用函数校验:
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定义验证规则 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '标题不能为空', 'min_length': '标题最少为5个字符', 'max_length': '标题最多为20个字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '标题5-20个字符'})) # 使用自定义验证规则 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = fields.EmailField(required=False, error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
三。钩子函数。
当你的原本字段中的函数限制校验经过后,还须要进行校验是,就须要经过钩子函数。
改函数须要返回对应的字段
钩子函数的定义是在自定义类中定义,类中会将你定义的字段钱加clean_封装好,定义的时候有两种报错方式:
def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: # raise ValidationError('奥术大师就卡的凯撒就确定会') self.add_error('username','光喊666是不行的 你得本身上') return username
一种是raise,一种是add_error。
还有一种是全局钩子,能够跨字段进行校验:
返回值是数据字典。
def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not password == confirm_password: self.add_error('confirm_password','两次密码不一致') return self.cleaned_data
四。其余字段的操做方式。
在字段中,有如下参数能够设置:
required 是否必填
label 注释信息
error_messages 报错信息
initial 默认值
widget 控制标签属性和样式(密码格式,文本格式等)
关于widget关键字,还能够用来操做class,也就是添加类:
widget=widgets.PasswordInput(
attrs={'class':'form-control c1 c2','username':'jason'})
其余类型字段:
1.radioSelect
gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() )
2.单选Select
hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() )
3.多选Select
hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
4.单选checkbox
keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )
5.多选checkbox
hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
以上的代码中initial是其默认选择项,widget表明其中的真实数据。
is_valid原理:
首先先获取全部的字段的信息,放入全局的cleaned_data中,再调用全局的校验方法,进行校验,有错就会报错。
五。cookies和session
因为http协议是无状态的,法记录用户状态 。
1.cookies
cookie就是保存在客户端浏览器上的键值对。
工做原理:当你登录成功以后 浏览器上会保存一些信息。下次再访问的时候 就会带着这些信息去访问服务端 服务端经过这些信息来识别出你的身份。
cookie虽然是写在客户端浏览器上的 可是是服务端设置的。浏览器能够选择不服从命令 禁止写cookie。
2.session
session就是保存在服务器上的键值对。
session虽然是保存在服务器上的键值对。可是它是依赖于cookie工做的。
服务端返回给浏览器一个随机的字符串。浏览器以键值对的形式保存。
sessionid就是一个随机字符串。浏览器在访问服务端的时候 就会将随机字符串携带上。后端获取随机串与后端的记录的作比对。如:
随机字符串1:数据1。
随机字符串2:数据2
查看Cookie
咱们使用Chrome浏览器,打开开发者工具。
六。cookies的获取和删除。
cookies的获取能够从COOKIES中获取。
设置cookie利用的就是HttpResponse对象
obj1.set_cookie('k1','v1')
获取cookie
request.COOKIE.get()
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
而再设置cookies中是以键值对的方式设置。设置的对象就是HttpResponse对象,最后再返回这个对象便可
obj.set_cookie('name','jason',max_age=30) return obj
参数:
其中还有删除方法:
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 删除用户浏览器上以前设置的usercookie值 return rep
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已经登陆的用户... return func(request, *args, **kwargs) else: # 没有登陆的用户,跳转刚到登陆页面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html") cookie版登陆
七。session的删除和设置。
session设置须要打开数据库,首先生成数据库文件:
# 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1']
而设置session通过了三件是,也就是session['key']= 'val'
1.django 内部自动生成一个随机字符串
2.将随机字符串和你要保存的数据 写入django_session表中(如今内存中生成一个缓存记录 等到通过中间件的时候才会执行)
3.将产生的随机字符串发送给浏览器写入cookie
获取session也会发生这几件事:
1.django内部会自动从请求信息中获取到随机字符串
2.拿着随机字符串去django_session表中比对
3.一旦对应上了就将对应的数据解析出来放到request.session中
session中的记录默认是保存14tian,过时后数据不会消失,可是取值取不到。
当单个session设置了多个值时,值会保存到一个sessionid中,能够正常取值。
django_session表中的一条记录针对一个浏览器。
删除信息。
删除信息有两种:
# 删除当前会话的全部Session数据 request.session.delete() # 删除当前的会话数据并删除会话的Cookie。 request.session.flush() 这用于确保前面的会话数据不能够再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。
delete是将浏览器中的session删除。
而flush,删除的是数据库中的session和浏览器中的。
session也能够设置超时时间:
# 设置会话Session和Cookie的超时时间 request.session.set_expiry(value) * 若是value是个整数,session会在些秒数后失效。 * 若是value是个datatime或timedelta,session就会在这个时间后失效。 * 若是value是0,用户关闭浏览器session就会失效。 * 若是value是None,session会依赖全局session失效策略。
过时后数据不会消失,可是取值取不到。
在后期能够将一些数据保存到session表中,保存的数据 能够在后端任意位置获取到。