一、全自动(推荐使用的**)css
优点:第三张能够任意的扩展字段html
缺点:ORM查询不方便,若是后续字段增长更改时不便添加修改前端
manyToManyField建立的第三张表属于虚拟的,后缀会自动添加有_id的外键字段python
建立的方式:jquery
二、纯手动(不推荐使用)正则表达式
须要手动创第三方表数据库
优点:第三张表能够任意的扩展字段django
缺点:ORM查询不便后端
三、半自动(推荐使用***)浏览器
优点:结合了全自动和半自动的两个优势,把建表的关系直接在一张表上表示出来
这样建立的表,在多对多时不支持ORM的操做有如下几种:
""" 多对多字段的 add set remove clear不支持 """
一、校验功能:
二、标签的渲染功能
三、渲染错误的信息
四、局部钩子,再次检验
简单的注册功能的版本:
实现简单的校验功能。设置条件,在注册的时候进行有效的检验
from django.shortcuts import render,reverse,redirect,HttpResponse from django.core.exceptions import ValidationError # Create your views here. 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)<6: errors['password'] = '密码太短,不安全' return render(request,'login.html',locals())
login.html:
在提交的按钮,输入框中绑定提示的错误信息,校验的条件在后端进行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ errors.username }}</span> </p> <p>password: <input type="password" name="password"> <span style="color: red">{{ errors.password }}</span> </p> <input type="submit"> </form> </body> </html>
总结:forms组件可以直接完成如下的三部分
1.渲染前端页面
2.校验数据是否合法
3.展现错误信息
基本的校验规则:
先写好一个继承了forms.Form的类
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必须是邮箱格式
forms的校验规则:根据字段一个个进行比对,符合条件和错误的分别的保存起来
1.将须要校验的数据,以字典的方式传递给自定义的类,实例化产生对象
form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'})
2.如何查看数据是否所有合法
form_obj.is_valid() # 只有全部的数据都符合要求 才会是True
False
3.如何查看错误缘由
form_obj.errors
{
'password': ['Ensure this value has at least 5 characters (it has 3).'],
'email': ['Enter a valid email address.']
}
4.如何查看经过校验的数据
form_obj.cleaned_data
{'username': 'jason'}
直接在pycharm终端输入命令进行校验:
注意事项:
1.自定义类中全部的字段默认都是必需要传值的
2.能够额外传入类中没有定义的字段名 forms组件不会去校验 也就意味着多传一点关系没有
书写from表单的三种方法:
前端简单的from表单以下,原始的
在Django中利用forms组件书写的from表单又如下三种方法:
先在后端开启路由向前端传递数据信息,views.py
def register(request): # 先生成一个空白的自定义类对象 form_obj = LoginForm() # 将该对象传递给前端 return render(requesr,'register.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method='post' novalidate> <p>第一种渲染页面的方式(封装程度过高 通常只用于本地测试 一般不适用)</p> {{ form_obj.as_p }} {{ form_obj.as_ul }} {{ form_obj.as_table }} <p>第二种渲染页面的方式(可扩展性较高 书写麻烦)</p> <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> <p>第三种渲染页面的方式(推荐)</p> {% for foo in form_obj %} <p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form> </body> </html>
moldes.py,建表的时候指定校验的方式,和条件
from django import forms from django.core.validators import RegexValidator from django.forms import widgets class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用户名',initial='tankdsb', error_messages={ 'max_length':'用户名最大八位', 'min_length':'用户名最小三位', 'required':'用户名不能为空' },widget=widgets.TextInput() ) # 用户名最长八位最短三位 password = forms.CharField(max_length=8,min_length=5,label='密码',error_messages={ 'max_length':'密码最大八位', 'min_length':'密码最小五位', 'required':'密码不能为空' }, widget=widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'}) ) # 密码最长八位最短五位 confirm_password = forms.CharField(max_length=8, min_length=5, label='确认密码', error_messages={ 'max_length': '确认密码最大八位', 'min_length': '确认密码最小五位', 'required': '确认密码不能为空' }, required=False, validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')]) # 密码最长八位最短五位 email = forms.EmailField(label='邮箱',error_messages={ 'required':'邮箱不能为空', 'invalid':'邮箱格式不正确' }) # email必须是邮箱格式
views.py
def reg(request): # 1 现生成一个空的自定义类的对象 form_obj = LoginForm() # 2 将该对象传递给前端页面 if request.method == 'POST': # 3 获取前端post请求提交过来的数据 # print(request.POST) # 因为request.POST其实也是一个字典,全部能够直接传给LoginForm form_obj = LoginForm(request.POST) # 4 校验数据 让forms组件帮你去校验 if form_obj.is_valid(): # 5 若是数据所有经过 应该写入数据库 pass # 6 若是不经过 一个像前端展现错误信息 return render(request,'reg.html',locals())
前端html
注意事项:
1.forms组件在帮你渲染页面的时候,只会渲染获取用户输入的标签 ,提交按钮须要你手动添加
2.input框的label注释 ,不指定的状况下,默认用的类中字段的首字母大写
告诉前端取消掉自带的校验方式,form标签指定novalidate属性便可,让后端来书写
掌握的自定义字段,用正则表达式书写校验规则,手机号的验证:
RegexValidator验证器
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import 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'邮箱'}))
就是forms组件暴漏给用户的,能够自定义额外的检验规则;必须写在Form类里面
咱们在Fom类中定义 clean_字段名() 方法,就可以实现对特定字段进行校验。
局部钩子(针对某一个字段作额外的校验) 校验用户名中不能包含666 一旦包含 提示
def clean_username(self): username = self.cleand_data.get('username') if '666' in username: self.add_error('username','密码有危险提示,请重新修改哦') return username
全局钩子(针对多个字段作额外的校验) 校验用户两次密码是否一致
有password和confirm_password两个字段:
# 全局钩子(针对多个字段作额外的校验) 校验用户两次密码是否一致 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 = widgets.PasswordInput() 控制标签属性 widget = widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'jason'}) """
多个类就空格格开书写:c2 c2
# 单选的radio框 gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() ) # 单选select hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() ) # 多选的select框 hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 单选的checkbox keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选的checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) phone = forms.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
具体的展现:
经过源码解析,看Django内部的forms组件是如何是实现校验的原理的;大体了解
因为Http协议的无状态的特色,也就是说即便第一次和服务器链接后而且登陆成功后,
第二次请求服务器依然不能知道当前请求是哪一个用户,cookies的出现就是为了解决这一现象。
cooki就是保存在客户端浏览器上的键值对:
工做原理:当用户登陆成功后,浏览器上会保存一些信息,下次再来访问的时候,就会携带着这些信息去访问服务端,服务端经过这些信息来识别出你的身份
session和cookie的做用有点相似,都是为了存储用户相关的信息。不一样的是,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,
不容易被窃取。但存储在服务器也有必定的弊端,就是会占用服务器的资源,但如今服务器已经发展至今,一些session信息仍是绰绰有余的。
session就是保存在服务器上的键值对
session虽然是保存在服务器上的键值对
可是它是依赖于cookie工做的
前提分析:Django返回给客户端浏览器的都必须是HttpResponse对象;
三板斧:
return HttpResonse(): 返回字符串
return render() 返回html页面
return redirect() 重定向到...
经过对cookie的操做,让服务器如何存储数据信息,保存用户的登陆状态,简单的写一个都登陆注册的例子:
首先先掌握如何操做cookie:
request.COOKIE.get()
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
rep = HttpResponse(...) rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
设置超时时间
max_age=None, 超时时间
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
删除Cookie
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 删除用户浏览器上以前设置的usercookie值 return rep
views,py
def lg(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jack' and password == '123': # 先获取url中get请求携带的参数 old_url = request.GET.get('next') # 判断用户是直接访问的登录页面 仍是从别的页面的调过来 if old_url: obj = redirect(old_url) else: # 若是用户直接访问的登录页面 那么登录完成以后 直接跳到网站的首页 obj = redirect('/home/') obj.set_cookie('name','jason',max_age=30) # 浏览器上就会保存键值对name:jack return obj return render(request,'login.html')
hime返回主页面的功能
def home(request): if request.COOKIES,get('name'): return HttpResonse('home主页面,只有登陆了才能够进来!') return redirect('login') return HttPResonse('home页面,只有登陆了才能看!')
若是页面过多,访问时都须要设置先登陆,这时候就想到了设置一个登陆装饰器,在全局哪里须要就往那里加。
from functools import wraps def login_auth(func): @wraps(func) def inner(request,*args,**kwargs): # 从request中获取cookie # print(request.path) # print(request.get_full_path()) target_url = request.get_full_path()# 获取url的后缀/?next=%s if request.COOKIES.get('name'): res = func(request,*args,**kwargs) return res else: return redirect('/login/?next=%s'%target_url) return inner
利用装饰器,开设其余跳转页面功能
@login_auth def index(request): return HttpResponse("index页面 只有登陆了才能访问") @login_auth def xxx(request): return HttpResponse('xxx页面 登录以后才能看')
注销功能删除cookie保存的数据,下次登陆就不会再保存原有的数据进行校验,得从新登陆
def logout(request):
obj = redirect('/lg/')
obj.delete_cookie('name')
return obj
login.html前端的简单书写from表单,按钮实现登陆接口
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ errors.username }}</span> </p> <p>password: <input type="password" name="password"> <span style="color: red">{{ errors.password }}</span> </p> <input type="submit"> </form> </body> </html
分析以上页面之间跳转的实现原理:基于cookie的原理,用获取和设置cookie,保存cookie所保存的用户数据信息,
下次再次访问的时候携带上cookie在客户端保存的用户信息进行校验比对,是否属于登陆的状态,登陆成功或自动保存。
获取、设置、删除Session中数据
request.session['k1'] request.session.get('k1',None) 取值通常应set的方式 request.session['k1'] = 123 赋值 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1']
session保存在服务端的,数据保存在Django默认自带的数据库表,当执行数据迁移命令的时候都会自带一个django_session表;
设置session request.session['name'] = 'jack' """ 上面这一句话发生了三件事 1.django 内部自动生成一个随机字符串 2.将随机字符串和你要保存的数据 写入django_session表中(先在内存中生成一个缓存记录,等到通过中间件的时候才会执行) 3.将产生的随机字符串发送给浏览器写入cookie sessionid:随机字符串 """ 获取session request.session.get('name') """ 上面这一句话发生了三件事 1.django内部会自动从请求信息中获取到随机字符串 2.拿着随机字符串去django_session表中比对 3.一旦对应上了就将对应的数据解析出来放到request.session中 """
方法的使用
def set_session(request): # request.session['name'] = 'jason' # request.session['name1'] = 'jason1' request.session['xxx'] = 'xxx' request.session.set_expiry(30) return HttpResponse('set_session') def get_session(request): # print(request.session.get('name')) # print(request.session.get('name3')) print(request.session.get('xxx')) return HttpResponse('set_session') def delete_session(request): # request.session.delete('xxx') request.session.flush() return HttpResponse('delete_session')
解析:
删除当前会话的全部Session数据 request.session.delete() # 删除的是浏览器的sessionid信息 # 删除当前的会话数据并删除会话的Cookie。 request.session.flush() # 将浏览器和服务端所有删除 这用于确保前面的会话数据不能够再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。 # 设置会话Session和Cookie的超时时间 request.session.set_expiry(value) *若是value是个整数,session会在些秒数后失效。 *若是value是个datatime或timedelta,session就会在这个时间后失效。 *若是value是0, 用户关闭浏览器session就会失效。 *若是value是None, session会依赖全局session失效策略。
总结:你在后期能够将一些数据保存到session表中,保存的数据 能够在后端任意位置获取到