• Python Django 多对多三种创建方式 form组件 cookie和session


    一 表中多对多关系三种创建方式

    以Book表和Author表多对多关系例

    1.第一种方式:全自动(推荐使用):models.ManyToManyField(to='类名')
    优点:不需要你手动创建第三张表
    缺点:第三张表不是你手动创建的,字段是固定的无法拓展
    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)
    
    2.第二种方式:手动创建第三张表(了解即可)
    优点:第三张表可以任意拓展字段
    缺点:orm 查询不方便
    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)
    
    3.第三种方式:半自动(推荐使用******)
    优点:结合自动和手动优点
    ps:多对多字段不支持增删改查:add(),remove(),set(),clear()
    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字段来记录的 """ 多对多字段的 add set remove clear不支持 """ 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)

    form组件

    form组件的三大功能

      #1.数据检验  #2.渲染页面  #3.展示信息

    1.form组件的基本用法

    ##校验数据

    #1.首先在views.py中写一个基础了forms.Form的类(定义与模型表的定义相似)
    from django import forms
    from django.forms import widgets
    from app01 import models


    class MyRegForm(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, label='用户名',
    error_messages={
    'max_length': '用户名最大八位',
    'min_length': '用户名最小三位',
    'required': '用户名不能为空'
    }, widget=widgets.TextInput(attrs={'class': 'form-control'})
    )
     

    #pycharm自带form组件类测试及语法

     测试语法总结:

    from app01 import views
    
    1.将需要校验的数据 以字典的方式传递给自定义的类 实例化产生对象:字典传值产生对象
    form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'})
    
    2.如何查看数据是否全部合法:对象.is_valid()
    form_obj.is_valid()  # 只有所有的数据都符合要求 才会是True
                        
    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'}
    
    1.校验数据时只要有一个校验通过:对象.is_valid()结果就为false
    2. 数据校验时会把数据与form组件类中的字段括号内的条件进行对比错误的放进对errors中,正确的放进cleaned_data中
    3.数据多传不管少传对象.is_valid()结果就为false
    4.产生对象要以字典形式传值

    ##前端渲染页面的三种方式

    # 后端
    在一个视图函数中返回html页面并传入form组件类的事例化对象,
    form组件只帮你渲染获取用户输入的标签,不会帮你渲染提交按钮,需要手动添加
    # 前端
    1.第一种方式(下面分别为p标签或ul 和table标签包裹输入框,对象中有几个form字段就有几个inpu框)
    <form action="">
        <p>第一种渲染页面的方式(封装程度太高 一般只用于本地测试  通常不适用)</p>
        {{ form_obj.as_p }}  
        {{ form_obj.as_ul }}
        {{ form_obj.as_table }}
    </form>
    2.第二种方式:{{ form_obj.username.label }}为注释
    <form action="">
        <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>
    </form>
    3.第三种方式:推荐(一个foo相当于一个input框)
    <form action="">
        <p>第三种渲染页面的方式(推荐)</p>
        {% for foo in form_obj %}
            <p>{{ foo.label }}{{ foo }}</p>
        {% endfor %}
    </form>
    4.前端取消校验:novalidate
    <form action="" method="post" novalidate>
       </form>

    ##注意

    注意事项
    1.forms组件在帮你渲染页面的时候 只会渲染获取用户输入的标签  提交按钮需要你手动添加                
    2.input框的label注释  不指定的情况下 默认用的类中字段的首字母大写            
    3.
    校验数据的时候可以前后端都校验 做一个双重的校验
    但是前端的校验可有可无 而后端的校验则必须要有,因为前端的校验可以通过爬虫直接避开
    前端取消浏览器校验功能
    form标签指定novalidate属性即可
    <form action="" method='post' novalidate></form>
    4.
    form组件提交数据如果数据不合法,页面上会保留之前用户输入的信息
    在使用form组件对模型表进行数据校验的时候,只需要保证字段一致
    那么在创建的对象的时候你就直接**form_obj.cleaned_data
    例:后端
    def reg(request):
        # 生成一个空对象
        form_obj = MyForm()
        if request.method == 'POST':
            print(request.POST)
            form_obj = MyForm(request.POST)
            if form_obj.is_valid():
                print(form_obj.cleaned_data)
                models.User.objects.create(**form_obj.cleaned_data)
        return render(request,'reg.html',locals())

    ##展示错误信息

    前端:{{ foo.errors.0 }}
    
    <form action="" method='post' novalidate>
        {% for foo in form_obj %}
            <p>{{ foo.label }}:{{ foo }}
            <span>{{ foo.errors.0 }}</span>
            </p>
        {% endfor %}
    </form>
    
    后端:form组件类中:error_messages={k1:v1,k2:v2,...}
    password = forms.CharField(max_length=8,min_length=5,label='密码',error_messages={'max_length':'密码最大八位','min_length':'密码最小五位','required':'密码不能为空'})

    2.钩子函数

    作用:相当于对数据的二次校验,编写在form组件的类中

    局部钩子函数:作用于一个字段

     局部钩子(针对某一个字段做额外的校验)   校验用户名中不能包含666 一旦包含 提示
       def clean_username(self):
            username = self.cleaned_data.get('username')
            if '666' in username:
                # raise ValidationError('奥术大师就卡的凯撒就肯定会')
                self.add_error('username','光喊666是不行的 你得自己上')
            return username

    全局钩子函数:作用于多个字段

     # 全局钩子(针对多个字段做额外的校验)    校验用户两次密码是否一致
        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

    3.form组件类中常用字段和插件

    1.初始值,input框里面的初始值:initial
    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用户名",
            initial="张三"  # 设置默认值
        )
        pwd = forms.CharField(min_length=6, label="密码")
    
    2.重写错误信息:error_messages
    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用户名",
            initial="张三",
            error_messages={
                "required": "不能为空",
                "invalid": "格式错误",
                "min_length": "用户名最短8位"
            }
        )
        pwd = forms.CharField(min_length=6, label="密码")
    
    3. 是否必填:required 
    
    4.控制标签属性和样式:密码为例,括号中可填样式
    from django.forms import widgets
    # 类中字段括号内书写
    widget=widgets.PasswordInput()
    widget=widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
    
    5.password
    widget=widgets.PasswordInput()
    widget=widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
    
    6.正则
    from django.core.validators import RegexValidator
    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必须是邮箱格式
    
    7.
    # 单选的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开头')],
        )
    View Code

    cookie和session

    产生背景:由于http协议是无状态的 无法记录用户状态 

    cookie:

    cookie就是保存在客户端浏览器上的键值对
    工作原理:当你登陆成功之后 浏览器上会保存一些信息
    下次再访问的时候 就会带着这些信息去访问服务端  服务端通过这些信息来识别出你的身份            
    cookie虽然是写在客户端浏览器上的  但是是服务端设置的 浏览器可以选择不服从命令 禁止写cookie

    session

    session就是保存在服务器上的键值对
    session虽然是保存在服务器上的键值对
    但是它是依赖于cookie工作的
                
    服务端返回给浏览器一个随机的字符串
    浏览器以键值对的形式保存
    sessionid:随机字符串
                
    浏览器在访问服务端的时候  就会将随机字符串携带上
    后端获取随机串与后端的记录的做比对
    随机字符串1:数据1
    随机字符串2:数据2

    1.Cookie的使用

    django返回给客户端浏览器的都必须是HttpResponse对象
            return HttpResponse()
            return render()
            return redirect()
    obj1 = HttpResponse()
    return obj1
    obj2 = render()
    return obj2
    obj3 = redirect()
    return obj3
    
    基本语法
    1.设置cookie利用的就是HttpResponse对象:对象.set_cookie('k1','v1')
       obj1.set_cookie('k1','v1')
    2获取cookie
       request.COOKIE.get()
    3.删除cookie
      obj1.delete_cookie("k1")
    例:
    def logout(request):
        rep = redirect("/login/")
        rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
        return rep
    4.
    设置超时时间
    max_age=None, 超时时间
    expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)

    # 装饰器

    
    
    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()
    if request.COOKIES.get('name'):
    res = func(request,*args,**kwargs)
    return res
    else:
    return redirect('/lg/?next=%s'%target_url)
    return inner
    
    
    def lg(request):
    if request.method == 'POST':
    username = request.POST.get('username')
    password = request.POST.get('password')
    if username == 'jason' 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:jason
    return obj
    return render(request,'lg.html')
     

    2.Session的使用

    # 登录
    def login(request):
        if request.method == 'POST':
            name = request.POST.get('name')
            pwd = request.POST.get('pwd')
            if name == 'jason' and pwd == '123':
                request.session['name'] = 'jason'
                targer_url = request.GET.get('next')
                if targer_url:
                    return redirect(targer_url)
                else:
    
    
    
                   return HttpResponse('ok')
        return render(request,'login.html')
    
    
    # 登录装饰器
    def login_auth(func):
        @wraps(func)
        def inner(request,*args,**kwargs):
            targe_url = request.get_full_path()
            if request.session.get('name'):
                res = func(request,*args,**kwargs)
                return res
            else:
                return redirect('/login/?next=%s' % targe_url)
        return inner
    1.设置session:request.session[k] = v
     request.session['name'] = 'jason'
    """
    上面这一句话发生了三件事
      1.django 内部自动生成一个随机字符串
      2.将随机字符串和你要保存的数据 写入django_session表中(现在内存中生成一个缓存记录 等到经过中间件的时候才会执行)
      3.将产生的随机字符串发送给浏览器写入cookie
       sessionid:随机字符串
                """
    2.获取session:request.session.get(k)
      request.session.get('name')

    获取session
    request.session.get('name')
    """
    上面这一句话发生了三件事
    1.django内部会自动从请求信息中获取到随机字符串
    2.拿着随机字符串去django_session表中比对
    3.一旦对应上了就将对应的数据解析出来放到request.session中

    """

    3.删除
    # 删除当前会话的所有Session数据
       request.session.delete(k)  # 删除的是浏览器的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失效策略。
  • 相关阅读:
    回发或回调参数无效。下拉菜单中使用ajax,联动菜单引起的问题解决方案
    jquery获取,赋值img的src值..
    jquery select set selectindex
    DateTime.ToString("dd/MM/yyyy");后,不能直接Convert.ToDateTime的解决:
    SQL Server根据查询结果,生成XML文件
    SQL生成XML
    SQLXML
    .net 点击刷新验证码问题
    C# 根据字节数截取字符串(区别汉字,英文,数字)
    c#中对xml文件的基本操作
  • 原文地址:https://www.cnblogs.com/tfzz/p/11580892.html
Copyright © 2020-2023  润新知