forms钩子函数,字段参数和类型,源码分析,ModelForm和cookie与session

forms组件钩子函数


钩子函数的含义其实就是在程序的执行过程中穿插额外的逻辑,比如说在判断用户名是否符合格式的同时校验用户名是否已存在,校验校验密码和确认密码是否一致等。

校验用户名是否已存在,钩子函数之局部钩子(校验单个字段)。

# 局部钩子:校验用户名是否已存在(一次性只能勾一个人)
'''钩子函数是数据经过了字段第一层参数校验之后才会执行'''
def clean_name(self):  # 自动生成的函数名 专门用于对name字段添加额外的校验规则
        # 1.先获取用户名
        name = self.cleaned_data.get('name')
        # 2.判断用户名是否已存在
        is_exist = models.User.objects.filter(name=name)
        if is_exist:
            # 3.提示信息
            self.add_error('name', '用户名已存在')
        # 4.最后将你勾上来的name返回回去
        return name

校验校验密码和确认密码是否一致,钩子函数之全局钩子(校验多个字段)。

# 全局钩子:校验密码与确认密码是否一致(一次性可以勾多个人)
def clean(self):
        # 1.获取多个字段数据
        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组件字段参数


参数 说明
min_length 最小长度
max_length 最大长度
label 字段名称
error_messages 错误提示
min_value 最小值
max_value 最大值
initial 默认值
validators 正则校验器
widget 控制渲染出来的标签各项属性
choices 选择类型的标签内部对应关系

validators案例

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

widget案例

#  forms.widgets.控制type的类型(attrs=控制各项属性:class id ...)
password = forms.CharField(                          widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)

choices案例

hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    )
def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        self.fields['hobby'].choices = models.Classes.objects.all().values_list('id','caption')

forms组件字段类型


password---密码

 pwd = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )

radioSelect--单选框

gender = forms.fields.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

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()
    )

forms组件字段类型了解即可,其他的可以参考Django Form表单组件 - JasonJi - 博客园 (cnblogs.com)

forms组件源码分析


我们从is_valid如何校验数据进行分析。

第一步:查看类源码发现只要给类传参self.is_bound肯定是True,重点看self.errors

def is_valid(self):
    return self.is_bound and not self.errors

第二步:查看了源码发现self._errors初始化就是None 所以肯定看full_clean方法

@property
def errors(self):
   	if self._errors is None:
       self.full_clean()
    return self._errors

第三步:查看full_clean方法,发现主要有3个方法。

def full_clean(self):
        self.cleaned_data = {}  # 创建了一个存储检查没问题的数据的空字典
        self._clean_fields()  # 校验数据
        self._clean_form()  # 封装
        self._post_clean()  # 返回

第四步:查看self._clean_fields方法

def _clean_fields(self):
        for name, field in self.fields.items():# 循环获取字段名和字段对象
            if field.disabled:  # 字段是否是禁用的
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))  # 如果不是禁用,获取字段对应的用户数据,进行校验
            try:  # 异常捕获
                if isinstance(field, FileField): # 文件数据
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)  # clean校验的方法
                self.cleaned_data[name] = value  # 以上校验没有问题,就上传到cleaned_data里
                if hasattr(self, 'clean_%s' % name): # 利用反射判断是否拥有clean_%s的方法,就是在获取钩子函数执行,
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value # 钩子函数不报错,添加到cleaned_data里
            except ValidationError as e:  # 钩子函数报错,添加提示保存信息
                self.add_error(name, e)

查看源码发现校验数据的整个过程内部都有异常处理机制

from django.core.exceptions import ValidationError
raise ValidationError('用户名不存在 你个DSB')
# 自己在clean_%s钩子函数主动抛出一个异常,也不会报错,因为钩子函数是交给_clean_fields这个方法里执行的,就算clean_%s报错也是交给了_clean_fields方法里的异常捕获处理。

ModelForm简介


forms组件主要配合models里面的默写类一起使用,但是默写类里面的字段需要在forms类中相当于重写一遍, 代码冗余。为了更好的结合forms与models的关系,有了一个ModelForm(基于forms组件)

class BookForm(forms.ModelForm):

    class Meta:
        model = models.Book
        fields = "__all__"
        labels = {
            "title": "书名",
            "price": "价格"
        }
        widgets = {
            "password": forms.widgets.PasswordInput(attrs={"class": "c1"}),
        }

class Meta下常用参数

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息

save()方法

每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例:

from myapp.models import Book
from myapp.forms import BookForm

# 根据POST数据创建一个新的form对象
form_obj = BookForm(request.POST)

# 创建书籍对象
new_ book = form_obj.save()

# 基于一个书籍对象创建form对象
edit_obj = Book.objects.get(id=1)
# 使用POST提交的数据更新书籍对象
form_obj = BookForm(request.POST, instance=edit_obj)
form_obj.save()

cookie与session简介


Cookie的由来

大家都知道HTTP协议是无状态的。

无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

什么是Cookie,原理是什么?

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

cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。cookie本质:指代服务端让客户端保存的数据(存储在客户端上与用户信息相关的数据)

ps:cookie就是存在客户端的东西

session的由来

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

session的原理

用户登录成功之后,服务端生成一个随机字符串可以称为id,可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时携带id,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。session本质:指代服务端保存的跟用户信息相关的数据。

ps:session就是存在服务端的东西。

session和cookie的关系

session的工作必须依赖于cookie。

django操作cookie


视图函数返回值都是需要一个HttpResonse,我们想要操作cookie,就不能直接返回对象,而是先用变量名指代,后操作对象方法。

res = HttpResonse()		
return res
res = render(),
return res
res = redirect()
return res

基本使用

res.set_cookie()  # 设置
res.COOKIE.get()  # 获取

有很多视图函数需要添加登录认证,有时候又需要取消登录认证,我们就可以使用装饰器。例如下面

def login_auth(func_name):
    def inner(request, *args, **kwargs):
        if request.COOKIES.get('name'):
            res = func_name(request, *args, **kwargs)
            return res
        else:
            return redirect('/login/')
    return inner
posted @ 2022-05-23 22:39  早安_1207  阅读(157)  评论(0)    收藏  举报
返回顶端