day063 form 和modelform组件

注册功能: (写一个简单的注册功能,要求用户名长度不得小于6位.)

  普通方式写注册功能 

    views视图下:

def register(request):
    error_msg=' '
    if request.method=='GET':
        username=request.POST.get('name')
        password=request.POST.get('upwd')
        if len(username)<6:
            error_msg='用户名长度不得小于6位.'
        else:
            return Httpresponse('注册成功.')
    return render(request,'register.html',{'error_msg': error_msg})

 

  login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<form action="/reg/" method="post">
    {% csrf_token %}
    <p>
        用户名:
        <input type="text" name="name">
    </p>
    <p>
        密码:
        <input type="password" name="pwd">
    </p>
    <p>
        <input type="submit" value="注册">
        <p style="color: red">{{ error_msg }}</p>
    </p>
</form>
</body>
</html>

 

  使用form组件实现注册功能

  views视图如下:

    首先我们先定义一个MyForm类:

from django import forms

class MyForm(forms.Form):
    name=forms.CharField( label='用户名:') #form字段的名称写的是什么,前段生成的input标签的时候,input标签的name就是什么
    upwd=forms.CharField( label='密码:') #label指的是在input标签前面加的文本内容

 

  再写一个views视图函数:

#基于form组件实现注册功能
def register(request):
    #实例化一个form对象
    form_obj=RegForm()
    if request.method==' POST ':
        form_obj=RegForm(data=request.POST)
        #既然传过来的input标签的name属性值和form类对应的字段名是一样的,所以接过来后,form就取出对应的form字段名相同的数据进行form校验
            
        if form_obj.is_valid():
            return Httpresponse('注册成功!!')
    return render(request,'register.html',{ 'form_obj': form_obj })

 

  login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册2</title>
</head>
<body>
    <form action="/reg2/" method="post" novalidate autocomplete="off">  #novalidate 告诉前端form表单,不要对输入的内容做校验
        {% csrf_token %}
        #{{ form_obj.as_p }}  直接写个这个,下面的用户名和密码的标签不自己写,你看看效果
        <div>
            <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
            {{ form_obj.name }} {{ form_obj.name.errors.0 }}  #errors是这个字段所有的错误,我就用其中一个错误提示就可以了,再错了再提示,并且不是给你生成ul标签了,单纯的是错误文本
           {{ form_obj.errors }} #这是全局的所有错误,找对应字段的错误,就要form_obj.字段名
        </div>
        <div>
            <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
            {{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
        </div>
        <div>
            <input type="submit" class="btn btn-success" value="注册">
        </div>
    </form>
</body>
</html>

 

  form常用字段与插件

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

  插件:

  initial  : 初始值,input框里面的默认值(初始值).

class loginform(forms.Form):
    username=forms.CharField(
        min_length=6, #最小长度
        max_length=10,  #最大长度
        initial ='请输入用户名' , #设置默认值
        )

    password=forms.CharField(min_length=6,label='密码:')

 

  error_messages : 重写错误信息.

class loginform(forms.Form):
    username=forms.CharField(
        error_messages={
            'required' : '不能为空',
            'invalid' :'格式错误',
            'min_length': '用户名最短为8位'
                }
            )
    pwd=forms.CharField(label='密码')

 

  password :

class loginform(forms.Form):
  ......
    pwd=forms.CharField(
        label='密码',
        widget=forms.widgets.PassowrdInput(attrs={'class':'c1'},render_value=True) 
)

 

    radio Select

  单选radio值为字符串

class loginform(forms.Form):
    username=forms.CharField(
        min_length=8,
        ......
        )

    gender=forms.fields.ChoiceField(
        choices=((1,''),(2,''),(3,'嘻嘻嘻')),
        label='性别:',
        initial=-------,
        widget=forms.widgets.RadioSelect()
        )

 

  单选select

class loginform(forms.Form):
    hobby=forms.fields.ChoiceField(
        choices=((1,'篮球'),(2,'足球'),(3,'乒乓球')),
        label='爱好:',
        initial='------',
        )

 

  多选select

class loginform(forms.Form):
    ......
    hobby=forms.Fields.MultipleChoiceField(
        choices=((1,'篮球'),(2,'足球'),(3,'乒乓球')),
        label='爱好:',
        widget=forms.widgets.SelectMultiple()
        )

 

  单选CheckBox

class loginform(forms.Form):
    ...
    keep=forms.fields.ChoiceField(
        label='是否记住密码',
        initial='Checked',
        widget=forms.widgets.CheckBoxInput()

 

  多选CheckBox

class loginform(forms.Form):
    ......
    hobby=forms.Fields.MultipleChoiceField(
        choices=((1,'篮球'),(2,'足球'),(3,'乒乓球')),
        label='爱好:',
        widget=forms.widgets.CheckboxSelectMultiple()
        )

 

  form 所有内置字段

  内置字段:

field 
    required=True, #是否允许为空
    widget=None,  #HTML插件
    label='None',   #用于生成label标签或者是显示内容(在input标签前面)
    initial=None,   #默认值
    help_text='' ,    #帮助信息(在标签旁边显示)
    error_messages=None,  #错误信息{required:不能为空 ,invalid: '格式错误'}
    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,                      自定制正则表达式
    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类型

 

  自定义验证函数

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
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(Form):
 
 
    title = fields.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 = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手机不能为空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

 

Hook钩子方法

局部钩子

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

class loginform(forms.Form):
    username=forms.CharField(
        min_length=5,
        label='用户名:',
        initial='默认值',
        error_messages={
        'required': '不能为空',
        'invalid': '格式错误',
        'min_length':'用户名最短为8位'},
        widget=forms.widgets.TextInput(attrs={'class':'form-control'})
        )

    def clean_username(self):
        value=self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('666翻了')

        else:
            return value

  全局钩子

  我们在form类中定义clean()方法,就能实现对字段进行全局校验,字段全部验证完毕,局部钩子也全部执行完毕之后,执行这个全局钩子校验.

class loginform(forms.Form):
     ......
    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)
    )
    ...
    # 定义全局的钩子,用来校验密码和确认密码字段是否相同,执行全局钩子的时候,cleaned_data里面肯定是有了通过前面验证的所有数据
    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', '两次密码不一致') #在re_password这个字段的错误列表中加上一个错误,并且clean_data里面会自动清除这个re_password的值,所以打印clean_data的时候会看不到它
            raise ValidationError('两次密码不一致')

 

modelform组件

from django import forms

class RbForms(forms.modelform):
    class Meta:
        model=models.Role
        fields='__all__'
        exclude=[xxx,xxx]
        widgets={
        'name': forms.TextInput(attrs={'class': 'form-control'})
        }
class BSForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})

 

 

 

 在写别的具体的form的时候只需要继承这个BSForm就可以啦

class HostForm(BSForm):
    class Meta:
        model = models.Host
        fields = "__all__"

    def clean_hostip(self):
        hostip = self.cleaned_data['hostip']
        host = models.Host.objects.filter(hostip=hostip)
        if host.count() == 0:
            return hostip
        elif host.count() != 0 and hostip == self.instance.hostip:
            return hostip
        else:
            raise forms.ValidationError("ip地址已存在,请重新输入.")

 

posted @ 2019-04-14 22:43  想扣篮的小矮子  阅读(103)  评论(0编辑  收藏  举报