Django-form组件和modelform组件

Django-form表单

在Django 中构建一个表单

功能:生成HTML标签+用户请求数据验证
使用form组件的优点:
1.form表单提交数据时,数据出现错误,返回的页面中仍可以保存之前输入的数据。 2.自定义限制字段的条件。

from类

from django import forms

# 改变input框类型type
from django.forms import widgets
class Loginform(forms.Form):
    user = forms.CharField(max_length=12, min_length=3,
                           error_messages={
                               "required": "不能为空",  # 设置提示错误信息
                               "max_length": "最大长度不能超过6",
                               "min_length": "最小长度不能小于3",
                           }
                           )

    phone = forms.CharField(
        error_messages={
            "required": "不能为空",
        }
    )

    email = forms.EmailField(error_messages={
        "required": "不能为空",
        "invalid": "格式错误"}
    )

    pwd = forms.CharField(max_length=8, min_length=4,
                          error_messages={
                              "required": "不能为空",
                              "max_length": "最大长度不能超过8",
                              "min_length": "最小长度不能小于4",
                          },
                          widget=widgets.PasswordInput(attrs={"class": "active"})  # attrs自定义类属性
                          )

    rpwd = forms.CharField(max_length=8, min_length=4,
                           error_messages={
                               "required": "不能为空",
                               "max_length": "最大长度不能超过8",
                               "min_length": "最小长度不能小于4",
                           },
                           widget=widgets.PasswordInput()
                           )
form类

定义一个Form 类,任意写字段。

字段允许的最大长度通过max_length 定义。它完成两件事情。首先,它在HTML 的<input> 上放置一个maxlength="100"(这样浏览器将在第一时间阻止用户输入多于这个数目的字符)。它还意味着当Django 收到浏览器发送过来的表单时,它将验证数据的长度。

Form 的实例具有一个is_valid() 方法,它为所有的字段运行验证的程序。当调用这个方法时,如果所有的字段都包含合法的数据,它将:

  • 返回True
  • 将表单的数据放到cleaned_data属性中。

完整的表单,第一次渲染时,看上去将像:

1
2
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100">

 注意它不包含 <form> 标签和提交按钮。我们必须自己在模板中提供它们。

视图

def login(request):
    if request.method == "POST":
        print(request.POST)
        form_obj = Loginform(request.POST)  #实例化对象,

        if form_obj.is_valid():  # 是否校验成功返回bool  is_vaild()方法是父类(Form)的
            # 1.数据全部合格
            # 2.取出合格数据


            print(form_obj.cleaned_data)  # 放合格数据
            name = form_obj.cleaned_data
            del name["rpwd"]
            phone =form_obj.cleaned_data.get("phone")
            # # 增加到数据库
            print(name)
            models.User_info.objects.create(**name)

            return redirect("/index/")
        else:
            # 1.至少存在一个错误字段
            # 2.取出合格数据

            # print(form_obj.errors)  # 存放错误信息,错误信息可以多条  {"字段":["","",""]}
            # print(type(form_obj.errors))  # class 'django.forms.utils.ErrorDict'>
            #
            # print(form_obj.errors["user"])  # 找到user字段的错误信息
            # print(type(form_obj.errors["user"]))  # <class 'django.forms.utils.ErrorList'>
            print("*" * 120)
            print(form_obj.errors)  # 全局钩子错误信息
            print(type(form_obj.errors))
            print(form_obj.errors.get("__all__"))  # 取出全局钩子错误信息

            all_errors = form_obj.errors.get("__all__")
            return render(request, "login.html", {"form_obj": form_obj, "all_errors": all_errors})

    form_obj = Loginform()  # 实例化对象 没有传值
    return render(request, "login.html", {"form_obj": form_obj})
视图

如果访问视图的是一个GET 请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时预期发生的情况。

如果表单的提交使用POST 请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。

我们调用表单的is_valid()方法;如果它不为True,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。

如果is_valid()True,我们将能够在cleaned_data 属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。

模板(三种方法)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>

</head>
<body>

################方法一#################
#只产生<p>和<input>,一般不建议使用
<form action="" method="post">
    {% csrf_token %}
   {{ form_obj.as_p }}
   <input type="submit">
</form>
################方法二#################
#自定义标签类型
<form action="" novalidate method="post" novaildate>  #novildate取消浏览器校验
    {% csrf_token %}
    <h3>注册用户</h3>
    <div><label for="">用户姓名</label>
        {{ form_obj.user }} <span>{{ form_obj.errors.user.0 }}</span>    {# 输入框    错误信息 #}
    </div>

    <div><label for="">联系方式</label>
        {{ form_obj.phone }} <span>{{ form_obj.errors.phone.0 }}</span>
    </div>

    <div><label for="">个人邮箱 </label>
        {{ form_obj.email }} <span>{{ form_obj.errors.email.0 }}</span>
    </div>

    <div><label for="">用户密码</label>
        {{ form_obj.pwd }} <span>{{ form_obj.errors.pwd.0 }}</span>
    </div>

    <div><label for="">确认密码</label>
{#        {{ form_obj.rpwd }} <span>{{  form_obj.errors.repwd.0 }}{{ form_obj.non_field_errors.0 }}</span>#}
        {{ form_obj.rpwd }} <span>{{  form_obj.errors.repwd.0 }}{{ all_errors.0 }}</span>
    </div>

    <input type="submit" value="注册">

</form>
###############方法三#################
#field.label 得到字段名字
<form action="" novalidate method="post">
    {% csrf_token %}
    {% for field in form_obj %}    
       </div>                                                                                                       
            <label for="">{{ field.label }}字段 </label>                                               
           {{ field }} #input框 <span>{{ field.errors.0 }}#错误信息</span> 
        </div>                                           
                                        
        {% endfor %}
<input type="submit">
</form>

</body>
</html> 
三种方法

以上三种方法都需要自己写提交按钮

第三种方法,如果要在页面显示自定义文字时,需要在字段属性写label=“自定义”

Django Form 类详解

绑定的和未绑定的表单实例

绑定的和未绑定的表单 之间的区别非常重要:

  • 未绑定的表单没有关联的数据。当渲染给用户时,它将为空或包含默认的值。
  • 绑定的表单具有提交的数据,因此可以用来检验数据是否合法。如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据。

Widgets

每个表单字段都有一个对应的Widget 类,它对应一个HTML 表单Widget,例如<input type="text">

在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField 具有一个TextInput Widget,它在HTML 中生成一个<input type="text">

字段的数据

不管表单提交的是什么数据,一旦通过调用is_valid() 成功验证(is_valid() 返回True),验证后的表单数据将位于form.cleaned_data 字典中。这些数据已经为你转换好为Python 的类型。

注:此时,你依然可以从request.POST 中直接访问到未验证的数据,但是访问验证后的数据更好一些。

在上面的联系表单示例中,is_married将是一个布尔值。类似地,IntegerField 和FloatField 字段分别将值转换为Python 的int 和float

 form组件的钩子

    # 自定义校验规则
    # 局部钩子
    def clean_user(self):
        user = self.cleaned_data.get("user")
        # 校验用户输入user和数据库user
        name = models.User_info.objects.filter(user=user)
        if not name:
            return user
        else:
            raise ValidationError("用户名已注册")

    def clean_phone(self):
        phone=self.cleaned_data.get("phone")
        ret=re.search("^1[3578]\d{9}$",phone)
        if ret:
            return phone
        else:
            raise ValidationError("手机格式不正确")


    # 全局钩子 先校验字段条件和局部钩子条件(到这一步数据全部合格)
    def clean(self):
        pwd = self.cleaned_data.get("pwd")
        rpwd = self.cleaned_data.get("rpwd")
        if pwd == rpwd:
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")
View Code

form组件补充

1.Django内置字段如下:

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,                      自定制正则表达式
    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类型
    ...
Django内置字段

2、常用选择插件:

 1 # 单radio,值为字符串
 2 # user = fields.CharField(
 3 #     initial=2,
 4 #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 5 # )
 6  
 7 # 单radio,值为字符串
 8 # user = fields.ChoiceField(
 9 #     choices=((1, '上海'), (2, '北京'),),
10 #     initial=2,
11 #     widget=widgets.RadioSelect
12 # )
13  
14 # 单select,值为字符串
15 # user = fields.CharField(
16 #     initial=2,
17 #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
18 # )
19  
20 # 单select,值为字符串
21 # user = fields.ChoiceField(
22 #     choices=((1, '上海'), (2, '北京'),),
23 #     initial=2,
24 #     widget=widgets.Select
25 # )
26  
27 # 多选select,值为列表
28 # user = fields.MultipleChoiceField(
29 #     choices=((1,'上海'),(2,'北京'),),
30 #     initial=[1,],
31 #     widget=widgets.SelectMultiple
32 # )
33  
34  
35 # 单checkbox
36 # user = fields.CharField(
37 #     widget=widgets.CheckboxInput()
38 # )
39  
40  
41 # 多选checkbox,值为列表
42 # user = fields.MultipleChoiceField(
43 #     initial=[2, ],
44 #     choices=((1, '上海'), (2, '北京'),),
45 #     widget=widgets.CheckboxSelectMultiple
46 # )
模板插件

解决静态字段不能实时更新问题 

此问题只适用于From组件,ModelFrom组件不用考虑(自身已经解决)

示例:例如choice类型字段,添加了新的数据,而在页面中不能显示出来,只有再次刷新页面才能获取最新的数据,因为程序运行时静态字段只加载一次

   choice的数据如果从数据库获取可能会造成数据无法实时更新

models.py

from django.db import models

class UserType(models.Model):
    title = models.CharField(max_length=32)
   
  def __str__(self):
    return self.title
class UserInfo(models.Model): name = models.CharField(max_length=32) email = models.CharField(max_length=32) ut = models.ForeignKey(to='UserType')

方法一:重写构造方法:

views.py      

from django.forms import Form
from django.forms import fields
class UserForm(Form): name = fields.CharField(label='用户名',max_length=32) email = fields.EmailField(label='邮箱') ut_id = fields.ChoiceField( # choices=[(1,'二笔用户'),(2,'闷骚')] choices=[] ) def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) # 每次实例化,重新去数据库获取数据并更新 self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title') def user(request): if request.method == "GET": form = UserForm() return render(request,'user.html',{'form':form})

方法二:使用ModelChoiceField字段

views.py

from django.forms import Form
from django.forms import fields
from django.forms.models import ModelChoiceField
class UserForm(Form):
    name = fields.CharField(label='用户名',max_length=32)
    email = fields.EmailField(label='邮箱')
    ut_id = ModelChoiceField(queryset=models.UserType.objects.all())    

Django-modelform组件

创建modelform

首先在models.py创建表

from django.db import models

# Create your models here.

class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.ForeignKey(to="Publish")
    author=models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name=models.CharField(max_length=32)
    email=models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name=models.CharField(max_length=32)

    def __str__(self):
        return self.name
models.py

在views.py创建modelfrom

from django.forms import ModelForm
from .models import *
在视图函数中,定义一个类,比如就叫BookModelForm,这个类要继承ModelForm,在这个类中再写一个原类Meta(规定写法,并注意首字母是大写的)
在这个原类中,有以下属性(部分):

class BookModelForm(ModelForm):
    class Meta:
        model=Book         #对应model中的类
        fields="__all__"     #字段“__all__”显示所有字段,["title","price"..]显示部分字段
        labels={             #自定义在前端显示的名字
            "title":"书名",
            "price":"价格",
            "publish":"出版社",
            "author":"作者"
        }
其它配置:
exclude = None #排除的字段 help_texts = None #帮助提示信息 widgets = None #自定义插件 error_messages = None #自定义错误信息 error_messages = { 'name':{'required':"用户名不能为空",}, 'age':{'required':"年龄不能为空",}, } #widgets用法,比如把输入用户名的input框给为Textarea #首先得导入模块 from django.forms import widgets as wid #因为重名,所以起个别名 widgets = { "name":wid.Textarea(attrs={"class":"c1"}) #还可以自定义属性 }

class BookModelForm(ModelForm):相对于完成了form表单中的class Loginform(forms.Form):
在html页面中的样式用法和form表单样式用法一样

添加数据

保存数据的时候,不用挨个取数据了,只需要save一下

def add(request):
    if request.method=="GET":
        model_form_obj=BookModelForm()
        return render(request,'add.html',locals())
    else:
        model_form_obj=BookModelForm(request.POST)
        # 校验数据
        if model_form_obj.is_valid():
            # 提交数据
            model_form_obj.save()
            return redirect("/index/")
        else:
            return render(request,"add.html",locals())

#############add.html#############
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>

</head>
<body>

<form action="" method="post" novalidate>
{% csrf_token %}
{{ model_form_obj.as_p }} #展示全部字段,也可以用for循环,与form表单用法一样
<input type="submit" value="提交">
</form>

</body>
</html>

编辑数据

如果不用ModelForm,编辑的时候得显示之前的数据吧,还得挨个取一遍值,如果ModelForm,只需要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就可以得到同样的效果
保存的时候要注意,一定要注意有这个对象(instance=obj),否则不知道更新哪一个数据
代码示例:

def edit(request,id):
    # 获取当前id的book对象
    edit_obj=Book.objects.filter(pk=id).first()
    if request.method=="GET":
        # 指定BookModelForm的实例对象edit_obj
        model_form_obj=BookModelForm(instance=edit_obj)
        return render(request,'edit.html',locals())
    else:
        model_form_obj=BookModelForm(request.POST,instance=edit_obj)
        if model_form_obj.is_valid():
            model_form_obj.save()
            return redirect("/index/")
        else:
            return render(request,"edit.html",locals())


######################edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>

</head>
<body>

<form action="" method="post" novalidate>
{% csrf_token %}
{{ model_form_obj.as_p }}
<input type="submit" value="提交">
</form>

</body>
</html>

总结: 从上边可以看到ModelForm用起来是非常方便的,比如增加修改之类的操作。但是也带来额外不好的地方,model和form之间耦合了。如果不耦合的话,mf.save()方法也无法直接提交保存。 但是耦合的话使用场景通常局限用于小程序,写大程序就最好不用了。

 Form组件和ModelForm组件之间的关系

   model.py 
     class Book(models.Model): # "book"
        title=models.CharField(verbose_name="标题",max_length=32)
        price=models.DecimalField(verbose_name="价格",decimal_places=2,max_digits=5,default=12) 
state
=models.IntegerField(choices=((1,"已出版"),(2,"未出版")) ,default=1) publish=models.ForeignKey(to="Publish",default=1) authors=models.ManyToManyField(to='author',default=1)
    ModelForm:
class BookModelForm(ModelForm): class Meta: model=Book field="__all__" Form: class BookModelForm(forms.Form) : title=forms.CharField(max_length=32) price=forms.DecimalField(decimal_places=2,max_digits=5,default=12)
state
=forms.ChoiceField(choices=((1,"已出版"),(2,"未出版")) ,default=1) 去数据----> ((),()) #渲染出普通的下拉菜单 publish=forms.ModelChoiceField()                       ----> Queryset    #渲染出单选的下拉菜单 authors=forms.ModelMultipleChoiceField()                  ----> Queryset    #渲染出多选的下拉菜单
    
   #三者为继承关系

    爷:ChoiceField(Field)
    儿:ModelChoiceField(ChoiceField) 
    孙:ModelMultipleChoiceField(ModelChoiceField) 

ModelForm作为中间桥梁将model中的字段对象类型转换为Forms中对应的字段对象类型

判断字对象类型方法:

描述

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。

isinstance() 与 type() 区别:

  • type() 不会认为子类是一种父类类型,不考虑继承关系。

  • isinstance() 会认为子类是一种父类类型,考虑继承关系。

如果要判断两个类型是否相同推荐使用 isinstance()。

语法

以下是 isinstance() 方法的语法:

isinstance(object, classinfo)

参数

  • object -- 实例对象。
  • classinfo -- 可以是直接或间接类名、基本类型或者有它们组成的元组。

返回值

如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。。


实例

以下展示了使用 isinstance 函数的实例:

>>>a = 2 >>> isinstance (a,int) True >>> isinstance (a,str) False >>> isinstance (a,(str,int,list)) # 是元组中的一个返回 True True

type() 与 isinstance()区别:

class A: pass class B(A): pass isinstance(A(), A) # returns True type(A()) == A # returns True isinstance(B(), A) # returns True

 

posted @ 2018-02-04 11:51  无名!  阅读(534)  评论(0编辑  收藏  举报