多对多的三种创建方式-forms相关组件-钩子函数-cookie与session

多对多的三种创建方式

  1、全自动(推荐使用的**)

优势:第三张可以任意的扩展字段

缺点:ORM查询不方便,如果后续字段增加更改时不便添加修改

  manyToManyField创建的第三张表属于虚拟的,后缀会自动添加有_id的外键字段

创建的方式:

 

   2、纯手动(不推荐使用)

  需要手动创第三方表

优势:第三张表可以任意的扩展字段

缺点:ORM查询不便

 

   3、半自动(推荐使用***)

优势:结合了全自动和半自动的两个优点,把建表的关系直接在一张表上表示出来

 

 这样创建的表,在多对多时不支持ORM的操作有以下几种:

"""
   多对多字段的
   add
   set
   remove
   clear不支持
   
"""

 Form组件

  forms组件功能

  1、校验功能:

  • 就是将form表单中的值在post请求发送到服务端时,服务端利用forms组件去检验是否符合规则
  • form表单中的name属性值要和自定义forms组件的字段一致。

  2、标签的渲染功能

  3、渲染错误的信息

  4、局部钩子,再次检验

使用forms组件是实现注册功能

简单的注册功能的版本:

  实现简单的校验功能。设置条件,在注册的时候进行有效的检验

views.py

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组件的基本用法

基本的校验规则:

  先写好一个继承了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的校验规则:根据字段一个个进行比对,符合条件和错误的分别的保存起来

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组件不会去校验 也就意味着多传一点关系没有

在书写的字段内;只能多传不能少传。

 forms三种渲染方式

  书写from表单的三种方法:

前端简单的from表单如下,原始的

 

 

 在Django中利用forms组件书写的from表单又以下三种方法:

先在后端开启路由向前端传递数据信息,views.py

def register(request):
    # 先生成一个空白的自定义类对象
    form_obj = LoginForm()
    # 将该对象传递给前端
    return  render(requesr,'register.html')

前端书写的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>

1、第一种渲染页面的方式(封装程度太高 一般只用于本地测试  通常不适

2、<p>第二种渲染页面的方式(可扩展性较高 书写麻烦)</p>

3、<p>第三种渲染页面的方式(推荐)</p>

 forms组件展示错误的信息提示

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注释 ,不指定的情况下,默认用的类中字段的首字母大写

 关键字:novalidate

  告诉前端取消掉自带的校验方式,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'邮箱'}))
自定义的验证函数方法

钩子函数(HOOK)

  就是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

 

forms组件其他字段及其他操作的使用

"""
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开头')],
    )

 

具体的展示:

 

forms组件的源码分析

  通过源码解析,看Django内部的forms组件是如何是实现校验的原理的;大致了解

 

 点击进去查看is_valid()方法

 

核心精髓实现功能的代码部分

Cookie与Session组件

  一、cookie:

  由于Http协议的无状态的特点,也就是说即使第一次和服务器连接后并且登录成功后,

第二次请求服务器依然不能知道当前请求是哪个用户,cookies的出现就是为了解决这一现象。

cooki就是保存在客户端浏览器上的键值对:

工作原理:当用户登录成功后,浏览器上会保存一些信息,下次再来访问的时候,就会携带着这些信息去访问服务端,服务端通过这些信息来识别出你的身份

  二、session:

  sessioncookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,

不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。

   session就是保存在服务器上的键值对
   session虽然是保存在服务器上的键值对
   但是它是依赖于cookie工作的

cookie就是保存在客户端浏览器上的键值对,点击查看已经登录了的Cookie,会自动保存一些信息

 

 

 查看cookie的方法

 

 如何操作cookie和session

  前提分析:Django返回给客户端浏览器的都必须是HttpResponse对象;

三板斧:

  return HttpResonse(): 返回字符串

  return render()   返回html页面

  return  redirect()  重定向到...

Django操作cookie

  通过对cookie的操作,让服务器如何存储数据信息,保存用户的登录状态,简单的写一个都登录注册的例子:

首先先掌握如何操作cookie:

获取cookie的方法

  request.COOKIE.get()

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

参数:

  • default: 默认值
  • salt: 加密盐
  • max_age: 后台控制过期时间

设置Cookie

   set_cookie

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

 

cookie登录版的校验

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的操作

  获取、设置、删除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所携带的表字段,其中的时间exprie_time采用的是UTC时间,相差个小时

Django默认的超时实际时间是14天

  重点掌握设置和获取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表中,保存的数据 可以在后端任意位置获取到

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

  

  

 

 

 

 

 

 

 

 

  

  

 

 

 

 

  

  

 

 

  

 

 

 

 

  

  

 

 

 



 

 

 

 

 

  

 

  

 

    

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-09-25 08:06  游走De提莫  阅读(276)  评论(0编辑  收藏  举报