66 Django -- Form组件

Form组件

​ 在很多场景需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。

Django form组件就实现了上面所述的功能。

Form组件的主要功能:

1. 生成html标签
2. 保留原来的数据
3. 检验提交的数据

Form组件使用以及校验

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

from django import forms


class UserInfo(forms.Form):

    username = forms.CharField(
        label='用户名:',
        widget=forms.widgets.TextInput,     # input标签 text类型

        min_length=3,
        required=True,  # 不允许为空,值为False可以为空

        error_messages={
            'required': '不能为空!',
            'min_length': '长度不能小于3位!',
        }, 

    )
    password = forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True), 
        #这个密码字段和其他字段不一样,默认在前端输入数据错误的时候,点击提交之后,默认是不保存的原来数据的,但是可以通过这个render_value=True让这个字段在前端保留用户输入的数据
        
        min_length=6,
        max_length=10,

        error_messages={
            'required': '不能为空!',
            'min_length': '长度不能小于6位!',
            'max_length': '长度不能超过10位!',
        },  # 设置报错显示的信息

    )

    sex = forms.ChoiceField(
        label='性别:',
        choices=((1, '女'), (2, '男'),),
        initial='1',    # 默认选中女
        widget=forms.widgets.RadioSelect,   # 插件为 input标签的radio类型
        # widget=forms.widgets.Select()     # 插件为 单选的select标签
    )

    date = forms.DateField(
        label='出生日期:',
        widget=forms.widgets.TextInput(attrs={'type': 'date'})
    )   # 必须指定type,不然不能渲染成选择时间的input框

    hobby = forms.MultipleChoiceField(
        label='爱好:',
        choices=((1, '喝酒'), (2, '抽烟'), (3, '烫头')),
        initial=['1', '3'],
        widget=forms.CheckboxSelectMultiple,  # 插件为 多选的input标签 checkbox类型
        # widget=forms.widgets.SelectMultiple()   # 插件为 多选的select标签
    )



def index(request):

    if request.method == 'GET':
        u_obj = UserInfo()
        return render(request, 'index.html', {'u_obj': u_obj})

    else:
        u_obj = UserInfo(request.POST)  # {'username':'对象','password':'对象'}
        print(u_obj.fields)
        '''OrderedDict([('username', < django.forms.fields.CharField object at 0x0000011F49FCD8D0 >),
                     ('password', < django.forms.fields.CharField object at 0x0000011F49FCD4E0 >),
                     ('sex', < django.forms.fields.ChoiceField object at 0x0000011F49FDA9E8 >),
                     ('date', <django.forms.fields.DateField object at 0x000002A26AC24208>),
                     ('hobby', < django.forms.fields.MultipleChoiceField object at 0x0000011F49FDAB70 >)])
        '''
        
        if u_obj.is_valid():
            # u_obj.is_valid()  #校验用户提交的数据,全部校验成功返回True,任意一个失败都返回False

            print('正确数据', u_obj.cleaned_data)  # 校验成功之后的数据cleaned_data
             # {'username': 'yan', 'password': '123123', 'sex': '2', 'date': datetime.date(2019, 10, 1), 'hobby': ['1', '3']}
                
            return HttpResponse('ok!')

        else:
            print('错误信息', u_obj.errors)
            """
            <ul class="errorlist"><li>password<ul class="errorlist"><li>长度不能小于6位!</li></ul></li></ul>
            """
            return render(request, 'index.html', {'u_obj': u_obj})  # 能够将错误信息errors对象传入html
        
            # return HttpResponse('error!')

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
    <style>
        .c1{
            background-color: yellowgreen;
        }
    </style>
</head>
<body>

<h1>登录</h1>
{{ u_obj.errors }}
/* 打印所有的错误信息 */
<form action="" method="post" novalidate>
    {% csrf_token %}
    <div>
        {{ u_obj.username.label }}{{ u_obj.username }} 
        <span style="color: red;">{{ u_obj.username.errors.0 }}</span>
    </div>

    <div>
        {{ u_obj.password.label }}{{ u_obj.password }} 
        <span style="color: red;">{{ u_obj.password.errors.0 }}</span>
    </div>

    <div>
        {{ u_obj.sex.label }}{{ u_obj.sex }} 
        <span style="color: red;">{{ u_obj.sex.errors.0 }}</span>
    </div>

    <div>
        {{ u_obj.date.label }}{{ u_obj.date }} 
        <span style="color: red;">{{ u_obj.date.errors.0 }}</span>
    </div>

    <div>
        {{ u_obj.hobby.label }}{{ u_obj.hobby }} 
        <span style="color: red;">{{ u_obj.hobby.errors.0 }}</span>
    </div>
    <input type="submit">
</form>



</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
</html>

RegexValidator验证器:

from django import forms
from django.core.validators import RegexValidator


class UserInfo(forms.Form):

    username = forms.CharField(
        label='用户名:',
        widget=forms.widgets.TextInput,     # input标签 text类型

        min_length=3,
        required=True,  # 不允许为空,值为False可以为空

        error_messages={
            'required': '不能为空!',
            'min_length': '长度不能小于3位!',
        },

        # RegexValidator验证器
        validators=[RegexValidator(r'^[a-z]|[A-Z]', '必须以字母开头!')]
    )

自定义函数验证(re模块):

from django.core.exceptions import ValidationError

import re
def mobile_validate(value):
    """
    检验手机号码格式
    :param value:
    :return:
    """
    # 自定义匹配规则
    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):
        # 匹配不到返回None,主动抛出错误
        raise ValidationError('手机号码格式错误!')
 

class UserInfo(forms.Form):

    username = forms.CharField(
        label='用户名:',
        widget=forms.widgets.TextInput,     # input标签 text类型

        required=True,  # 不允许为空,值为False可以为空

        error_messages={
            'required': '不能为空!',
        },

        validators=[mobile_validate, ]  # 使用自定义函数规则,能够将对应字段提交的值传入mobile_validate函数中

    )

局部钩子:

在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。

会先校验usernam前面定义的校验方式,如果前面的校验通过了,会进行局部钩子定义的校验,全部完成后,再校验下一个字段。

class UserInfo(forms.Form):
     
    def clean_username(self):
        # 对username进行校验
        value=self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('不能含666!')
        else:
            return value    # 必须返回value,不然会将username删除(按错误处理)

源码:

try:
    self.cleaned_data[name] = value
    # 获取检验字段的键与值
    if hasattr(self, 'clean_%s' % name):
        # 判断是否有局部钩子
        value = getattr(self, 'clean_%s' % name)()  # 如果有,就去执行  
        self.cleaned_data[name] = value
        # 并将正确后的返回值在添加到字段中
except ValidationError as e:
    # 如果检验报错,捕获错误,添加到对应字段
    self.add_error(name, e)

全局钩子:

在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验,字段全部验证完,局部钩子也全部执行完之后,才会执行这个全局钩子校验。

class UserInfo(forms.Form):
    
    r_password = forms.CharField(
            label='确认密码:',
            widget=forms.widgets.PasswordInput,)
    
    def clean(self):
        password = self.cleaned_data.get('password')
        r_password = self.cleaned_data.get('r_password')
        if password == r_password:
            return self.cleaned_data
        else:
            # raise ValidationError('两次密码不一致!')     # 添加到了全局错误中
            self.add_error('r_password', '两次密码输入不一致!')  # 将错误信息添加到 r_password字段

批量添加样式:

通过重写form类的init方法来实现。

在view.py文件class下

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field_name, field in self.fields.items(): #orderdict(('username',charfield对象))

            field.widget.attrs.update({'class':'form-control'})

图书馆系统添加书籍:

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django import forms

class AddBookForm(forms.Form):
    title = forms.CharField(
        label='书名',
    )
    price=forms.DecimalField(
        label='价格',
        max_digits=5,
        decimal_places=2 , #999.99
        # widget = forms.TextInput(attrs={'class':'form-control'}),
    )
    publishdate=forms.CharField(

        label='出版日期',
        widget=forms.TextInput(attrs={'type':'date'}),

    )
    # publishs_id = forms.ChoiceField(
    #     choices=models.Publish.objects.all().values_list('id','name'),  #[(),()]
    # )
    # authors=forms.MultipleChoiceField(
    #     choices=models.Author.objects.all().values_list('id','name')
    #
    # )

    publishs_id = forms.ModelChoiceField(
        queryset=models.Publish.objects.all(),
    )
    authors = forms.ModelMultipleChoiceField(
        queryset=models.Author.objects.all(),

    )
    # csrfmiddlewaretoken = forms.ChoiceField
    # 批量添加属性样式
    def __init__(self,*args,**kwargs):

        super().__init__(*args,**kwargs)

        for field_name, field in self.fields.items(): #orderdict(('username',charfield对象))
            field.widget.attrs.update({'class':'form-control'})


def addbook(request):
    if request.method == 'GET':

        book_obj = AddBookForm()
        return render(request,'addbook.html',{'book_obj':book_obj})

    else:
        book_obj = AddBookForm(request.POST)
        print(request.POST)
        if book_obj.is_valid():
            print(book_obj.cleaned_data)
            #{'title': 'python2', 'price': Decimal('11'),
            # 'publishDate': '2019-10-03', 'publishs': '3', 'authors': ['1', '5']}

            # 'authors': <QuerySet [<Author: 陈硕>, <Author: 海狗>, <Author: 子文>]>} # models.py中定义了 __str__方法
            data = book_obj.cleaned_data
            authors = data.pop('authors')

            new_book = models.Book.objects.create(
                **data
                # publishs = 对象
            )
            new_book.authors.add(*authors)

            # return HttpResponse('ok')
            return redirect('showbooks')
        else:
            return render(request,'addbook.html',{'book_obj':book_obj})

addbook.html

<h1>添加书籍</h1>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="" method="post" novalidate>

                {% csrf_token %}
                {% for field in book_obj %}
                   <div class="form-group">
                      {{ field.label }}
                      {{ field }}
                      <span class="text-danger">{{ field.errors.0 }}</span>

                  </div>
                {% endfor %}
                              <button type="submit" class="btn btn-success pull-right">提交</button>
            </form>
        </div>
    </div>
</div>

</body>

ModelForm

与上面例子不同是在 publishs_id 与publishs.

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django import forms

class AddBookForm(forms.Form):
    title = forms.CharField(
        label='书名',
    )
    price=forms.DecimalField(
        label='价格',
        max_digits=5,
        decimal_places=2 , #999.99
        # widget = forms.TextInput(attrs={'class':'form-control'}),
    )
    publishs = forms.ModelChoiceField(
        queryset=models.Publish.objects.all(),
    )
    authors = forms.ModelMultipleChoiceField(
        queryset=models.Author.objects.all(),

    )
    
def addbook(request):
    if request.method == 'GET':

        book_obj = AddBookForm()
        return render(request,'addbook.html',{'book_obj':book_obj})

    else:
        book_obj = AddBookForm(request.POST)
        print(request.POST)
        if book_obj.is_valid():
            print(book_obj.cleaned_data)
            #{'title': 'python2', 'price': Decimal('11'),
            # 'publishdate': '2019-10-03', 'publishs': '3', 'authors': ['1', '5']}

            # 'authors': <QuerySet [<Author: 陈硕>, <Author: 海狗>, <Author: 子文>]>} # models.py中定义了 __str__方法
            data = book_obj.cleaned_data
            authors = data.pop('authors')

            new_book = models.Book.objects.create(
                **data
                # publishs = 对象
            )
            new_book.authors.add(*authors)

            # return HttpResponse('ok')
            return redirect('showbooks')
        else:
            return render(request,'addbook.html',{'book_obj':book_obj})
class BookForm(forms.Form):
    ...
    publishs = forms.ModelChoiceField(
        label='出版社',
        queryset=models.Publish.objects.all(),
    )

    authors = forms.ModelMultipleChoiceField(
        label='作者',
        queryset=models.Author.objects.all(),
    # 上面两个字段时modelform,可以携带数据
        
def editbook(request, book_id):
        """编辑书籍"""
    edit_obj = Book.objects.get(id=book_id)
    if request.method == 'GET':
        form_obj = BookForm(instance=edit_obj.values())
        return render(request, 'editbook.html',{'edit_obj':edit_obj})   # 将老对象传入,信息显示在页面

猜你喜欢

转载自www.cnblogs.com/yzm1017/p/11892620.html
今日推荐