django(forms组件、cookies与session)

一、forms组件

1.功能

  - 渲染页面

  - 校验数据

  - 展示信息

需要先写一个类,该类类似于models中的表类

from django import forms


class MyRegForm(forms.Form):
    # 用户名最少3位最多8位
    username = forms.CharField(max_length=8,min_length=3)
    password = forms.CharField(max_length=8,min_length=3)
    # email字段必须填写符合邮箱格式的数据
    email = forms.EmailField()

2.如何校验数据

# 1.传入待校验的数据  用自己写的类 传入字典格式的待校验的数据
form_obj = views.MyRegForm({'username':'jason','password':'12','email':'123456'})
# 2.判断数据是否符合校验规则
form_obj.is_valid()  # 该方法只有在所有的数据全部符合校验规则才会返回True
False
# 3.如何获取校验之后通过的数据
form_obj.cleaned_data
{'username': 'jason'}
# 4.如何获取校验失败及失败的原因
form_obj.errors
{
 'password': ['Ensure this value has at least 3 characters (it has 2).'],
 'email': ['Enter a valid email address.']
 }
# 5.注意 forms组件默认所有的字段都必须传值 也就意味着传少了是肯定不行的 而传多了则没有任何关系 只校验类里面写的字段 多传的直接忽略了
form_obj = views.MyRegForm({'username':'jason','password':'123456'})
form_obj.is_valid()
Out[12]: False
form_obj.errors
Out[18]: {'email': ['This field is required.']}


form_obj = views.MyRegForm({'username':'jason','password':'123456',"email":'123@qq.com',"hobby":'hahahaha'})
form_obj.is_valid()
Out[14]: True
form_obj.cleaned_data
Out[15]: {'username': 'jason', 'password': '123456', 'email': '123@qq.com'}
form_obj.errors
Out[16]: {}

数据校验一个前后端都得有,但是前端的校验弱不禁风,可有可无,而后端的校验则必须非常全面

取消校验功能

form表单取消前端浏览器自动校验功能
  <form action="" method="post" novalidate>

3.如何渲染页面

forms组件只帮你渲染获取用户输入(输入、选择、下拉、文件)的标签,不渲染按钮和form表单标签,渲染出来的每一个input提示信息都是类中字段首字母大写

<p>第一种渲染方式:多个p标签  本地测试方便 封装程度太高了 不便于扩展</p>
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<p>第二种渲染方式:  扩展性较高 书写较为繁琐</p>
<label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label>
{{ form_obj.username }}
{{ form_obj.password.label }}{{ form_obj.password }}
{{ form_obj.email.label }}{{ form_obj.email }}
<p>第三种渲染方式  推荐使用</p>
{% for form in form_obj %}
    <p> {{ form.label }}{{ form }}</p>
{% endfor %}

4.如何渲染错误信息

前端

<form action="" method="post" novalidate>
    {% for form in form_obj %}
    <p>
        {{ form.label }}{{ form }}
        <span>{{ form.errors.0 }}</span>  # 这个是模板语法 索引不会出现超出报错
    </p>
    {% endfor %}
    <input type="submit">
</form>

后端

def reg(request):
    # 1 先生成一个空的类对象
    form_obj = MyRegForm()
    if request.method == 'POST':
        # 3 获取用户数据并交给forms组件校验  request.POST
        form_obj = MyRegForm(request.POST)
        # 4 获取校验结果
        if form_obj.is_valid():
            return HttpResponse('数据没问题')
        else:
            # 5 获取校验失败的字段和提示信息
            print(form_obj.errors)

    # 2 直接将该对象传给前端页面
    return render(request,'reg.html',locals())

5.常用参数

label input的提示信息
error_messages 自定义报错的提示信息
required 设置字段是否允许为空
initial 设置默认值
widget 控制type类型及属性
  - widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})
  - widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})

6.钩子函数

作用:实现自定义的验证功能

全局钩子:

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

局部钩子:

def clean_username(self):
    username = self.cleaned_data.get('username')
    if '666' in username:
        self.add_error('username','光喊666是不行的')
    return username

小结:如果你想同时操作多个字段的数据你就用全局钩子,如果你想操作单个字段的数据你就用局部钩子

7.其他渲染方式

radioSelect

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="密码")
    gender = forms.fields.ChoiceField(
        choices=((1, ""), (2, ""), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

- 单选select

class LoginForm(forms.Form):
    ...
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )

- 多选select

class LoginForm(forms.Form):
    ...
    hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )

- 单选checkbox

class LoginForm(forms.Form):
    ...
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

多选checkbox

class LoginForm(forms.Form):
    ...
    hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )
Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型
Django Form内置字段

补充:

正则校验:

phone = forms.CharField(
    validators=[
        RegexValidator(r'^[0-9]+$', '请输入数字'),
        RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
    ]
)

例子:使用form组件实现注册功能

先定义好一个RegForm类:

from django import forms

# 按照Django form组件的要求自己写一个类
class RegForm(forms.Form):
    name = forms.CharField(label="用户名")
    pwd = forms.CharField(label="密码")

再写一个视图函数:

# 使用form组件实现注册方式
def register2(request):
    form_obj = RegForm()
    if request.method == "POST":
        # 实例化form对象的时候,把post提交过来的数据直接传进去
        form_obj = RegForm(request.POST)
        # 调用form_obj校验数据的方法
        if form_obj.is_valid():
            return HttpResponse("注册成功")
    return render(request, "register2.html", {"form_obj": form_obj})

前端页面:login2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册2</title>
</head>
<body>
    <form action="/reg2/" method="post" novalidate autocomplete="off">
        {% csrf_token %}
        <div>
            <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
            {{ form_obj.name }} {{ form_obj.name.errors.0 }}
        </div>
        <div>
            <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
            {{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
        </div>
        <div>
            <input type="submit" class="btn btn-success" value="注册">
        </div>
    </form>
</body>
</html>

 

二、cookie

1.什么是cookie?

Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。

原理:服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

简而言之就是指保存在客户端浏览器上的键值对

2.设置cookie

rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)

参数

  - key, 键

  - value='', 值

  - max_age=None, 超时时间

  - expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)

  - path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问

  - domain=None, Cookie生效的域名

  - secure=False, https传输

  - httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

3.删除cookie

def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
    return rep

例子:Cookie版登陆校验:

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

1.什么是session?

Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session

简而言之,Session就是保存在服务端上的键值对

2.设置session

request.session['key'] = value

实现该方法内部机制:

  - django内部会自动生成一个随机字符串

  - 去django_session表中存储数据,键就是随机字符串,值是要保存的数据

  - 将生成好的随机字符串返回给客户端浏览器,浏览器保存键值对

3.获取session

request.session.get('key')

实现该方法内部机制:

  - django会自动取浏览器的cookie查找sessionid键值对,获取随机字符串

  - 拿着该随机字符串取django_session表中比对数据

  - 如果比对上了,就将随机字符串对应的数据获取出来并封装到request.session供用户调用

4.设置会话Session和Cookie的超时时间

request.session.set_expiry(value)

  - 如果value是个整数,session会在些秒数后失效。
  -  如果value是个datatime或timedelta,session就会在这个时间后失效。
  - 如果value是0,用户关闭浏览器session就会失效。
  - 如果value是None,session会依赖全局session失效策略。

django中默认的session超时时间为14天

5.删除session

request.session.delete():删除当前会话的所有Session数据

request.session.flush():删除当前的会话数据并删除会话的Cookie,用于确保前面的会话数据不可以再次被用户的浏览器访问

补充:django中默认的session超时时间为14天

例子:Session版登陆验证

from functools import wraps


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()
        if request.session.get("user"):
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "alex" and pwd == "alex1234":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")


@check_login
def logout(request):
    # 删除所有当前请求相关的session
    request.session.delete()
    return redirect("/login/")


@check_login
def index(request):
    current_user = request.session.get("user", None)
    return render(request, "index.html", {"user": current_user})

Session版登录验证
Session版登陆验证
posted @ 2020-01-13 19:20  treeter  阅读(190)  评论(0编辑  收藏  举报