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>

 

 

posted on 2017-11-19 17:31  shisanjun  阅读(2300)  评论(0编辑  收藏  举报

导航