Django Form和ModelForm


Form

局部钩子、全局钩子

ModelForm

两种的区别

          Form介绍

form组件的主要功能如下:

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

        使用form组件实现书籍的增加功能

首先定义一个类

在views.py中定义一个BookForm的类

from django import forms

#自定义一个form类
class BookForm(forms.Form):
    title = forms.CharField(
        max_length = 12,
        max_length = 6,
        label = "书名",        #页面 label标签显示的中文名
        widget = forms.widgets.TextInput(attrs={"class":"form-control"})        #widget 插件
    )

    publish_date = forms.DateField(
        label = "出版日期"
        widget = forms.widgets.DateInput(attrs={"type":"date","class":"form-control"})
    )

    phone = forms.CharField(
        label = "手机号",
        max_length = 11,
        required = True,    #是否允许为空
        widget = forms.widgets.TextInput(attrs={"class":"form-control"})
    )

    publisher = forms.ModelChoiceField(
        queryset = models.Publisher.objects.all(),    #queryset:查询数据库中的数据
        widget = forms.widgets.Select(attrs = {"class":"form-control})
    )

    authors = forms.ModelMultipleChoiceField(
        queryset = models.Author.objects.all(),
        widget = forms.widgets.SelectMultiple(attrs={"class":"form-control"})
    )
views.py

对应的html操作

<form action="" method="post" novalidate autocomplete="off">

    {%csrf_token%}

    {% for field in form_obj %}
        <div class="form-group">
            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
            {{ field }}
            <p> {{ field.errors.0 }}</p>
        </div>
    {% endfor %}

    <input type="submit" class="btn btn-info">
</form>
addbook.html

 添加书籍函数

def addbook(request):
    form_obj = BookForm()
    if request.POST:
        #做校验
        form_obj = BookForm(request.POST)
        if form_obj.is_valid(): #做数据有效性的校验
            # 因为有多对多的字段,需要额外处理
            authors = form_obj.cleaned_data.pop("authors")
            # 创建新书籍对象
            book_obj = models.Book.objects.create(**form_obj.cleaned_data)
            # 将书籍对象和作者建立关联
            book_obj.authors.add(*authors)

            return redirect('/book_list/')

    return render(request,'addbook.html',locals())
views.py

          Form常用字段与插件

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

        initial

初始值,input框里面的初始值

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length = 8,
        label = "用户名"
        initial = "张三"    #设置默认值
    )

    pwd = forms.CharField(min_length=6,label="密码")

        error_messages

重写错误信息

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length = 8,
        label = "用户名",
        initial = "张三",
        error_messages = {
            "required":    "不能为空",
            "invalid":    "格式错误",
            "min_length":     "用户名最短8位"
    )
    pwd = forms.CharField(min_length=6,label="密码")

        radioSelect

单radio值为字符串,choices不建议固定,具体方法下方有说明

class LoginForm(forms.Form):
    ...
    ...
    gender = forms.fields.ChoiceField(
        choices = ((1,''), (2,''), (3,'保密')),
        label = "性别",
        initial = 3,
        widget = forms.widgets.RadioSelect()
    )

        单选select

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.ChoiceField(
        choice = ((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label = "爱好",
        initial = 3,
        widget = forms.widgets.Select()
    )

        多选select

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=[1, 3],
        widget = forms.widgets.SelectMultiple()
    )

        单选CheckBox

class Login(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="爱好",
        initial=[1, 3],
        widget = forms.widgets.CheckboxSelectMultiple()
    )

关于choice的注意事项

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段,**获取的值无法实时更新**,那么需要自定义构造方法从而达到此目的

具体方法:

class FInfo(forms.Form):
    ...
    publisher = forms.ModelChoiceField(
        queryset=models.Publisher.objects.all(),
        widget=forms.widgets.Select(attrs={"class": "form-control"})
    )

          Django Form所有内置字段

Field
    required=True            是否运行为空
    widget=None,            HTML插件
    label=None,                用于生成label标签或显示内容
    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,              最小值


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


RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}


FileField(Field)
    allow_empty_file=False     是否允许空文件


ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)


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= ''            空值的默认值


TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值


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类型
Django Form内置字段

 自定义校验规则

1、使用内置的正则校验器: validators=[]
from django.core.validators import RegexValidator

class BookForm(forms.Form):
    phone = forms.CharField(
    max_length=11,
    validators=[RegexValidator(r'^1[35789]\d{9}$',"手机号格式不正确")]
    )
2、手写一个校验函数
from django.core.exceptions import ValidationError

# 自定义一个字段的校验规则函数

def phone_validate(value):
    # 拿用户填写的手机号去数据库中校验
    is_exits = models.Book.objects.filter(phone=value)
    if is_exits:
        #如果手机号被注册,不能再注册
        raise ValidationError("该手机号已经被注册")
    else:
        return value


# 第二步在自定义的BookForm类中phone字段添加phone_validate
from django.core.validators import RegexValidator

class BookForm(forms.Form):
    ...

    phone = forms.CharField(
        max_length=11,
        validators = [RegexValidator(r'^1[356789]\d{9}$','手机号格式不正确'),phone_validate]
    )
3、使用钩子函数做校验
局部钩子(hook)
from django.core.exceptions import ValidationError

black_list = ["alex",]
class BookForm(forms.Form):
    ...

    # 定义一个局部钩子,对title字段做有效性验证
    def clean_title(self): 
        value = self.cleand_data.get("title") #判断有没有敏感词
      #第一种方法:
        #if "alex" in value:
        #    raise ValidationError("alex已被和谐")
        #else:
        #    return value

        #第二种方法:
        for v in black_list:
            if v in value:
                raise ValidationError("{}为敏感词".format(v))
        return value
全局钩子 (两个字段值做比较)
#全局钩子,在form类内部定义一个 clean(self) 方法

black_list = ["alex",]

class BookForm(forms.Form):
    def clean(self):
        #取到所有字段的数据
        # self.add_error("字段","密码和确认密码不一致")

        title = self.cleand_data.get("title")
        for v in black_list:
            if v in title:
                self.add_error("title","包含敏感词{}".format(v))
        return title

        form组件给html标签设置默认值

# 以修改书籍功能为例:

def editbook(request,edit_id):
    book_obj = models.Book.objects.filter(id=edit_id).first()
    # 把ORM中一个对象快速转换成字典格式
    from django.forms import model_to_dict
    book_dict = model_to_dict(book_obj)
    book_dict["publish_date"] = book_obj.publish_date.strftime("%Y-%m-%d")
    form_obj = BookForm(book_dict)

    if request.POST:
        # 从用户提交过来的数据中取数据
        form_obj = BookForm(request.POST)
        if form_obj.is_valid():
            #去数据库更新数据
            book_obj.title = form_obj.cleaned_data.get("title")
            book_obj.publish_date = form_obj.cleaned_data.get("publish_date")
            book_obj.publisher_id = form_obj.cleaned_data.get("publisher")
            book_obj.save()
            book_obj.authors.set(form_obj.cleaned_data.get("authors"))
            return redirect('/book_list/')

    return render(request, 'editbook.html', locals())

ORM中把一个对象转换成字典格式

from django.forms import model_to_dict

book_obj = models.Book.objects.filter(id=edit_id).first()
book_dict = model_to_dict(book_obj)

补充进阶

批量添加样式

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

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    ...

    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'
            })

         ModelForm

form与model的终极结合,具有以下功能:验证、数据库操作。

# 写一个和Model类一一对应的form
from django import forms
class BookModelForm(forms.ModelForm): class Meta: model = models.Book #对应的model中的类 fields = "__all__" # model类里所有的字段都展示 # fields = ["title",] # 指定展示某些字段 exclude = None #除了指定字段,其他字段都显示 labels = { #设置label标签名 "title":"书名", "phone":"手机号", "publish_date":"出版日期", "publisher":"出版社", "authors":"作者", } widgets = { #设置每个字段的插件信息 "title":forms.widgets.TextInput(attrs={"class":"form-control"}), "phone": forms.widgets.TextInput(attrs={"class": "form-control"}), "publish_date":forms.widgets.DateInput(attrs={"type":"date","class":"form-control"}), "publisher": forms.widgets.Select(attrs={"class": "form-control"}), "authors": forms.widgets.SelectMultiple(attrs={"class": "form-control"}), } error_messages = { #设置每个字段的报错提示信息 "publisher":{ "required":"必须选一个出版社!" } }
class Meta: 常用参数
model = models.Student  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息
instance = book_obj #实例名
def editbook(request,edit_id):
    book_obj = models.Book.objects.filter(id=edit_id).first()
    form_obj = BookModelForm(instance=book_obj)

    if request.POST:
        # 从用户提交过来的数据中取数据
        form_obj = BookModelForm(request.POST,instance=book_obj)
        if form_obj.is_valid():
            #去数据库更新数据
            form_obj.save()
            return redirect('/book_list/')

    return render(request, 'editbook.html', locals())
书籍编辑操作
def addbook(request):
    form_obj = BookModelForm()
    if request.POST:
        #做校验
        form_obj = BookModelForm(request.POST)
        if form_obj.is_valid(): #做数据有效性的校验
            form_obj.save()
            return redirect('/book_list/')

    # 实例化BookForm
    return render(request,'addbook.html',locals())
书籍添加操作

 

class Meta:
model,                            #对应model的类
fields                                #字段
exclude=None                    #排查的字段
labels=None                        #提示信息
widgets=None                    #自定义插件
error_messages=None        #自定义错误信息
field_classes=None                #自定义字段类

验证执行过程:
is_valid -> full_clean --> 钩子 --> 整体错误

字典字段验证:
def clean_字段名(self):
    return "新值"

验证:
model_form_obj.is_valid()
model_form_obj.clean()
model_form_obj.cleand_data

用于创建:
model_form_obj = XXOOModelForm(request.POST)

用于更新和初始化
obj = model.tb.objects.get(id=1)
model_form_obj = XXOOModelForm(request.POST,instance=obj)
Model Form组件

 

          Form和ModelForm区别

model可以操作数据库字段,form验证也有那几个字段,虽然耦合度很低,但是代码还是有重复的。如果使用ModelForm,可以利用model

里的字段,form里的字段就可以不用写了。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM