zoukankan      html  css  js  c++  java
  • Django-form表单

    构建一个表单

    假设你想在你的网站上创建一个简单的表单,以获得用户的名字。你需要类似这样的模板:

    1
    2
    3
    4
    5
    <form action="/your-name/" method="post">
        <label for="your_name">Your name: </label>
        <input id="your_name" type="text" name="your_name">
        <input type="submit" value="OK">
    </form>

    这是一个非常简单的表单。实际应用中,一个表单可能包含几十上百个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作。

    我们可能需要在表单提交之前,在浏览器端作一些验证。我们可能想使用非常复杂的字段,以允许用户做类似从日历中挑选日期这样的事情,等等。

    这个时候,让Django 来为我们完成大部分工作是很容易的。

    so,两个突出优点:

        1 form表单提交时,数据出现错误,返回的页面中仍可以保留之前输入的数据。

        2 方便地限制字段条件

    在Django 中构建一个表单

    Form 类

    我们已经计划好了我们的 HTML 表单应该呈现的样子。在Django 中,我们的起始点是这里:

    1
    2
    3
    4
    5
    6
    #forms.py
     
    from django import forms
     
    class NameForm(forms.Form):
        your_name = forms.CharField(label='Your name', max_length=100)

     它定义一个Form 类,只带有一个字段(your_name)。

    字段允许的最大长度通过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> 标签和提交按钮。我们必须自己在模板中提供它们。

    视图

    发送给Django 网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图。这允许我们重用一些相同的逻辑。

    当处理表单时,我们需要在视图中实例化它:

    #views.py
    
    from django.shortcuts import render
    from django.http import HttpResponseRedirect
    
    from .forms import NameForm
    
    def get_name(request):
        # if this is a POST request we need to process the form data
        if request.method == 'POST':
            # create a form instance and populate it with data from the request:
            form = NameForm(request.POST)
            # check whether it's valid:
            if form.is_valid():
                # process the data in form.cleaned_data as required
                # ...
                # redirect to a new URL:
                return HttpResponseRedirect('/thanks/')
    
        # if a GET (or any other method) we'll create a blank form
        else:
            form = NameForm()
    
        return render(request, 'name.html', {'form': form})

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

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

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

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

    模板

    我们不需要在name.html 模板中做很多工作。最简单的例子是:

    1
    2
    3
    4
    5
    <form action="/your-name/" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="Submit" />
    </form>

     根据{{ form }},所有的表单字段和它们的属性将通过Django 的模板语言拆分成HTML 标记 。

    注:Django 原生支持一个简单易用的跨站请求伪造的防护。当提交一个启用CSRF 防护的POST 表单时,你必须使用上面例子中的csrf_token 模板标签。

    现在我们有了一个可以工作的网页表单,它通过Django Form 描述、通过视图处理并渲染成一个HTML <form>

    Django Form 类详解

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

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

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

    字段详解

    考虑一个比上面的迷你示例更有用的一个表单,我们完成一个更加有用的注册表单:

    #forms.py
    
    from django import forms
    
    class RegisterForm(forms.Form):
        username = forms.CharField(max_length=100,
                                   error_messages={"min_length":"最短为5个字符","required":"该字段不能为空"},
                                   )
        password = forms.CharField(max_length=100,
                                   widget=widgets.PasswordInput(attrs={"placeholder":"password"})
                                    )
    
        telephone=forms.IntegerField(
            error_messages={
                "invalid":"格式错误"
            }
    
                                    )
    
    
        gender=forms.CharField(
              initial=2,
              widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
                 )
    
        email = forms.EmailField()
        is_married = forms.BooleanField(required=False)

    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

    使用表单模板

    你需要做的就是将表单实例放进模板的上下文。如果你的表单在Context 中叫做form,那么{{ form }}将正确地渲染它的<label> 和 <input>元素。

    表单渲染的选项

    对于<label>/<input> 对,还有几个输出选项:

    • {{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中
    • {{ form.as_p }} 将它们渲染在<p> 标签中
    • {{ form.as_ul }} 将它们渲染在<li> 标签中

    注意,你必须自己提供<table> 或<ul> 元素。

    {{ form.as_p }}会渲染如下:

     1 <form action="">
     2     <p>
     3         <label for="id_username">Username:</label>
     4         <input id="id_username" maxlength="100" name="username" type="text" required="">
     5     </p>
     6  
     7  
     8     <p>
     9         <label for="id_password">Password:</label>
    10         <input id="id_password" maxlength="100" name="password" placeholder="password" type="password" required="">
    11     </p>
    12  
    13  
    14     <p>
    15         <label for="id_telephone">Telephone:</label> <input id="id_telephone" name="telephone" type="number" required="">
    16     </p>
    17  
    18  
    19     <p>
    20         <label for="id_email">Email:</label> <input id="id_email" name="email" type="email" required="">
    21     </p>
    22  
    23  
    24     <p>
    25         <label for="id_is_married">Is married:</label> <input id="id_is_married" name="is_married" type="checkbox">
    26     </p>
    27  
    28  
    29     <input type="submit" value="注册">

    我们没有必要非要让Django 来分拆表单的字段;如果我们喜欢,我们可以手工来做(例如,这样允许重新对字段排序)。每个字段都是表单的一个属性,可以使用{{ form.name_of_field }} 访问,并将在Django 模板中正确地渲染。例如:

    手工渲染字段

    1
    2
    3
    4
    5
    <div class="fieldWrapper">
        {{ form.Username.errors }}
        {{ form.Username.label_tag }}
        {{ form.Username }}
    </div>

    渲染表单的错误信息

    1、

    1
    2
    3
    registerForm=RegisterForm(request.POST)
    print(type(registerForm.errors))                      #<class 'django.forms.utils.ErrorDict'>
    print(type(registerForm.errors["username"]))          #<class 'django.forms.utils.ErrorList'>

    2、

    使用{{ form.name_of_field.errors }} 显示表单错误的一个清单,并渲染成一个ul。看上去可能像:

    1
    2
    3
    <ul class="errorlist">
        <li>Sender is required.</li>
    </ul>

     form组件的钩子

    def foo(request):
    
    
        if request.method=="POST":
    
            regForm=RegForm(request.POST)
    
            if regForm.is_valid():
                pass
                # 可用数据: regForm.cleaned_data,
                # 将数据插入数据库表中
    
    
            else:
                pass
                # 可用数据: regForm.errors
                # 可以利用模板渲染讲errors嵌套到页面中返回
                # 也可以打包到一个字典中,用于ajax返回
    
        else:
            regForm=RegForm()
        return render(request,"register.html",{"regForm":regForm})
    
        
    
        '''
        实例化时:
    
            self.fields={
                "username":"字段规则对象",
                "password":"字段规则对象",
    
            }
    
    
        is_valid时:
    
            self._errors = {}
            self.cleaned_data = {}
    
    
            #局部钩子:
    
            for name, field in self.fields.items():
                  try:
    
                        value = field.clean(value)
                        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)
    
            # 全局钩子:
    
            self.clean()     # def self.clean():return self.cleaned_data
    
            return  not self.errors    # True或者False
    
    
        '''

    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类型
        ...
    View Code

    2、Django内置插件:

    TextInput(Input)
    NumberInput(TextInput)
    EmailInput(TextInput)
    URLInput(TextInput)
    PasswordInput(TextInput)
    HiddenInput(TextInput)
    Textarea(Widget)
    DateInput(DateTimeBaseInput)
    DateTimeInput(DateTimeBaseInput)
    TimeInput(DateTimeBaseInput)
    CheckboxInput
    Select
    NullBooleanSelect
    SelectMultiple
    RadioSelect
    CheckboxSelectMultiple
    FileInput
    ClearableFileInput
    MultipleHiddenInput
    SplitDateTimeWidget
    SplitHiddenDateTimeWidget
    SelectDateWidget
    View Code

    3、常用选择插件:

    # 单radio,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单radio,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.RadioSelect
    # )
     
    # 单select,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单select,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.Select
    # )
     
    # 多选select,值为列表
    # user = fields.MultipleChoiceField(
    #     choices=((1,'上海'),(2,'北京'),),
    #     initial=[1,],
    #     widget=widgets.SelectMultiple
    # )
     
     
    # 单checkbox
    # user = fields.CharField(
    #     widget=widgets.CheckboxInput()
    # )
     
     
    # 多选checkbox,值为列表
    # user = fields.MultipleChoiceField(
    #     initial=[2, ],
    #     choices=((1, '上海'), (2, '北京'),),
    #     widget=widgets.CheckboxSelectMultiple
    # )
    View Code
  • 相关阅读:
    搜索回车跳转页面
    登录验证码
    【排序算法】排序算法之插入排序
    PAT 乙级 1044 火星数字 (20 分)
    PAT 甲级 1035 Password (20 分)
    PAT 甲级 1041 Be Unique (20 分)
    PAT 甲级 1054 The Dominant Color (20 分)
    PAT 甲级 1027 Colors in Mars (20 分)
    PAT 甲级 1083 List Grades (25 分)
    PAT 甲级 1005 Spell It Right (20 分)
  • 原文地址:https://www.cnblogs.com/gpd-Amos/p/8431859.html
Copyright © 2011-2022 走看看