zoukankan      html  css  js  c++  java
  • 27)django-form操作示例(动态Select数据,自定义字段验证,全局验证等)

    1)普通传递select数据

        # -*- coding:utf-8 -*-
        __author__ = 'shisanjun'
        
        from django import forms
        from django.forms import fields,widgets
        
        class UserInfoForm(forms.Form):
            user=fields.CharField(
                required=False,
                widget=widgets.Textarea(attrs={"class":"c1"}),
            )
            pwd=fields.CharField(
                max_length=12,
                widget=widgets.PasswordInput(attrs={"class":"c1"})
            )
        
            user_type=fields.ChoiceField(
                choices=[(1,"普通用户"),(2,"超级用户")],
                widget=widgets.Select,
        )
    
    
        <form action='{% url "index" %}' method="post">
            <p>
                {{ obj.user }}
            </p>
            <p>{{ obj.pwd }}</p>
            <p>{{ obj.user_type }}</p>
        </form>

    2)上面choice应该从数据库中取数据

    from django import forms
    from django.forms import fields,widgets
    from app01 import models
    class UserInfoForm(forms.Form):
        user=fields.CharField(
            required=False,
            widget=widgets.Textarea(attrs={"class":"c1"}),
        )
        pwd=fields.CharField(
            max_length=12,
            widget=widgets.PasswordInput(attrs={"class":"c1"})
        )
        user_type=fields.ChoiceField(
            #choices=[(1,"普通用户"),(2,"超级用户")],
            choices=models.UserType.objects.all().values_list("id","name"),#要返回元组列表
            widget=widgets.Select,
        )


    3)上面有个问题,就是数据库中新增加的数据,需要重起服务才能加载新数据。

    这是什么原因造成的?
    上面user,pwd,user_type都是放在类里面的,都是静态字段,数据加载都是一次性加载在内存里面的。
    
    
    #解决:
    def index(request):
        from app01 import forms
        obj=forms.UserInfoForm()
        obj.fields#这里面封装了user,pwd,user_type,当数据库有新的数据的时候从新赋值
        obj.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
        return render(request,"index.html",{"obj":obj})

    4)上面如果有很多choince,那要写很多,怎么改进(两种方法)

    重新构造form的__init__()方法。在init里面赋值上面也是对类对象的实例重新赋值。
    
    
    #方法1
    # -*- coding:utf-8 -*-
    __author__ = 'shisanjun'
    
    from django import forms
    from django.forms import fields,widgets
    from app01 import models
    class UserInfoForm(forms.Form):
        user=fields.CharField(
            required=False,
            widget=widgets.Textarea(attrs={"class":"c1"}),
        )
        pwd=fields.CharField(
            max_length=12,
            widget=widgets.PasswordInput(attrs={"class":"c1"})
        )
        user_type=fields.ChoiceField(
            #choices=[(1,"普通用户"),(2,"超级用户")],
            choices=models.UserType.objects.all().values_list("id","name"),
            widget=widgets.Select,
        )
    
        def __init__(self,*args,**kwargs):
            super(UserInfoForm,self).__init__(*args,**kwargs)
            self.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
    
    #方法2
    
    
     -*- coding:utf-8 -*-
    __author__ = 'shisanjun'
    
    from django import forms
    from django.forms import fields,widgets
    from app01 import models
    class UserInfoForm(forms.Form):
        user=fields.CharField(
            required=False,
            widget=widgets.Textarea(attrs={"class":"c1"}),
        )
        pwd=fields.CharField(
            max_length=12,
            widget=widgets.PasswordInput(attrs={"class":"c1"})
        )
      #方法1 user_type=fields.ChoiceField( #choices=[(1,"普通用户"),(2,"超级用户")], choices=[],#这里不用赋值,原因实例化的时候init会赋值 widget=widgets.Select, ) #方法2 user_type2=fields.CharField(widget=widgets.Select(choices=[])) def __init__(self,*args,**kwargs): super(UserInfoForm,self).__init__(*args,**kwargs) self.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name") #方法2 self.fields["user_type2"].widget.choices=models.UserType.objects.all().values_list("i
    d","name")

    5)上面从数据库取数据也django也可以自己做,但是不太好。

    from django.forms.models import ModelChoiceField
    user_type3=ModelChoiceField(
            queryset=models.UserType.objects.all(),#django会自动取
        )
    
    这里是有代价的,要在model里面加__str__,不然显示的名称是对象
    class UserType(models.Model):
        name=models.CharField(max_length=32)
        def __str__(self):
            return self.name
    
    
    
    # -*- coding:utf-8 -*-
    __author__ = 'shisanjun'
    
    from django import forms
    from django.forms import fields,widgets
    from app01 import models
    from django.forms.models import ModelChoiceField
    class UserInfoForm(forms.Form):
        user=fields.CharField(
            required=False,
            widget=widgets.Textarea(attrs={"class":"c1"}),
        )
        pwd=fields.CharField(
            max_length=12,
            widget=widgets.PasswordInput(attrs={"class":"c1"})
        )
        user_type=fields.ChoiceField(
            #choices=[(1,"普通用户"),(2,"超级用户")],
            choices=[],#这里不用赋值,原因实例化的时候init会赋值
            widget=widgets.Select,
        )
        #方法2
        user_type2=fields.CharField(widget=widgets.Select(choices=[]))
    
        #方法3
        user_type3=ModelChoiceField(
            empty_label="请选择",
            queryset=models.UserType.objects.all(),#django会自动取
        )
    
        def __init__(self,*args,**kwargs):
            super(UserInfoForm,self).__init__(*args,**kwargs)
            self.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
          #方法2
            self.fields["user_type2"].widget.choices=models.UserType.objects.all().values_list("id","name")

    6)默认值

    def index(request):
        from app01 import forms
        obj=forms.UserInfoForm()#如果这里赋值字典就是显示默认值(初始化)
        obj.fields#这里面封装了user,pwd,user_type,当数据库有新的数据的时候从新赋值
        #obj.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
        return render(request,"index.html",{"obj":obj})

    7)用户验证

    def index(request):
        if request.method=="GET":
            obj=forms.UserInfoForm()#如果这里赋值字典就是显示默认值(初始化)
            obj.fields#这里面封装了user,pwd,user_type,当数据库有新的数据的时候从新赋值
            #obj.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
            return render(request,"index.html",{"obj":obj})
        elif request.method=="POST":
            obj=forms.UserInfoForm(request.POST,request.FILES)
            obj.is_valid()#他是基于什么验证的,根据from模板验证。form会为长度,为空等验证,但是如果用户已存在,就不应该进行上面验证。
    
    
    
        # -*- coding:utf-8 -*-
    __author__ = 'shisanjun'
    
    from django import forms
    from django.forms import fields,widgets
    from app01 import models
    from django.forms.models import ModelChoiceField
    from django.core.exceptions import ValidationError
    class UserInfoForm(forms.Form):
        user=fields.CharField(
            required=False,
            widget=widgets.Textarea(attrs={"class":"c1"}),
        )
        pwd=fields.CharField(
            max_length=12,
            widget=widgets.PasswordInput(attrs={"class":"c1"})
        )
        user_type=fields.ChoiceField(
            #choices=[(1,"普通用户"),(2,"超级用户")],
            choices=[],#这里不用赋值,原因实例化的时候init会赋值
            widget=widgets.Select,
        )
        #方法2
        user_type2=fields.CharField(widget=widgets.Select(choices=[]))
    
        #方法3
        user_type3=ModelChoiceField(
             empty_label="请选择",
            queryset=models.UserType.objects.all(),#django会自动取
        )
    
        def __init__(self,*args,**kwargs):
            super(UserInfoForm,self).__init__(*args,**kwargs)
            self.fields["user_type"].choices=models.UserType.objects.all().values_list("id","name")
          #方法2
            self.fields["user_type2"].widget.choices=models.UserType.objects.all().values_list("id","name")
    
    
    
    class RegiesterForm(forms.Form):
        user=fields.CharField()#一个字段一个字段的循环,这里通过了,才会到clean_user,下面以次类推
        email=fields.EmailField()
    
        def clean_user(self): #通过调用form预留的钩子,clean_user单独对user做验证
            c=models.Userinfo.objects.filter(name=self.cleaned_data['user'])
            #如果数据库中有,应该给页面报错了,显示用户已存在
            if not c:
                return self.cleaned_data['user'] #返回原来值
            else:
                #如果没有抛出valideionError
                 raise ValidationError("用户名已存在",code="xxx")
        def clean_email(self):
            pass
        

    8)如果用户名不对,错误显示在用户名旁边。(上面是单独给字段写的钩子),也可以对整个form做钩子验证。

    class LoginForm(forms.Form):
        user=fields.CharField()#一个字段一个字段的循环,这里通过了,才会到clean_user,下面以次类推
        pwd=fields.CharField()
        email=fields.EmailField()
    
        def clean_user(self): #通过调用form预留的钩子,clean_user单独对user做验证
            c=models.Userinfo.objects.filter(name=self.cleaned_data['user'])
            #如果数据库中有,应该给页面报错了,显示用户已存在
            if not c:
                return self.cleaned_data['user'] #返回原来值
            else:
                #如果没有抛出valideionError
                 raise ValidationError("用户名已存在",code="user")
        def clean_email(self):
            pass
    
        def clean(self): #对整体验证
    
            #判断用户名和密码是否存在
            c= models.Userinfo.objects.filter(user=self.cleaned_data["user"],password=self.cleaned_data["pwd"])
            if not c:
                return self.cleaned_data #源码中self.cleaned_data=cleaned_data
            else:
                raise   ValidationError("用户名或密码错误")
    

    源码中一共有3个钩子

    self._clean_fields()对字段验证
    self._clean_form()对整体验证
    self._post_clean()
    

    form验证经历阶段:数据来了-->到相应的form-->先拿到第一个字段,先进行正则表达式判断完,然后执行字段的钩子,--〉所有字段运行完了到clean(),最后到postclean()

    is_valid()--->self.errors-->full_clean -->  self._clean_fields()对字段验证
                                                self._clean_form()对整体验证
                                                self._post_clean()
    

    views中错误信息

    #views.py
    def register(request):
        obj=forms.RegiesterForm(request.POST)
        if obj.is_valid(): #下面操作,可以在is_valid做验证
            #obj.cleaned_data['']
            obj.cleaned_data()
        else:
            obj.errors
            #obj.errors就是个字段,每个字段的错误放在各自的字段里面,整体clean错误放在那里了
            from django.core.exceptions import NON_FIELD_ERRORS
            """
            '__all__':[],整体的错误信息clean抛出的异常都在这里==〉NON_FIELD_ERRORS
            NON_FIELD_ERRORS:[],
            'user':['code':required,'message':'xxx'],
            'pwd':['code':required,'message':'xxx'],
    
       """
    
    
    account.py
    from django.shortcuts import redirect,render,HttpResponse
    from django import forms
    from django.forms import fields
    from django.forms import widgets
    from app01 import models
    from django.core.exceptions import ValidationError
    import json
    class LoginForm(forms.Form):
        user=fields.CharField()#一个字段一个字段的循环,这里通过了,才会到clean_user,下面以次类推
        pwd=fields.CharField()
        email=fields.EmailField()
    
        def clean_user(self): #通过调用form预留的钩子,clean_user单独对user做验证
            c=models.Userinfo.objects.filter(name=self.cleaned_data['user'])
            #如果数据库中有,应该给页面报错了,显示用户已存在
            if not c:
                return self.cleaned_data['user'] #返回原来值
            else:
                #如果没有抛出valideionError
                raise  ValidationError("用户名已存在",code="user")
        def clean_email(self):
            pass
        #
        # def clean(self): #对整体验证
        #
        #     #判断用户名和密码是否存在
        #     c= models.Userinfo.objects.filter(user=self.cleaned_data["user"],password=self.cleaned_data["pwd"])
        #     if not c:
        #         return self.cleaned_data #源码中self.cleaned_data=cleaned_data
        #     else:
        #          ValidationError("用户名或密码错误")
    
    class JsonCustomEncoder(json.JSONEncoder):
        from django.core.exceptions import ValidationError
        def default(self, field):
            if isinstance(field,ValidationError):
                return {'code':field.code,'message':field.messages}
            else:
                return json.JSONEncoder.default(self,field)
    
    def  login(request):
        res={"status":False,"data":None,"error":None}
        if request.method=="GET":
            return render(request,"login.html")
        elif request.method=="POST":
            obj=LoginForm(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
                res["status"]=True
            else:
                #res["error"]=obj.errors.as_json()
                #为什么不用as_json,as_json返回的是字典,所以res就会变成嵌套字典,json不能转嵌套字典,下面用as_data()结合cls定制
                #print(obj.errors.as_data())#errors.as_data()返回的是 ValidationError类型,不是字典,不能直接序列化
                res["error"]=obj.errors.as_data()
            result=json.dumps(res,cls=JsonCustomEncoder)#dumps有个参数cls,可以定制
    
            return HttpResponse(json.dumps(result))
    
    #obj.errors返回的是ErrorDict,不是字典(虽然继承字典)
    #obj.errors.as_json() 返回的字符串(前端要连续反解两次)
    #obj.errors.as_data()  返回原生的字典 但是返回value 是ValidationError,不能直接序列化
    模板
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form id="fm">
            {% csrf_token %}
            <p>
                <input type="text" name="username">
            </p>
            <p>
                <input type="text" name="password">
            </p>
            <a id="submit">提交</a>
        </form>
        <script src="/static/jquery-1.12.4.js"></script>
        <script>
            $(function(){
                //框架自加载
                $("#submit").click(
                        function(){
                            $.ajax(
                                    {
                                        url:"{% url 'login' %}",
                                        type:"post",
                                        data:$("#fm").serialize(),
                                        success:function(arg){
                                            arg=JSON.parse(arg);
                                            console.log(arg);
                                        },
                                        error:function(arg){
                                            console.log(arg);
                                        }
    
                                    }
                            )
                        }
                )
            })
        </script>
    </body>
    

     

  • 相关阅读:
    北京高考零分作文(看到最后一句笑喷了!)
    关于前几天无法访问的问题
    用 PHP 读取和编写 XML DOM[转]
    Delphi对INI文件的详细操作方法
    如何在WebService中获取客户端的IP地址
    正则表达式30分钟入门教程
    [原创]shell对xml操作的脚本
    预防SQL注入攻击之我见(好文章)
    表驱动方法(非常好的数据结构)
    请教shell读写XML问题
  • 原文地址:https://www.cnblogs.com/lixiang1013/p/7860463.html
Copyright © 2011-2022 走看看