• django修身大法之一阳指


    Form表单

    ⼀、概要

    通常情况下,我们需要自己手动在HTML页面中,编写form标签和其内的其它元素。但这费时费力,而且有可能写得不太恰当,数据验证也比较麻烦。有鉴于此,Django在内部集成了一个表单模块,专门帮助我们快速处理表单相关的内容。Django的表单模块给我们提供了下面三个主要功能

    • 准备和重构数据用于页面渲染
    • 为数据创建HTML表单元素
    • 接收和处理用户从表单发送过来的数据

    ⼆、Form相关的对象包括

    • Widget:用来渲染成HTML元素的部件,如:forms.Textarea对应HTML中的textarea标签
    • Form对象中的一个字段,如:EmailField表示ema字段,如果这个字段不是有效的Emd地址格式,就会产生错误。
    • Form:一系列Fed对象的集合,负责验证和显示HTML元素
    • Form media:用来渲染表单的CSS和 avaScrip资源。

    三、基本使用

    1、说明

    Form对象封装了⼀系列Field和验证规则,Form类都必须直接或间接继承自

    django.forms.Form,定义Form有两种方式:

    • 直接继承Form
    class XXXForm(forms.Form):
     	pass
    
    • 结合Model,继承django.forms.ModelForm
    class XXX(models.Model):
        字段 = models.CharField(max_length=30)
        字段 = models.CharField(max_length=20)
    class XXXForm(ModelForm):
        class Meta:
            model = XXX
            field = ('字段', '字段') # 只显示model中指定的字段,显示所有是用'__all__
    

    2、示例代码

    1. models

      class Shop(models.Model):
          title = models.CharField('标题'
          , max_length=30)
          content = models.CharField('内容'
          , max_length=20)
      
          class Meta:
          	db_table = 'T_SHOP'
      
    2. ModelForm

      class ShopForm(ModelForm):
          class Meta:
              model = models.Shop
              fields = ('title', 'content') # 只显示model中指定的字段
      
    3. views

      from django.shortcuts import render
      # Create your views here.
      from .forms import ShopForm
      def add_shop(request):
           if request.method == "POST":
              form = ShopForm(request.POST)
              if form.is_valid(): # 所有验证都通过
                  # 处理表单数据
                  title = form.cleaned_data['title']
                  print(title)
                  # content = form.cleaned_data['content']
                   # 保存数据
                   form.save()
                   return render(request, 'shop/add_shop.html',
                  {"shop_form": form})
          else:
           form = ShopForm()
           return render(request, 'shop/add_shop.html', {"shop_form":
          form})
      
    4. 模板中使用

      <form action=
      "{% url 'add' %}" method="post">
      {% csrf_token %}
      {{ shop_form }}
      <input type="submit" value=
      "提交"/>
      </form>
      

    四、常用的field类

    • 核心字段参数
    参数名 说明
    required 给字段添加必填属性,不能空着,若要表示⼀个字段不是
    必需的,设置required=False
    label label参数用来给字段添加‘⼈类友好’的提示信息。如果没
    有设置这个参数,那么就用字段的首字母大写名字
    label_suffix Django默认为上面的label参数后面加个冒号后缀,如果
    想⾃定义,可以使用 label_suffix 参数
    initial 为HTML页面中表单元素定义初始值。也就是input元素的
    value参数的值
    widget 指定渲染Widget时使用的widget类,也就是这个form字
    段在HTML页面中是显示为文本输⼊框?密码输⼊框?单
    选按钮?多选框?还是别的
    help_text 该参数用于设置字段的辅助描述文本
    error_messages 该参数允许你覆盖字段引发异常时的默认信息。 传递的是
    ⼀个字典,其值为你想覆盖的错误信息
    validators 指定⼀个列表,其中包含了为字段进⾏验证的函数
    localize localize参数帮助实现表单数据输⼊的本地化。
    disabled 设置有该属性的字段在前端页面中将显示为不可编辑状
    态。
    • 核心字段
    字段名 参数
    BooleanField 默认的Widget:CheckboxInput
    空值:False
    规范:Python 的True 或 False。
    错误信息的键:required
    IntegerField 默认的Widget:当Field.localize是False时为NumberInput,否则
    为TextInput。
    空值:None
    规范化为:Python 整数或⻓整数。
    验证给定值是⼀个整数。 允许前导和尾随空格,类似Python的int()函数。
    错误信息的键:max_value, invalid, required, min_value
    CharField 默认的Widget:TextInput
    空值:''(⼀个空字符串)
    规范化为:⼀个Unicode 对象。 如果提供,验证max_length 或
    min_length。 否则,所有的输⼊都是合法的。
    错误信息的键:required, max_length, min_length
    ChoiceField 默认的Widget:Select
    空值:' '(⼀个空字符串)
    规范化为:⼀个Unicode 对象。 验证给定的值在选项列表中存在。
    错误信息的键:required, invalid_choice
    invalid_choice:错误消息可能包含%(value)s,它将被选择的选项
    替换掉。接收⼀个额外的必选参数:choices⽤来作为该字段选项的
    ⼀个⼆元组组成的可迭代对象(例如,列表或元组)或者⼀个可调
    ⽤对
    DateField 默认的Widget:DateInput
    空值:None
    规范化为:⼀个Python datetime.date 对象。
    错误信息的键:required, invalid
    input_formats:⼀个格式的列表,验证给出的值是⼀个
    datetime.date、datetime.datetime 或指定⽇期格式的字符串。如
    果不提供,默认的⽇期格式:['%Y-%m-
    %d','%m/%d/%Y','%m/%d/%y']
    DateTimeField 默认的Widget:DateInput
    空值:None
    规范化为:⼀个Python datetime.datetime对象。
    错误信息的键:required, invalid
    input_formats:默认的格式['%Y-%m-%d %H:%M:%S', '%Y-%m-
    %d %H:%M', '%Y-%m-%d', '%m/%d/%Y %H:%M:%S','%m/%d/%Y
    %H:%M','%m/%d/%Y', '%m/%d/%y %H:%M:%S', '%m/%d/%y
    %H:%M', '%m/%d/%y']
    DecimalField 默认的Widget:当Field.localize是False时为NumberInput,否则
    为TextInput。
    空值:None
    规范化为:Python decimal对象。
    错误信息的键: max_whole_digits , max_digits ,
    max_decimal_places , max_value , invalid, required, min_value
    可选参数:max_value,min_value:允许的值的范围,需要赋值
    decimal.Decimal对象,不能直接给个整数类型。
    max_digits:值允许的最⼤位数(⼩数点之前和之后的数字总共的位数,前导的零将被删除)。
    decimal_places:允许的最⼤⼩数位。
    FloatField 默认的Widget:当Field.localize是False时为NumberInput,否则
    为TextInput。
    空值:None
    规范化为:Float 对象。
    验证给定的值是⼀个浮点数。
    错误信息的键:max_value, invalid, required, min_value
    可选的参数:max_value和min_value
    EmailField 默认的Widget:EmailInput
    空值:''(⼀个空字符串)
    规范化为:Unicode 对象。 使⽤正则表达式验证给出的值是⼀个合
    法的邮件地址。
    错误信息的键:required, invalid
    两个可选的参数⽤于验证,max_length 和min_length。
    ImageField 默认⼩部件: ClearableFileInput
    空值: None
    规范化为: UploadedFile 将⽂件内容和⽂件名称封装到单个对象
    中的对象。
    验证⽂件数据是否已绑定到表单,并且该⽂件是Pillow可以理解的
    图像格式。
    错误信息键: required , invalid , missing , empty ,
    invalid_image
    FileField 默认⼩部件: ClearableFileInput
    空值: None
    规范化为: UploadedFile 将⽂件内容和⽂件名称封装到单个对象
    中的对象。
    可以验证⾮空⽂件数据已被绑定到表单。
    错误信息键: required , invalid , missing , empty ,
    max_length
    ModelChoiceField 默认的Widget:Select
    空值:None
    规范化为:⼀个模型实例。
    验证给定的id存在于查询集中。
    错误信息的键:required, invalid_choice 可以选择⼀个单独的模型
    对像,适⽤于表示⼀个外键字段。 ModelChoiceField默认widet不
    适⽤选择数量很⼤的情况,在⼤于100项时应该避免使⽤它。
    可选参数:
    empty_label:默认情况下,ModelChoiceField使⽤的上下点击样式

    五、Form常用属性和方法

    名称 说明 示例
    cleaned_data(字典) 表单中验证通过的⼲净数
    form.cleaned_data
    form.cleaned_data.get('username')
    changed_data 有变化的字段的列表 form.changed_data
    fields(字典) 表单中的字段属性 form.fiedls['username']
    is_bound 表单是否绑定数据 form.is_bound
    errors(字典) 错误信息 form.errors
    is_valid() 表单中数据是否验证通
    过,通过返回True,否
    则返回False
    form.is_valid()
    has_changed() 检查表单数据是否已从初
    始数据更改
    form.has_changed()
    errors.as_json(escape_html=False) 返回JSON序列化后的错
    误信息字典
    form.errors.as_json()

    六、表单渲染的选项

    1、概要

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

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

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

    • 常用渲染项:

      有⽤的属性包括:{{ field }}
      
      {{ field.label }}
      该领域的标签,例如。Email address
      
      {{ field.label_tag }}
      字段的标签包含在适当的HTML <label>标记中。这包括表格label_suffix。例
      如,默认label_suffix值为冒号:
      <label for="id_email">Email address:</label>
      {{ field.id_for_label }}
      将⽤于此字段的ID(id_email在上⾯的示例中)。如果您⼿动构建标签,则可能
      需要使⽤此代替label_tag。例如,如果你有⼀些内联JavaScript并且想要避免
      硬编码字段的ID,它也很有⽤。
      {{ field.value }}
      该字段的值。例如someone@example.com。
      {{ field.html_name }}
      将在输⼊元素的名称字段中使⽤的字段的名称。这会将表单前缀考虑在内,如果已
      设置的话。
      {{ field.help_text }}
      与该字段关联的任何帮助⽂本。
      {{ field.errors }}
      输出包含与此字段对应的任何验证错误的a 。您可以使⽤循环⾃定义错误的表示。
      在这种情况下,循环中的每个对象都是包含错误消息的简单字符串。<ul
      class="errorlist">{% for error in field.errors %}
      {{ field.is_hidden }}
      True如果表单字段是隐藏字段, False则此属性。它作为模板变量并不是特别有
      ⽤,但在条件测试中可能很有⽤,例如:
      {% if field.is_hidden %}
       {# Do something special #}
      {% endif %}
      {{ field.field }}
      Field来⾃此BoundField包装的表单类的实例。您可以使⽤它来访问 Field属
      性,例如 。{{ char_field.field.max_length }}
      

    2、渲染

    1. form.as_ul

      <form action="/add/" method="post">
          {% csrf_token %}
          <ul>
              {{ form.as_ul }}
          </ul>
          <input type="submit" value=
                  "注册⼀个学⽣">
      </form>
      
    2. 手动渲染

      <form action="/add/" method="post">
          {% csrf_token %}
          <div>
              <label for="{{ form.name.id_for_label }}">姓名:</label>
              {{ form.name }}
              {{ form.name.errors }}
          </div>
          <div>
              <label for="{{ form.sex.id_for_label }}">性别:</label>
              {{ form.sex }}
              {{ form.sex.errors }}
          </div>
          <div>
              <label for="{{ form.age.id_for_label }}">年龄:</label>
              {{ form.age }}
              {{ form.age.errors }}
          </div>
          <input type="submit">
      </form>
      
      
    3. 循环渲染

      {% for field in form %}
          <div class="fieldWrapper">
              {{ field.errors }}
              {{ field.label_tag }} {{ field }}
              {% if field.help_text %}
                  <p class="help">{{ field.help_text|safe }}</p>
              {% endif %}
          </div>
      {% endfor %}
      
    4. 循环隐藏和可见字段

      {# Include the hidden fields #}
      {% for hidden in form.hidden_fields %}
          {{ hidden }}
      {% endfor %}
      {# Include the visible fields #}
      {% for field in form.visible_fields %}
          <div class="fieldWrapper">
              {{ field.errors }}
              {{ field.label_tag }} {{ field }}
          </div>
      {% endfor %}
      
    5. 可重用的表单模板

      # In your form template:
      {% include "form_snippet.html" %}
      # In form_snippet.html:
      {% for field in form %}
          <div class="fieldWrapper">
              {{ field.errors }}
              {{ field.label_tag }} {{ field }}
          </div>
      {% endfor %}
      

    七、重写验证

    1. 说明

    2. 举个栗子

    3. 注意事项

      • 函数名就必须为clean_字段名
      • 必须有返回值
      • 只能拿自己当前字段值
      • raise ValidationError('xxx')
      class UserFrom(forms.Form):
      # ⾃定义⽅法(局部钩⼦),密码必须包含字⺟和数字
          def clean_password(self):
              if self.cleaned_data.get('password').isdigit() or
              self.cleaned_data.get('password').isalpha():
                  raise ValidationError('密码必须包含数字和字⺟')
       
              else:
                  return self.cleaned_data['password']
      	def clean_valid_code(self): # 检验验证码正确;之前⽣成的验证码保存在了了session中
      		if self.cleaned_data.get('valid_code').upper() ==
      self.request.session.get('valid_code'): 
      			return self.cleaned_data['valid_code']
       		else: 
      			raise ValidationError('验证码不正确')
      # ⾃定义⽅法(全局钩⼦, 检验两个字段),检验两次密码⼀致;
       
              def clean(self): 
              	if self.cleaned_data.get('password') !=
              self.cleaned_data.get('password2'):
              	raise ValidationError('密码不⼀致')
      
              else:
              	return self.cleaned_data
       
      # 注意,上⾯的字典取值⽤get, 因为假如在clean_password中判断失败,那么没有返回值,最下⾯的clean⽅法直接取值就会失败
      

    完整代码

    1. 示例代码

      class UserFrom(forms.Form):
       
          name = forms.BooleanField(label='⽤户名', required=True,error_messages={'required': u'必选'})
          password = forms.CharField(label= '密码',widget=forms.PasswordInput(attrs={'placeholder': '请输⼊密码'}))
          confirm_password = forms.CharField(label='密码',widget=forms.PasswordInput(attrs={'placeholder': '请再次输⼊密
          码'}))
         	# 下拉框
          city = forms.ChoiceField(choices=[(1, '上海'), (2,  '北京',), (3, '⼴州')])
      	 create_date = forms.DateField(label= '选择时间', input_formats=['%Y-%m-%d'])
                                                                                      price = forms.DecimalField(label='价格' , max_digits=10,decimal_places=2)
          head = forms.FileField(allow_empty_file=True)
          img = forms.ImageField(allow_empty_file=True)
          email = forms.EmailField(required=False,
              error_messages={'required': u'邮箱不能为空' ,'invalid': u'邮箱格式错误'},
              widget=forms.TextInput(
              attrs={'class': "form-control",'placeholder': u'邮箱'}))
              		       address=forms.ModelChoiceField(queryset=models.Address.objects.filter(uid
              =2), empty_label=None , to_field_name=None)
      
          def clean_password(self):
      
              if self.cleaned_data.get('password').isdigit() or
              self.cleaned_data.get('password').isalpha():
      
                  raise ValidationError('密码必须包含数字和字⺟')
      
          	else:
      
          		return self.cleaned_data['password']
      
          def clean_valid_code(self):
      
              if self.cleaned_data.get('valid_code').upper() ==
              self.request.session.get('valid_code'):
      
          		return self.cleaned_data['valid_code']
      
         		 else:
      
         			 raise ValidationError('验证码不正确')
      
          def clean(self):
      
              if self.cleaned_data.get('password') !=
              self.cleaned_data.get('confirm_password'):
      
          		raise ValidationError('密码不⼀致')
      
          	else:
      			return self.cleaned_data
      

    ⼋、综合

    #-----------------------------------------models.py
    from django.db import models
    class Info(models.Model):
         name = models.CharField(max_length=64)
         sex = models.CharField(max_length=64)
         birthday = models.CharField(max_length=64)
         age=models.CharField(max_length=64)
         qualification=models.CharField(max_length=64)
         job=models.CharField(max_length=64)
         email=models.CharField(max_length=64,default='')
    class Hobby(models.Model):
     	item=models.CharField(max_length=64)
    
    #-----------------------------------------form.py
    from django import forms
    from app01 import models
    from django.core.exceptions import ValidationError
    
    class InfoForm(forms.Form):
     	def validate_name(value):
             try:
                 models.Info.objects.get(name=value)
                raise ValidationError('%s 的信息已经存在!'%value)
             except models.Info.DoesNotExist:
                pass
            
            
         sex_choice=((0,'男'),
         (1,'⼥'))#select的数据可以像这样写,也可以在另外⼀张表中动
        态去拿
         name = forms.CharField(validators=[validate_name],label='姓
        名',error_messages={'required':'必填'})
         age = forms.CharField(label='年龄',error_messages={'required':'必
        填'})
         # sex = forms.CharField(label='性别',error_messages=
        {'required':'必填',},)
                                                          
         sex=forms.IntegerField(widget=forms.widgets.Select(choices=sex_choice
        ,
         attrs={'class':'setform2'} ))
         birthday = forms.CharField(label='⽣⽇',error_messages=
        {'required':'必填'})
         qualification = forms.CharField(label='学历',error_messages=
        {'required':'必填'},widget=forms.TextInput(attrs=
        {'class':'formset','placeholder':'本科' }))
         email=forms.EmailField(max_length=100,min_length=10)
         job = forms.CharField(label='⼯作',error_messages={'required':'必
        填'})
         def __init__(self,*args,**kwargs):
    		 super(Info_form,self).__init__(*args,**kwargs) 
            self.fields['hobby']=forms.CharField(widget=forms.widgets.Select(choi
            ces=models.Hobby.objects.values_list('id','item')))
    
                                                          
    #-----------views.py
    from django.shortcuts import render,HttpResponse
    def add_info(req):
     	if req.method=='POST':
    		 Info_form_obj=Info_form(req.POST)
     		if Info_form_obj.is_valid():
                Info.objects.create(name=Info_form_obj.cleaned_data['name'],
                age=Info_form_obj.cleaned_data['age'],
                sex=Info_form_obj.cleaned_data['sex'],
                birthday=Info_form_obj.cleaned_data['birthday'],
                qualification=Info_form_obj.cleaned_data['qualification'],
                 job=Info_form_obj.cleaned_data['job']
                 )
                 return HttpResponse('添加成功!')
     		else:
    			 error_obj=Info_form_obj.errors
    			 print('***************')
                 print(type(error_obj))#<class
                'django.forms.utils.ErrorDict'>
                 print(error_obj['name'][0])#必填
                 print(error_obj.get('age'))#<ul class="errorlist"><li>必
                填</li></ul>
                 return render(req,'add_info.html',
                {'form_obj':Info_form_obj,'error_obj':error_obj})
                 Info_form_obj=Info_form()
                 return render(req,'add_info.html',{'form_obj':Info_form_obj})
                                                          
                                                          
    #------add_info.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    
        <meta charset="UTF-8">
    
        <title>添加个⼈信息</title>
    
        <style>
    
            .formset {
    
                color: rebeccapurple;
    
                border: dashed cadetblue;
            }
    
        </style>
    </head>
    <body>
    
    <form action=
                  "{% url 'add_info' %}" method="post">
    
        <p>姓名{{ form_obj.name }}{{ error_obj.name.0 }}</p>
    
        <p>年龄{{ form_obj.age }}{{ error_obj.age.0 }}</p>
    
        <p>⽣⽇{{ form_obj.birthday }}{{ error_obj.birthday.0 }}</p>
    
        <p>⼯作{{ form_obj.job }}<span>{{ error_obj.job }}</span>
        </p>
    
        <p>学历{{ form_obj.qualification }}<span>{{
                error_obj.qualification }}</span></p>
    
        <p>性别{{ form_obj.sex }}<span>{{ error_obj.sex }}</span>
        </p>
    
        <p>邮箱{{ form_obj.email }}<span>{{ error_obj.email }}
    </span></p>
    
        <p>爱好{{ form_obj.hobby }}<span>{{ error_obj.hobby }}
    </span></p>
    
        {{ form_obj.as_p }}
    
        <input type="submit" value=
                "提交"><br>
    
        {% csrf_token %}
    
    </form>
    </body>
    </html>
    
  • 相关阅读:
    CodeSmith中SchemaExplorer属性的介绍
    Bugku-INSERT INTO 注入
    XCTF-PHP2
    网络安全实验室CTF-注入关
    XSS挑战
    SQL注入
    CTFHub-技能树-命令执行
    CTFHub-技能树-文件上传
    史上最难的一道Java面试题 (分析篇)
    高可用的一些思考和理解
  • 原文地址:https://www.cnblogs.com/williamweson/p/13614719.html
Copyright © 2020-2023  润新知