Django Form

Form組件

建立Form類時,主要涉及到 【字段】 和 【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTMLcss

form組件的主要功能以下:html

  • 生成頁面可用的HTML標籤
  • 對用戶提交的數據進行校驗
  • 保留上次輸入內容

1、註冊實例

一、app01.models.py

?
1
2
3
4
5
6
7
8
9
10
from django.db import models
 
class UserInfo(models.Model):
     nid = models.BigAutoField(primary_key = True )
     name = models.CharField(verbose_name = '用戶名' , max_length = 32 , unique = True )
     pwd = models.CharField(verbose_name = '密碼' , max_length = 64 )
     email = models.EmailField(verbose_name = '郵箱' , unique = True )
     tel = models.CharField(verbose_name = '手機' )
     avatar = models.ImageField(verbose_name = '頭像' )
     create_time = models.DateTimeField(verbose_name = '建立時間' , auto_now_add = True )

二、app01.myforms.py

from django import forms
from django.forms import widgets
from app01.models import UserInfo
from django.core.exceptions import ValidationError


class RegisterForm(forms.Form):
    name = forms.CharField(
        required=True,
        min_length=6,
        max_length=32,
        label="用戶名",
        error_messages={
            "required": "用戶名不能爲空",
            "min_length": "用戶名長度不能小於6個字符",
            "max_length": "用戶名長度不能大於32個字符"},
        widget=widgets.TextInput(attrs={"class": "form-control"})
    )
    pwd = forms.CharField(
        required=True,
        min_length=6,
        max_length=32,
        label="密碼",
        error_messages={
            "required": "密碼不能爲空",
            "min_length": "密碼長度不能小於6個字符",
            "max_length": "密碼長度不能大於32個字符"},
        widget=widgets.PasswordInput(attrs={"class": "form-control"})
    )
    r_pwd = forms.CharField(
        required=True,
        min_length=6,
        max_length=32,
        label="確認密碼",
        error_messages={
            "required": "密碼不能爲空",
            "min_length": "密碼長度不能小於6個字符",
            "max_length": "密碼長度不能大於32個字符"},
        widget=widgets.TextInput(attrs={"class": "form-control"})
    )
    email = forms.EmailField(
        label="郵箱",
        error_messages={
            "required": "郵箱不能爲空",
            "invalid": "格式錯誤,必須是郵箱格式"
        },
        widget=widgets.TextInput(attrs={"class": "form-control"})
    )
    tel = forms.CharField(
        label="手機號",
        widget=widgets.TextInput(attrs={"class": "form-control"})
    )

    # 局部鉤子
    def clean_name(self):
        val = self.cleaned_data.get("name")
        ret = UserInfo.objects.filter(name=val)
        if not ret:
            return val
        else:
            raise ValidationError("該用戶已註冊!")

    def clean_email(self):
        val = self.cleaned_data.get("email")
        ret = UserInfo.objects.filter(email=val)
        if not ret:
            return val
        else:
            raise ValidationError("該郵箱已註冊!")

    def clean_tel(self):
        val = self.cleaned_data.get("tel")
        if len(val) == 11:
            return val
        else:
            raise ValidationError("手機號格式錯誤")

    # 全局鉤子
    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        r_pwd = self.cleaned_data.get('r_pwd')
        if pwd and r_pwd:
            if pwd == r_pwd:
                return self.cleaned_data
            else:
                # raise ValidationError('兩次密碼不一致') # 加在全局的錯誤裏面 form.errors.get("__all__")
                self.add_error("r_pwd", ValidationError('兩次密碼不一致'))  # 加在r_pwd的錯誤裏
        else:
            return self.cleaned_data

三、app01.views.py

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from django.shortcuts import render,HttpResponse
from app01.myforms import *
 
def reg(request):
     if request.method = = "POST" :
         form = RegisterForm(request.POST) # form表單的name屬性值應該與forms組件字段名稱一致
 
         if form.is_valid(): # 驗證成功作什麼
             form.cleaned_data.pop( 'r_pwd' )
             user_info = form.cleaned_data
             UserInfo.objects.create( * * user_info)
            
             return redirect( '/' )
         else :
             return render(request, "reg.html" , locals ())
     form = RegisterForm()
     return render(request, "reg.html" , locals ())
 
'''
form=UserForm({"name":"bubu","email":"123@qq.com","xxxx":"123123123"}) :xxxx字段不會驗證,只驗證UserForm類裏面有的字段
form.is_valid()                 :返回布爾值
form.cleaned_data               :全部乾淨的字段以及對應的值{"name":"bubu","email":"123@qq.com"}
form.errors                     :ErrorDict : {"校驗錯誤的字段":["錯誤信息",]}
form.errors.get("name")         :ErrorList ["錯誤信息",]
form.errors.get("name")[0]      :取出第一個錯誤信息
 
全局鉤子錯誤
forms中:
raise ValidationError('兩次密碼不一致') #加在全局的錯誤裏面 form.errors.get("__all__")
self.add_error("r_pwd", ValidationError('兩次密碼不一致'))
views.py中:errors=form.errors.get("__all__")
模板中:<span>{{ errors.0 }}</span>
'''
- 內部原理

                    def login(request):
                        if request.method == 'GET':
                            return render(request,'login.html')
                        else:
                            obj = LoginForm(request.POST)
                            # is_valid
                            """
                            1. LoginForm實例化時,
                                self.fields={
                                    'user': 正則表達式
                                    'pwd': 正則表達式
                                }
                            2. 循環self.fields
                                  flag = True
                                  errors
                                  cleaned_data
                                  for k,v in self.fields.items():
                                    # 1. user,正則表達式
                                    input_value = request.POST.get(k)
                                    正則表達式和input_value
                                    flag = False
                                  return flag
                           """
                            if obj.is_valid():
                                print(obj.cleaned_data)
                            else:
                                print(obj.errors)
                            return render(request,'login.html')
is_valid
def ajax_login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        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)


<body>
    <h1>用戶登陸</h1>
    <form id="f1" >
        {% csrf_token %}
        <p>
            <input type="text" name="user" />
        </p>
        <p>
            <input type="password" name="pwd" />
        </p>

        <a onclick="submitForm();">提交</a>
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function submitForm(){
            $('.c1').remove();
            $.ajax({
                url: '/ajax_login/',
                type: 'POST',
                data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\
                dataType:"JSON",
                success:function(arg){
                    console.log(arg);
                    if(arg.status){

                    }else{
                        $.each(arg.msg,function(index,value){
                            console.log(index,value);
                            var tag = document.createElement('span');
                            tag.innerHTML = value[0];
                            tag.className = 'c1';
                            $('#f1').find('input[name="'+ index +'"]').after(tag);
                        })
                    }
                }
            })
        }
    </script>
</body>
ajax+forms
from django.shortcuts import render,redirect
from app01 import models
from django.forms import Form
from django.forms import fields
from django.forms import widgets



class 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'})   # 有HTML標籤,含有錯誤信息
        obj = ClassForm(initial={'title': row.title})         # 只有HTML標籤
        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})






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_model
class 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})
#
# 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 clean_t1(self):
    #     pass
from django.core.exceptions import ValidationError
class 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
def test(request):
    obj = TestForm(initial={'t3':[2,3]})
    obj.is_valid()
    return render(request,'test.html',{'obj':obj})
班級views.py
<body>
    <h1>班級列表</h1>
    <div>
        <a href="/add_class/">添加</a>
    </div>
    <ul>
        {% for row in cls_list %}
            <li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">編輯</a>  </li>
        {% endfor %}
    </ul>
</body>

<body>
    <h1>添加班級</h1>
    <form method="POST" action="/add_class/" novalidate>
        {% csrf_token %}
        {{ obj.title }} {{ obj.errors.title.0 }}
        <input type="submit" value="提交" />
    </form>
</body>

<body>
    <h1>編輯班級</h1>
    <form method="POST" action="/edit_class/{{ nid }}/">
        {% csrf_token %}
        <p>
            {{ obj.title }} {{ obj.errors.title.0 }}
        </p>
        <input type='submit' value="提交" />
    </form>
</body>

<body>
    <h1>添加學生</h1>
    <form action="/add_student/" method="POST">
        {% csrf_token %}
        <p>
            {{ obj.name }}
        </p>
        <p>
            {{ obj.email }}
        </p>
        <p>
            {{ obj.age }}
        </p>
        <p>
            {{ obj.cls_id }}    <!--/////////////////////////-->
        </p>
        <input type="submit" value="提交" />
    </form>
</body>

<body>
    <h1>學生列表</h1>
    <a href="/add_student/">添加</a>
    <ul>
        {% for row in stu_list %}
            <li>{{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }}
                <a href="/edit_student/{{ row.id }}/">編輯</a></li>
        {% endfor %}
    </ul>
</body>
班級模板
from django.forms import Form
from django.forms import fields
from django.forms import widgets
from django.core.exceptions import ValidationError
class RegisterForm(Form):
    username = fields.CharField(
        widget=widgets.TextInput(attrs={'class':'form-control'})
    )
    password = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class':'form-control'})
    )
    password2 = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class':'form-control'})
    )
    avatar = fields.FileField(widget=widgets.FileInput(attrs={'id':"imgSelect",'class':"f1"  }))
    code = fields.CharField(
        widget=widgets.TextInput(attrs={'class':'form-control'})
    )

    def __init__(self,request,*args,**kwargs):
        super(RegisterForm,self).__init__(*args,**kwargs)
        self.request = request

    def clean_code(self):
        input_code = self.cleaned_data['code']
        session_code = self.request.session.get('code')
        if input_code.upper() == session_code.upper():
            return input_code
        raise ValidationError('驗證碼錯誤')


    # def clean_password2(self):
    #     p1 = self.cleaned_data['password']
    #     p2 = self.cleaned_data['password2']
    #     return p2

    def clean(self):
        p1 = self.cleaned_data.get('password')
        p2 = self.cleaned_data.get('password2')
        if p1 == p2:
            # return self.cleaned_data
            return None
        # self.add_error(None,ValidationError('密碼不一致'))
        self.add_error("password2",ValidationError('密碼不一致'))



###################################################################
from app01.forms import RegisterForm
from django.core.exceptions import NON_FIELD_ERRORS
def register(request):
    """
    用戶註冊
    :param request:
    :return:
    """
    if request.method == "GET":
        obj = RegisterForm(request)
        return render(request,'register.html',{'obj':obj})

    else:
        # 驗證碼操做
        obj = RegisterForm(request,request.POST,request.FILES)
        if obj.is_valid():
            pass
        else:
            print(obj.errors['__all__'])
            print(obj.errors[NON_FIELD_ERRORS])
            # obj.errors
            """
            {
                __all__: [錯誤1,錯誤2]
                user: [錯誤1,錯誤2]
                password: [錯誤1,錯誤2]
            }
            """
        return render(request,'register.html',{'obj':obj})


###################################################################
# 方式一:views.py中:form.errors['__all__'] 或者 form.errors[NON_FIELD_ERRORS]  模板中:{{ form.non_field_errors }}  獲取 __all__的錯誤
# 方式二:views.py中:errors=form.errors.get("__all__")   模板中:<span>{{ errors.0 }}</span>
Form組件中經過構造方法能夠封裝本身想要值

四、templates.reg.html

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<! DOCTYPE html>
< html lang="en">
< head >
     < meta charset="UTF-8">
     < title >Title</ title >
     < style >
     .error{
             color: red;
     }
     .register {
     width: 400px;
     margin-top: 20px;
     margin-left: auto;
     margin-right: auto;
     border: 1px solid #f0f0f0;
     padding: 10px 30px 50px 30px;
     -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05);
     box-shadow: 5px 10px 10px rgba(0, 0, 0, .05);
     }
     .register h3{font-size: 25px; text-align: center;font-weight: bold;}
     </ style >
     <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
     < link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
     integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</ head >
< body >
  
< div class="register">
             < h3 >用戶註冊</ h3 >
             < form action="" method="post" novalidate>
                 {% csrf_token %}
                 < div class="form-group">
                     < label for="id_name">{{ form.name.label }}</ label >
                     {{ form.name }} < span class="pull-right error">{{ form.name.errors.0 }}</ span >
                 </ div >
                 < div class="form-group">
                     < label for="id_pwd">{{ form.pwd.label }}</ label >
                     {{ form.pwd }} < span class="pull-right error">{{ form.pwd.errors.0 }}</ span >
                 </ div >
                 < div class="form-group">
                     < label for="id_r_pwd">確認密碼</ label >
                     {{ form.r_pwd }} < span class="pull-right error">{{ form.r_pwd.errors.0 }}</ span >< span class="pull-right error">{{ errors.0 }}</ span >
                 </ div >
                 < div class="form-group">
                     < label for="id_email">郵箱</ label >
                     {{ form.email }} < span class="pull-right error">{{ form.email.errors.0 }}</ span ></ div >
                 < div class="form-group">
                     < label for="id_tel">手機號</ label >
                     {{ form.tel }} < span class="pull-right error">{{ form.tel.errors.0 }}</ span ></ div >
  
                 < input type="submit" class="btn btn-default" value="註冊"/>
             </ form >
  
<!--
             <h3>forms組件渲染方式2</h3>
             <form action="" method="post" novalidate>
                  {% csrf_token %}
                 {% for field in form %}
                     <div class="form-group">
                         <label for="{{ field.auto_id }}">{{ field.label }}</label>
                         {{ field }} <span class="pull-right error">{{ field.errors.0 }}</span>
                     </div>
                 {% endfor %}
                  <input type="submit" class="btn btn-default" value="註冊"/>
             </form>
  
             <h3>forms組件渲染方式3</h3>
             <form action="" method="post">
                  {% csrf_token %}
                  {{ form.as_p }}
                  <input type="submit" class="btn btn-default" value="註冊"/>
             </form>
-->
  
</ div >
  
</ body >
</ html >

2、經常使用字段與插件

from django import forms
from django.forms import widgets

from blog.models import UserInfo
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator


class TestForm(forms.Form):
    user = forms.CharField(
        min_length=8,
        label="用戶名",
        initial="張三",  # 初始值,input框裏面的初始值。
        error_messages={  # 重寫錯誤信息。
            "required": "不能爲空",
            "invalid": "格式錯誤",
            "min_length": "用戶名最短8位"
        }
    )

    pwd = forms.CharField(
        min_length=6,
        label="密碼",
        widget=widgets.PasswordInput(attrs={'class': 'form-control'})
    )

    content = forms.CharField(
        widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'form-control'})
    )

    # 單radio,值爲字符串
    gender = forms.ChoiceField(
        # choices=((1, '男'), (2, '女'),),
        initial=2,
        widget=widgets.RadioSelect()
    )

    # 單checkbox
    keep = forms.fields.ChoiceField(
        label="是否記住密碼",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

    # 多選checkbox,值爲列表
    hobbies1 = forms.MultipleChoiceField(
        initial=[2, ],
        choices=((1, '足球'), (2, '籃球'),),
        widget=widgets.CheckboxSelectMultiple()
    )

    # 單select,值爲字符串
    city = forms.ChoiceField(
        choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select()
    )

    # 多選select,值爲列表
    hobbies2 = forms.MultipleChoiceField(
        choices=((1, '足球'), (2, '籃球'),),
        initial=[1, ],
        widget=widgets.SelectMultiple()
    )

    """
    #choice字段注意事項
    在使用選擇標籤時,須要注意choices的選項能夠從數據庫中獲取,可是因爲是靜態字段 ***獲取的值沒法實時更新***,
    方法1:那麼須要自定義構造方法從而達到此目的。
    """

    def __init__(self, *args, **kwargs):
        super(TestForm, self).__init__(*args, **kwargs)
        # self.fields['city2'].choices = ((1, '上海'), (2, '北京'),)
        #
        self.fields['gender'].choices = ((1, ''), (2, ''),)
        self.fields['city'].choices = UserInfo.objects.all().value_list('id', 'caption')
        self.fields['hobbies1'].choices = UserInfo.objects.all().value_list('id', 'hobbies')
        self.fields['hobbies2'].choices = UserInfo.objects.all().value_list('id', 'hobbies')


"""
在使用選擇標籤時,須要注意choices的選項能夠從數據庫中獲取,可是因爲是靜態字段 ***獲取的值沒法實時更新***,
方法2:使用django提供的ModelChoiceField和ModelMultipleChoiceField字段來實現
"""
class TestForm2(forms.Form):
    authors = forms.ModelMultipleChoiceField(queryset=UserInfo.objects.all())
    # authors = forms.ModelChoiceField(queryset=UserInfo.objects.all())

    # authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多選
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 單選
經常使用字段與插件
1、表單的綁定屬性
Form.is_bound:若是你須要區分綁定的表單和未綁定的表單,能夠檢查下表單的is_bound屬性值 f = ContactForm({}) f.is_bound 空字典也是True
  
2、使用表單驗證數據
Form.clean()若是你要自定義驗證功能,那麼你須要從新實現這個clean方法。
Form.is_valid()調用is_valid()方法來執行綁定表單的數據驗證工做,並返回一個表示數據是否合法的布爾值。
Form.errors 表單的errors屬性保存了錯誤信息字典
Form.errors.as_data() 返回一個字典,它將字段映射到原始的ValidationError實例。
Form.errors.as_json(escape_html=False) 返回JSON序列化後的錯誤信息字典。
Form.add_error(field, error) 向表單特定字段添加錯誤信息。 field參數爲字段的名稱。若是值爲None,error將做爲Form.non_field_errors()的一個非字段錯誤。
Form.has_error(field, code=None) 判斷某個字段是否具備指定code的錯誤。當code爲None時,若是字段有任何錯誤它都將返回True。
Form.non_field_errors() 返回Form.errors中不是與特定字段相關聯的錯誤。
對於沒有綁定數據的表單 驗證沒有綁定數據的表單是沒有意義的。即 f = ContactForm({}) f.is_valid() 是False
  
3、檢查表單數據是否被修改
Form.has_changed() 當你須要檢查表單的數據是否從初始數據發生改變時,能夠使用has_changed()方法。
Form.changed_data返回有變化的字段的列表。
  
4、訪問表單中的字段
Form.cleaned_data:若是你的數據沒有經過驗證,cleaned_data字典中只包含合法的字段
經過fileds屬性訪問表單的字段: f.fields['name'].label = "Username"
  
5、表單的HTML生成方式
Form.as_p()
Form.as_ul()
Form.as_table()
表單API
Field
    required=True,               是否容許爲空
    widget=None,                 HTML插件
    label=None,                  用於生成Label標籤或顯示內容
    initial=None,                初始值
    help_text='',                幫助信息(在標籤旁邊顯示)
    error_messages=None,         錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
    show_hidden_initial=False,   是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一直)
    validators=[],               自定義驗證規則
    localize=False,              是否支持本地化
    disabled=False,              是否能夠編輯
    label_suffix=None            Label內容後綴
 
 
CharField(Field)
    max_length=None,             最大長度
    min_length=None,             最小長度
    strip=True                   是否移除用戶輸入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             總長度
    decimal_places=None,         小數位長度
 
BaseTemporalField(Field)
    input_formats=None          時間格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            時間間隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定製正則表達式 phone = fields.RegexField('139\d+')
    max_length=None,            最大長度
    min_length=None,            最小長度
    error_message=None,         忽略,錯誤信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否容許空文件
 
ImageField(FileField)      
    ...
    注:須要PIL模塊,pip3 install Pillow
    以上兩個字典使用時,須要注意兩點:
        - form表單中 enctype="multipart/form-data"
        - view函數中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默認select插件
    label=None,                Label內容
    initial=None,              初始值
    help_text='',              幫助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查詢數據庫中的數據
    empty_label="---------",   # 默認空顯示內容
    to_field_name=None,        # HTML中value的值對應的字段
    limit_choices_to=None      # ModelForm中對queryset二次篩選
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   對選中的值進行一次轉換
    empty_value= ''            空值的默認值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   對選中的每個值進行一次轉換
    empty_value= ''            空值的默認值
 
ComboField(Field)
    fields=()                  使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件選項,目錄下文件顯示在頁面中
    path,                      文件夾路徑
    match=None,                正則匹配
    recursive=False,           遞歸下面的文件夾
    allow_files=True,          容許文件
    allow_folders=False,       容許文件夾
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用
 
SlugField(CharField)           數字,字母,下劃線,減號(連字符)
    ...
 
UUIDField(CharField)           uuid類型
    ...
內置字段
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
內置插件
"""
方式1:RegexValidator驗證器
"""
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from blog.models import UserInfo


class FInfo(forms.Form):
    user = forms.CharField(max_length=5,
                           validators=[RegexValidator(r'^[0-9]+$', '請輸入數字', 'invalid'),
                                       RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )

    def clean_user(self):
        val = self.cleaned_data.get("username")
        user = UserInfo.objects.filter(username=val).first()
        if not user:
            return val
        else:
            raise ValidationError("該用戶已註冊!")


"""
方式2:自定義驗證函數
"""
import re
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError


# 自定義驗證規則
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手機號碼格式錯誤')


class PublishForm(forms.Form):
    title = forms.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '標題不能爲空',
                                            'min_length': '標題最少爲5個字符',
                                            'max_length': '標題最多爲20個字符'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                            'placeholder': '標題5-20個字符'}))

    # 使用自定義驗證規則--自定義方法
    phone = forms.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手機不能爲空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                            'placeholder': '手機號碼'}))

    email = forms.EmailField(required=False,
                             error_messages={'required': '郵箱不能爲空', 'invalid': '郵箱格式錯誤'},
                             widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '郵箱'}))
字段的兩種校驗

3、Hook方法(常見校驗)

  • 除了上面兩種方式,咱們還能夠在Form類中定義鉤子函數,來實現自定義的驗證功能。
  • 局部鉤子:在Form類中定義 clean_字段名() 方法,就可以實現對特定字段進行校驗。
  • 全局鉤子:在Form類中定義 clean() 方法,就可以實現對字段進行全局校驗。
from django import forms
from django.core.exceptions import ValidationError


class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用戶名",
        initial="張三",
        error_messages={
            "required": "不能爲空",
            "invalid": "格式錯誤",
            "min_length": "用戶名最短8位"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )
    password = forms.CharField(
        min_length=6,
        label="密碼",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    re_password = forms.CharField(
        min_length=6,
        label="確認密碼",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )

    # 定義局部鉤子,用來校驗username字段
    def clean_username(self):
        value = self.cleaned_data.get("username")
        if "666" in value:
            raise ValidationError("光喊666是不行的")
        else:
            return value

    # 定義全局的鉤子,用來校驗密碼和確認密碼字段是否相同
    def clean(self):
        password_value = self.cleaned_data.get('password')
        re_password_value = self.cleaned_data.get('re_password')
        if password_value == re_password_value:
            return self.cleaned_data
        else:
            self.add_error('re_password', '兩次密碼不一致')
            raise ValidationError('兩次密碼不一致')

    # 批量添加樣式
    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })
Hook方法和批量添加樣式

4、模板屬性

屬性 說明
{{ field.label }} 字段對應的label信息
{{ field.label_tag }} 自動生成字段的label標籤,注意與{{ field.label }}的區別。
{{ field.id_for_label }} 自定義字段標籤的id
{{ field.value }} 當前字段的值,好比一個Email字段的值someone@example.com
{{ field.html_name }} 指定字段生成的input標籤中name屬性的值
{{ field.help_text }} 字段的幫助信息
{{ field.errors }} 包含錯誤信息的元素
{{ field.is_hidden }} 用於判斷當前字段是否爲隱藏的字段,若是是,返回True
{{ field.field }} 返回字段的參數列表。例如{{ char_field.field.max_length }}

Django提供了兩種獨立的方法,用於循環那些不可見的和可見的字段,hidden_fields()和visible_fields()python

屬性
{% for field in form.visible_fields %}....{% endfor %}
{% for field in form.hidden_fields %}....{% endfor %}
相關文章
相關標籤/搜索