博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django之Form组件(***)
阅读量:5044 次
发布时间:2019-06-12

本文共 22042 字,大约阅读时间需要 73 分钟。

Django之Form组件

  Form组件,用来做一些数据的提前验证,比如登录注册中,我们定义的是邮箱登录,但是用户却手机号登录,那么用Form组件去实现这个验证过程的话,数据的判断逻辑就不会去数据库匹配啦,这样反而会减轻数据库的压力。

  所以,Form组件的功能是对用户的请求数据库做验证的。并且对获取的数据也可以做验证功能。  

  以往的登录注册中的问题:

    在以往的登录注册页面中,如果有输入错误,页面是重新刷新的,所以之前输入对的数据也会随之消失。

    重复进行用户数据的校验:正则,长度,是否为空等等。

    Form组件的解决方法:

    先导入模块:  

from django.forms import Formfrom django.forms import fields 

  后端:

from django.forms import Formfrom django.forms import fieldsclass LoginForm(Form):     “““     字段的名称必须与前端的字典名称一致。     ”””    username = fields.CharField(          max_length=18,        min_length=6,        required=True,        error_messages={            "required":"用户名不能为空",            "min_length":"长度不能小于6位",            "max_length":"长度不能大于18位",        }    )    password = fields.CharField(        min_length=16,        required=True,        error_messages={            "required": "密码不能为空",            "min_length": "长度不能小于16位",        }    )def login(request):    if request.method == "GET":        return render(request,"login.html")    else:        obj = LoginForm(request.POST)        ret = obj.is_valid()  #内部自动校验,ret是False和True的结果        if ret:            #用户输入格式正确            print(obj.cleaned_data)  #cleaned_data 是一个字典类型,是校验成功后拿到的值。            return redirect('http://www.baidu.com')        else:            #用户输入格式错误            # print(obj.errors)  #所有的错误信息,这个错误信息是一个对象,是 __str__ 方法            # print(obj.errors["username"])            # print(obj.errors["username"][0]) #在LoginForm类中定义了多个条件,那么就可能有多个错误信息,所有可以用索引取第一个            # print(obj.errors["password"])            # print(obj.errors["password"][0])            return render(request,"login.html",{
"obj":obj})

  前端:

    
Title

登录

{
% csrf_token %}

username:{

{ obj.errors.username.0 }}

password:{

{ obj.errors.password.0 }}

   到目前为止,Form组件的功能是对用户提交的数据进行校验和保留上次输入的内容。

   对于前端用户提交的方式,有两种,一个是Form提交,一种是Ajax提交。 

   其中Form提交是刷新,并失去上次的输入内容。

   而Ajax是不刷新,保留上次输入的内容。

   Form的验证流程:

    当执行is_valid的时候,Form的验证原理是:

      1. 获取当期类中所有的字段,也就是LoginForm类中,我们定义的字段。  

             也就是当每次对LoginForm实例化的时候,会将LoginForm类中的字段放到self.fields中。这个self.fields 类似一个字典。

       self.fields = {

        "username":fields.CharField(required=Ture),  #这个对象包含了这个正则表达式。

        "password":fields.CharField(required=Ture),  #这个对象包含了这个正则表达式。

       }

      2. 会循环self.fields,被循环的个数是由LoginForm来定的,因为self.fields循环的就是LoginForm的字段的个数。

        for k,v in self.fields.items():

            k:k是每次循环的username或password,它是字符串类型。

          v:v是对应的正则表达式。

          input_value = request.POST.get(k):这个表达式的意思是POST里有很多数据。

                           这里的k是字段的名字,如何写username,那么就会去找username的数据。

          v的正则表达式的值和input_value的值进行校验。

        所以在for循环之前,先定义一个flag=Ture.

        当v的正则和input_value校验不匹配时,返回False的值。

flag = Turefor k,v in self.fields.items():    input_value = request.POST.get(k)    flag = Falsereturn flag

  以上的Form流程就是is_valid的原理。

    is_valid 是True的话,获取cleaned_data的数据。

   Form组件和Ajax的提交验证:

    用Ajax的形式将前端的数据发送给后台:

    前端代码:  

    
Title

用户登录

{
% csrf_token %}

username:{

{ obj.errors.username.0 }}

password:{

{ obj.errors.password.0 }}

提交

    后端代码:

def ajax_login(request):    obj = LoginForm(request.POST)    if obj.is_valid():        print(obj.cleaned_data)    else:        print(obj.errors)    return HttpResponse("...")

    这样就算是提交错误了,页面是不会刷新,并且会保留之前输入的数据。

    但这里我们实现代码会发现一个问题,那就是在错误提交数据后,也是不刷新啦,前端输入的值后端也拿到了,但是但是,有个问题点,就是前端的页面并没有错误提示呀。

    所以,这个ajax的提交并不是那么的那么的完美,所以,我们接下来,就来完善一下这个ajax提交的错误提示。

  Form组件和Ajax提交验证的显示错误信息:

    这里用到了json.dumps的序列化操作,序列化的是obj.errors的对象。

    前端代码:

    
Title

用户登录

{
% csrf_token %}

username:{

{ obj.errors.username.0 }}

password:{

{ obj.errors.password.0 }}

提交

    后端代码:

def ajax_login(request):    import json    ret = {
'status': True,'msg': None} obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: # print(obj.errors) # obj.errors对象 ret['status'] = False ret['msg'] = obj.errors v = json.dumps(ret) return HttpResponse(v)

   这样就可以实现ajax提交有错误提示啦。

 

  Form组件的常见字段和参数:

    在XXXForm类中的字段:

    数字类型:

#数字类型number = fields.IntegerField(     min_value=10,   #这里的min和max不是单纯的数字长度,而是数字范围。     max_value=1000, #有错误信息    error_messages={        "required":"number不能为空",        "invalid":"number格式错误,必须是数字",  #invalid 格式错误         "min_value":"数值必须大于10",         "max_value":"数值必须小于1000",    })    

    邮件类型:

#邮件类型Email = fields.EmailField(    #有错误信息    error_messages={        "required":"Email不能为空",        "invalid":"Email格式错误,必须是邮件格式",  #invalid 格式错误    })

    还有对URL的fields.URLField。

    还有fields.SlugField 和fields.GenericIPAddressField、fields.DateField、fields.DateTimeField。

    但这些内置的也会有不完善的地方,无法达到我们的需求。所以,还有fields.RegesField。

    可以用这个fields.RegesField去写正则表达式。  

test = fields.RegesField('185\d+')

    fields.RegesField是继承CharField的。

    而CharField也是继承的Field,所以,我们来看一下Field的里的参数:

  Form组件的Field类的参数:

    widget 是用来指定生成什么样的HTML标签。比如select,text,input。

    但用widget要先导入:from django.forms import widgets

 

     label:写什么就在前端页面显示什么,在前端的写法是:   

{
{ obj.字段名.label }}  好比:    {
{ obj.t1.label }}

    initial:初始值。

    在input框中显示默认值用的。

    help_text:提供帮助信息:

在Form的字段中定义help_text:    help_text='.......'在前端代码和label一样:{
{ obj.t1.help_text }}这样就可以在前端显示:.......啦

    validators:自定义验证规则

validators=[ ]

    localize=False:是否支持本地化,这是用来转化时间的。

    disabled=False:是否可以编辑。

    label_suffix=None:label内容后缀。

    上述这里参数,处理validators以外。其余的一起用,是可以自动生成HTML标签的。

    这里就实现一个用后端参数实现一个前端标签的代码:

    后端:

class TestForm(Form):    t1 = fields.CharField(        required=True,        label="usename",        label_suffix=":",        help_text="输入username",        disabled=False,        initial="username",        max_length=8,        min_length=2,        error_messages={            "required":"不能为空",            "max_length":"long",            "min_length":"短",        }    )def login(request):    if request.method =="POST":        obj = TestForm()        return render(request,"login.html",{
"obj":obj}) else: obj = TestForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,"login.html",{
"obj":obj})

    前端:

    
Title

用户登录

{
% csrf_token %}

{

{ obj.t1.label }}{
{ obj.t1.label_suffix }} {
{ obj.t1 }}{
{ obj.t1.help_text }}

    以上就是用后端代码实现前端的标签的。

  Form小总结:

    form有验证的功能:  

      1.类:

        字段 = 正则

      2.is_valid()

    form有生成HTML标签的功能:  

        1.类:

        字段 = 正则( 正则这里规定一些生成HTML标签的特性)

      2.is_valid()

  Form组件保留上次输入的内容:

    前端:  

    
{
% csrf_token %}

user: {

{ obj.user }} {
{ obj.errors.user.0 }}

email: {

{ obj.email }} {
{ obj.errors.email.0 }}

password: {

{ obj.password }} {
{ obj.errors.password.0 }}

phone: {

{ obj.phone }} {
{ obj.errors.phone.0 }}

    后端:

class RegiterForm(Form):    user = fields.CharField(min_length=8)    email = fields.EmailField()    password = fields.CharField()    phone = fields.RegexField('139\d+')def register(request):    if request.method == 'GET':        obj = RegiterForm()        return render(request,'register.html',{
'obj':obj}) #这个obj里是没有值的,因为是第一次的请求 else: obj = RegiterForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'register.html',{
'obj':obj}) #这里的obj有值,值是input输入的值。

   这样输入的值就可以保留。

   Form组件小示例班级学生老师管理:

     示例中,应用到的Form特性,以后在任何的提交数据和编辑数据中,都应该应用Form组件去完成,因为我们是不信任提交的数据的。

       所有,需要用Form去做验证。

      后端views代码:    

from django.shortcuts import render,redirectfrom app01 import modelsfrom django.forms import Formfrom django.forms import fieldsfrom django.forms import widgetsclass ClassForm(Form):    title = fields.RegexField('全栈\d+')def class_list(request):    cls_list = models.Classes.objects.all()    return render(request,'class_list.html',{
'cls_list':cls_list})def add_class(request): if request.method == "GET": obj = ClassForm() return render(request,'add_class.html',{
'obj': obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): # obj.cleaned_data # 字典 # 数据库创建一条数据 # print(obj.cleaned_data) # models.Classes.objects.create(title=obj.cleaned_data['tt']) models.Classes.objects.create(**obj.cleaned_data) return redirect('/class_list/') return render(request,'add_class.html',{
'obj': obj})def edit_class(request,nid): if request.method == "GET": row = models.Classes.objects.filter(id=nid).first() # 让页面显示初始值 # obj = ClassForm(data={'title': 'asdfasdfasdfas'}) obj = ClassForm(initial={
'title': row.title}) return render(request,'edit_class.html',{
'nid': nid,'obj':obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): models.Classes.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/class_list/') return render(request,'edit_class.html',{
'nid': nid,'obj':obj})class StudentForm(Form): name = fields.CharField( min_length=2, max_length=6, widget=widgets.TextInput(attrs={
'class': 'form-control'}) ) email = fields.EmailField(widget=widgets.TextInput(attrs={
'class': 'form-control'})) age = fields.IntegerField(min_value=18,max_value=25,widget=widgets.TextInput(attrs={
'class': 'form-control'})) cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={
'class': 'form-control'}) )def student_list(request): stu_list = models.Student.objects.all() return render(request,'student_list.html',{
'stu_list':stu_list})def add_student(request): if request.method == "GET": obj = StudentForm() return render(request,'add_student.html',{
'obj':obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Student.objects.create(**obj.cleaned_data) return redirect('/student_list/') return render(request,'add_student.html',{
'obj':obj})def edit_student(request,nid): if request.method == "GET": row = models.Student.objects.filter(id=nid).values('name','email','age','cls_id').first() obj = StudentForm(initial=row) return render(request,'edit_student.html',{
'nid':nid,'obj': obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Student.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/student_list/') return render(request,'edit_student.html',{
'nid':nid,'obj': obj})
View Code

      后端models代码:

from django.db import modelsclass Classes(models.Model):    title = models.CharField(max_length=32)class Student(models.Model):    name = models.CharField(max_length=32)    email = models.CharField(max_length=32)    age = models.IntegerField(max_length=32)    cls = models.ForeignKey('Classes')class Teacher(models.Model):    tname = models.CharField(max_length=32)    c2t = models.ManyToManyField('Classes')
View Code

     URL:

from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^class_list/', views.class_list),    url(r'^add_class/', views.add_class),    url(r'^edit_class/(\d+)/', views.edit_class),    url(r'^student_list/', views.student_list),    url(r'^add_student/', views.add_student),    url(r'^edit_student/(\d+)/', views.edit_student),]
View Code

     前端class_list 代码:

    

班级列表

    {
    % for row in cls_list %}
  • {
    { row.title }} 编辑
  • {
    % endfor %}
View Code

    前端add_class 代码:

    

添加班级

{
% csrf_token %} {
{ obj.title }} {
{ obj.errors.title.0 }}
View Code

    前端edit_class 代码:

    

编辑班级

{
% csrf_token %}

{

{ obj.title }} {
{ obj.errors.title.0 }}

View Code

    前端student_list 代码:

    

学生列表

添加
    {
    % for row in stu_list %}
  • {
    { row.name }}-{
    { row.email }}-{
    { row.age }}-{
    { row.cls_id }}-{
    { row.cls.title }} 编辑
  • {
    % endfor %}
View Code

    前端add_student 代码:

    

添加学生

{
% csrf_token %}

{

{ obj.name }}

{

{ obj.email }}

{

{ obj.age }}

{

{ obj.cls_id }}

View Code

    前端edit_student 代码:

    
{
% csrf_token %}
{
{ obj.name }}
{
{ obj.email }}
{
{ obj.age }}
{
{ obj.cls_id }}
View Code

      老师多对多:

    注意:

Select框:                单选                    cls_id = fields.IntegerField(                        # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])                        widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={
'class': 'form-control'}) ) cls_id = fields.ChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.Select(attrs={
'class': 'form-control'}) ) obj = FooForm({
'cls_id':1}) 多选 xx = fields.MultipleChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple ) obj = FooForm({
'cls_id':[1,2,3]})

    老师的models 代码:

from django.db import modelsclass Classes(models.Model):    title = models.CharField(max_length=32)    def __str__(self):        return self.titleclass Student(models.Model):    name = models.CharField(max_length=32)    email = models.CharField(max_length=32)    age = models.IntegerField()    cls = models.ForeignKey('Classes')class Teacher(models.Model):    tname = models.CharField(max_length=32)    """    10    """    c2t = models.ManyToManyField('Classes')
View Code

    老师的view后端代码:

def teacher_list(request):    tea_list = models.Teacher.objects.all()    return render(request,'teacher_list.html',{
'tea_list':tea_list})from django.forms import models as form_modelclass TeacherForm(Form): tname = fields.CharField(min_length=2) # xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all()) # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all()) xx = fields.MultipleChoiceField( # choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple ) def __init__(self,*args,**kwargs): super(TeacherForm,self).__init__(*args,**kwargs) self.fields['xx'].choices = models.Classes.objects.values_list('id','title')# obj = TeacherForm()# 1. 找到所有字段# 2. self.fields = {
# tname: fields.CharField(min_length=2)# }def add_teacher(request): if request.method == "GET": obj = TeacherForm() return render(request,'add_teacher.html',{
'obj':obj}) else: obj = TeacherForm(request.POST) if obj.is_valid(): xx = obj.cleaned_data.pop('xx') row = models.Teacher.objects.create(**obj.cleaned_data) row.c2t.add(*xx) # [1,2] return redirect('/teacher_list/') return render(request,'add_teacher.html',{
'obj':obj})def edit_teacher(request,nid): if request.method == "GET": row = models.Teacher.objects.filter(id=nid).first() class_ids = row.c2t.values_list('id') # print(class_ids) # id_list = [] id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else [] # obj = TeacherForm(initial={'tname':row.tname,'xx':[1,2,3]}) obj = TeacherForm(initial={
'tname':row.tname,'xx':id_list}) return render(request,'edit_teacher.html',{
'obj':obj})
View Code

    老师的teacher_list 前端代码:

    

老师列表

{
% for row in tea_list %}
{
% endfor %}
ID 老师姓名 任教班级 编辑
{
{ row.id }}
{
{ row.tname }}
{
% for item in row.c2t.all %} {
{ item }}
{
% endfor %}
编辑
View Code

    老师的add_teacher 前端代码:  

    
{
% csrf_token %}

姓名:{

{ obj.tname }}

班级:{

{ obj.xx }}

View Code

    老师的edit_teacher 前端代码:

    

编辑老师

{
{ obj.tname }} {
{ obj.xx }}
View Code

   Form组件的常用组件:

    CheckBox复选框的应用:

    test后端:

class TestForm(Form):      t1 = fields.CharField(          widget=widgets.Textarea(attrs={})      )      t2 = fields.CharField(   #单选的          widget=widgets.CheckboxInput      )       t3 = fields.MultipleChoiceField(   #多选          choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')],          widget=widgets.CheckboxSelectMultiple      )      t4 = fields.ChoiceField(  #单选,选择互斥          choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')],          widget=widgets.RadioSelect      )      t5 = fields.FileField(   #上传文件          widget=widgets.FileInput      )

 

def test(request):    obj = TestForm(initial={
't3':[2,3]}) #默认选中 obj.is_valid() return render(request,'test.html',{
'obj':obj})

    test前端:

    
{
{ obj.t2 }} {
{ obj.t3 }} {
{ obj.t4 }} {
{ obj.t5 }}

   Form验证执行流程和钩子:

    is_valid方法的内部流程:

       is_valid的里面有一个return的返回,返回的有self.is_bound 和 self.errors。

        这个self.is_bound是只有True和False两个值,在初始化的时候,会进行校验,这个校验的值就是True 和 False。

        在self.errors里面有一个self.full_clean方法,这个方法就是去做验证的。这个验证就是循环Form自己的字段,

        然后去提交的数据进行校验。

          在self.full_clean里面,我们可以看到有cleaned_data,这就是我们的校验数据,这是一个字典类型。

          最后是clean_fields处理data的数据。      

self._clean_fields()  #做cleaned_data数据处理用self._clean_form()   #做钩子用self._post_clean()

    钩子:是自己定义的,当定义的钩子函数被调用是当每一个字段,自己的正则表达式,自己的函数后执行完,才会执行这个钩子函数。

       如果想用钩子函数,那么cleaned_data里是已经有值的啦。       

    样例:   

from django.core.exceptions import ValidationErrorclass TestForm(Form):    user = fields.CharField(validators=[])    pwd = fields.CharField()    def clean_user(self):        v = self.cleaned_data['user']        if models.Student.objects.filter(name=v).count():            raise ValidationError('用户名已经存在')        return self.cleaned_data['user']    def clean_pwd(self):        return self.cleaned_data['pwd']    def clean(self):   #钩子        # user = self.cleaned_data.get('user')        # email = self.cleaned_data.get('email')        # if models.Student.objects.filter(user=user,email=email).count():        #     raise ValidationError('用户名和邮箱联合已经存在')        return self.cleaned_data
# def _post_clean(self):    #     """    #     An internal hook for performing additional cleaning after form cleaning    #     is complete. Used for model validation in model forms.    #     """    #     pass

   Form扩展:

      字段 = 默认的正则表达式。

      在默认的正则中,可以在加额外的正则,用validators=[]

    clean_字段 = 必须有返回值。

    clean():有返回值,用定义的返回值 cleaned_data = 返回值。

        没有返回值,用原来的值,cleaned_data = 原有的值。

 

    Form的验证流程:

      1. 写一个Form,用户提供大量的验证数据,先一个一个字段的去获取。

      2. 拿一个字段,做自己的正则,当自己的正则执行完,就执行自己的函数。

      3. 所有的字段执行完自己函数后,执行clean()方法。

   Form组件总结:    

    1. 使用

      class Foo:
        xx = xxxxxx() # 正则,插件
        def clean_xx():
          ...
        def clean():
          pass
    2. 页面展示
      obj = Foo()

      # 灵活

      <form>
        {
{obj.xx}}
        {
{obj.xx}}
        {
{obj.xx}}
      </form>

      # 简单

      {
{obj.as_p}}
      <ul>
        {
{obj.as_ul}}
      </ul>
      <table>
        {
{obj.as_table}}
      </table>

    3. 后台

      is_valid()
      clean_data
      errors

 

   参考blog:XXXXXXXX6144178.html

------- END -------

转载于:https://www.cnblogs.com/george92/p/11441796.html

你可能感兴趣的文章