day58_9_24多对多建表手动,form组件(判断类型),cookies和session

一。多对多建表关系之手动添加。

  1。全自动

  像之前讲过的一样,我们可以通过manytomanyField的字段来建立多对多关系:

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.纯手动。

  再多对都的创建中,也可以手动创建第三张表,然后使用外键连接到两个表之间。

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到该表中

扫描二维码关注公众号,回复: 7354080 查看本文章
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中获取。

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

  参数:

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

  其中还有删除方法:

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版登录
cookies登录校验

七。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表中,保存的数据 可以在后端任意位置获取到。

猜你喜欢

转载自www.cnblogs.com/LZXlzmmddtm/p/11581854.html