Django高级之forms组件

forms组件之校验字段

第一步:定义一个类,继承forms.Form
第二步:在类中写字段,要校验的字段,字段属性就是校验规则
第三步:实例化得到一个Form对象,把要校验的数据传入
第四步:调用register_form.is_valid()校验,校验通过就是True
第五步:校验通过有register_form.cleaned_data
第六步:校验不通过 register_form.errors
forms组件
    -数据校验
    -渲染页面
    -错误信息
    -局部全局钩子
    -使用步骤:
        -写一个类,继承Form类
        -写字段,字段参数(限制该字段的长短)
        -错误信息中文:字段参数
        -widget:控制生成标签的属性
        -视图函数中:
            -实例化得到form对象时,把要校验的数据传入
            -is_valid():clean_data和errors就有值了
            -如果校验通过就存,不通过就给页面提示
       -渲染页面
            -for循环的方式渲染页面(在标签前后可以再加标签)
   -局部钩子
       -def clean_字段名(self):
            -校验规则
            -如果通过,return 值
            -如果不通过,抛异常
   -全局钩子(多个字段校验)
        -def clean(self):
            -如果通过,return clean_data
            -如果不通过,抛异常
#定义类
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
class RegisterForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=3, label='用户名')
    password = forms.CharField(max_length=8, min_length=3, label='密码')
    re_password = forms.CharField(max_length=8, min_length=3, label='确认密码')
    email = forms.EmailField(label='邮箱')
   

#在视图中使用
register_form = RegisterForm(request.POST)
if register_form.is_valid():
      # 校验通过,存
      # 取出校验通过的数据
      print('校验通过')
      print(register_form.cleaned_data)
else:
     # 校验不通过
     print('校验不通过')
     print(register_form.errors)

forms组件之渲染标签

后端

def register(request):
    if request.method == 'GET':
        # 实例化生成一个空form对象
        register_form = RegisterForm()
        return render(request, 'register.html', {'form',register_form})

渲染一

<h2>通过form自动渲染一</h2>
<form action="" method="post">
    <p>用户名 {{ form.name }}</p>
    <p>密码 {{ form.password }}</p>
    <p>确认密码 {{ form.re_password }}</p>
    <p>邮箱 {{ form.email }}</p>
    <input type="submit" value="提交">

</form>

渲染 二

<h2>通过form自动渲染二(基本用这种)</h2>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% for item in form %}
                    <div class="form-group">
                        <p>{{ item.label }}{{ item }} <span style="color: red">{{ item.errors.0 }}</span></p>
                    </div>

                {% endfor %}
                <input type="submit" value="提交"><span style="color: red">{{ error }}</span>
            </form>
        </div>
    </div>
</div>

渲染 三

#自动渲染
<form action="" method="post">
    {{ form.as_p }}
    {#    {{ form.as_table }}#}
    {#    {{ form.as_ul }}#}
</form>

forms组件之渲染错误信息

前端

<h2>通过form自动渲染二(基本用这种)</h2>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% for item in form %}
                    <div class="form-group">
                        <p>{{ item.label }}{{ item }} <span style="color: red">{{ item.errors.0 }}</span></p>
                    </div>
                {% endfor %}
                <input type="submit" value="提交"><span style="color: red">{{ error }}</span>
            </form>
        </div>
    </div>
</div>

后端

class RegisterForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=3, label='用户名',
                           error_messages={'max_length': '最长为8', 'min_length': '最短为3'},
                           widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, label='密码',
                               error_messages={'required': '该字段必填'},
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱',
                             error_messages={'required': '必填', 'invalid': '必须是邮箱格式'},
                             widget=widgets.TextInput(attrs={'class': 'form-control'}))


def forms(request):
    if request.method == 'GET':
        register_from = RegisterForm()
        return render(request, 'forms.html', {'form': register_from})
    else:
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            # 校验通过,存
            # 取出校验通过的数据
            print('校验通过')
            print(register_form.cleaned_data)
            register_form.cleaned_data.pop('re_password')
            models.User.objects.create(**register_form.cleaned_data)

        else:
            # 校验不通过
            print('校验不通过')
            print(register_form.errors)
        return render(request, 'forms.html', {'form': register_form})

models数据库

class User(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    email = models.EmailField(null=True) #新建不需要null

forms组件全局钩子,局部钩子

后端

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError

class RegisterForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=3, label='用户名',
                           error_messages={'max_length': '最长为8', 'min_length': '最短为3'},
                           widget=widgets.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, label='密码',
                               error_messages={'required': '该字段必填'},
                               widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    re_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
                                  widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label='邮箱',
                             error_messages={'required': '必填', 'invalid': '必须是邮箱格式'},
                             widget=widgets.TextInput(attrs={'class':'form-control'}))
    def clean_name(self):#name字段的局部钩子
        # 校验名字不能以sb开头
        name=self.cleaned_data.get('name')
        if name.startswith('sb'):
            # 校验不通过,必须抛异常,
            raise ValidationError('不能以sb开头')
        else:#校验通过,返回name对应的值

            return name

    def clean(self): # 全局钩子
        password=self.cleaned_data.get('password')
        re_password=self.cleaned_data.get('re_password')
        if re_password==password:
            #校验通过
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')

    def clean(self): # 全局钩子
        password=self.cleaned_data.get('password')
        re_password=self.cleaned_data.get('re_password')
        if  not re_password==password:
            raise ValidationError('两次密码不一致')




from app01 import models


def register(request):
    if request.method == 'GET':
        # 生成一个空form对象
        register_form = RegisterForm()
        return render(request, 'register.html', {'form': register_form})

    else:
        # 实例化得到对象,传入要校验的数据
        # register_form=RegisterForm(data=request.POST)
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            # 校验通过,存
            # 取出校验通过的数据
            print('校验通过')
            print(register_form.cleaned_data)
            register_form.cleaned_data.pop('re_password')
            models.User.objects.create(**register_form.cleaned_data)
            return HttpResponse('ok')

        else:
            # 校验不通过
            print('校验不通过')
            print(register_form.errors)
            error=register_form.errors.get('__all__')[0]
            print(type(register_form.errors.as_json))
            from django.forms.utils import ErrorDict
            # name_error=register_form.errors.get('name')[0]
            return render(request, 'register.html', {'form': register_form,'error':error})

前端

<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% for item in form %}
                    <div class="form-group">
                        <p>{{ item.label }}{{ item }} <span style="color: red">{{ item.errors.0 }}</span></p>
                    </div>
                {% endfor %}
                <input type="submit" value="提交"><span style="color: red">{{ error }}</span>
            </form>
        </div>
    </div>
</div>

forms组件源码分析

1 为什么局部钩子要写成 clean_字段名,为什么要抛异常
2 入口在 is_valid()
3 校验流程
    -先校验字段自己的规则(最大,最小,是否必填,是不是合法)
    -校验局部钩子函数
    -全局钩子校验
    
    
4 流程
    -is_valid()---》return self.is_bound and not self.errors
    -self.errors:方法包装成了数据数据
        -一旦self._errors有值,就不进行校验了(之前调用过了)
    -self.full_clean():核心
        self._errors = ErrorDict()
        if not self.is_bound:  
            return
        self.cleaned_data = {}
        self._clean_fields()
        self._clean_form()
        self._post_clean()
        
        
    -self._clean_fields():核心代码,局部钩子执行位置
    
     value = field.clean(value)# 字段自己的校验规则
     self.cleaned_data[name] = value #把校验后数据放到cleaned_data
     if hasattr(self, 'clean_%s' % name): # 判断有没有局部钩子
        value = getattr(self, 'clean_%s' % name)() #执行局部钩子
        self.cleaned_data[name] = value #校验通过,把数据替换一下
    # 如果 校验不通过,会抛异常,会被捕获,捕获后执行
    self.add_error(name, e)
    
  - def _clean_form(self):#全局钩子执行位置
    def _clean_form(self):
        try:
            #如果自己定义的form类中写了clean,他就会执行
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
posted @ 2021-12-03 16:35  沈忻凯  阅读(58)  评论(0)    收藏  举报