zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    软件工程概论课堂测试一
    每日站立会议个人博客五
    每日站立会议个人博客四
    典型用户以及场景分析
    每日站立会议个人博客三
    每日站立会议个人博客二
    每日站立会议个人博客一
    软件工程概论第三周学习进度条
    软件工程概论第二周学习进度条
    四则运算1
  • 原文地址:https://www.cnblogs.com/caochao-/p/8412830.html
Copyright © 2011-2022 走看看