• Django Form


    简单使用

    1. 自定义一个django.forms.Form子类
    2. 根据forms的类型定义需要的form的标签
    3. 可以指定参数,如label和max_length
    4. 视图中使用is_valid()方法,form全部通过,返回True
    5. 使用cleaned_data属性,这是is_valid之后的数据
    # web/forms.py
    
    from django import forms
    
    
    class USER(forms.Form):
        name = forms.CharField(max_length=32, label="用户名")
        email = forms.EmailField(max_length=128)
    
    
    
    # web/views.py
    
    from django.shortcuts import render
    from django.http import HttpResponse
    from web.forms import USER
    
    
    # Create your views here.
    
    def register(request):
    
        if request.method == "GET":
            form = USER()
            return render(request, "web/register.html", {"form": form})
    
        if request.method == "POST":
            form = USER(request.POST)
    
            if form.is_valid():
                print(form.cleaned_data)
    
        return HttpResponse("ok!")
    
    
    <!-- web/register.html -->
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        
        <title>register</title>
    </head>
    <body>
    <form action="{% url "web:register" %}" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    

    这样我们用GET方式访问web/register就会生成形如这样的表单:

    <form action="/web/register" method="post">
        <input type="hidden" name="csrfmiddlewaretoken" value="LpAyJTwviENuwiy92DEkOCXAwxte8rGDbBMSgSXvbdOtrnSa81Rd18ZWCdiO7Hyl">
        <label for="id_name">用户名:</label><input type="text" name="name" maxlength="32" required="" id="id_name">
    <label for="id_email">Email:</label><input type="email" name="email" maxlength="128" required="" id="id_email">
        <input type="submit" value="提交">
    </form>
    
    

    注意:form标签和 submit是手动生成的!!

    form字段

    从上面可以看出,要生成什么input标签,都是我们通过定义类属性来实现的。该类属性就相当于model的字段:

    字段 对应input 参数 空值 错误
    BooleanField checkbox False required
    CharField text max_length、min_length 、strip、empty_value empty_value参数 required、max_length、min_length
    ChoiceField select choices = [("value", "show in page"), ] 空字符串 required、invalid_choice
    DateTimeField text input_formats 时间格式 None required、invalid
    DateField text input_formats 时间格式 None required、invalid
    EmailField email max_length、min_length 和 empty_value empty_value参数 required、invalid
    IntegerField number or text max_value 和 min_value None required、invalid、max_value、min_value
    FileField file max_length 和 allow_empty_file None required、invalid、missing、empty、max_length
    ImageField 需要Pillow file None required、invalid、missing、empty、invalid_image

    详见官网:内置 Field 类

    form字段的参数

    这里指的是通用参数

    参数 说明
    required 空值时ValidationError 异常,可指定False
    label 指定label标签
    label_suffix 指定label后缀,如user:变为user=
    initial 指定默认值
    widget 指定部件(处理 HTML 的渲染),点击这里查看
    help_text 在字段标签后面加上span标签作为提示
    error_messages 一个字典,key为该字段可以触发的错误,value为提示信息
    disabled 是否可以禁用
    validators 指定验证器,点击这里查看验证器
    localize 实现表单数据输入和渲染输出的本地化

    form的api

    如:

    
    from django import forms
    
    
    class UserForm(forms.Form):
        name = forms.CharField(max_length=32, label="用户名")
    
    u = UserForm()
    

    那么u有什么api呢??

    1. .is_bound
      有无绑定表单实例,UserForm({”name": "abc"})为True,否则为False
    2. .clean()
      运行自定义的钩子函数进行验证,如何自定义见下文。
    3. .is_valid()
      验证数据
    4. .cleaned_data
      已经经过检验的数据
    5. .errors
      获取错误信息的html
      .errors.as_data() 获得错误信息字典,如{'email': ['错误邮箱格式']}
      .errors.as_json() 获得错误信息的json数据,如{"email": [{"message": "u9519u8befu90aeu7bb1u683cu5f0f", "code": "invalid"}]}
    6. .add_error(field, error)
      手动添加,会在cleaned_data中删除字段。error 参数可以是一个字符串,但最好是 ValidationError 的实例,如何抛出见这里
    7. .has_error(field, code=None)
      本方法返回一个布尔值,表示一个字段是否有特定错误 code 的错误。如果 code 是 None,如果字段包含任何错误,它将返回 True。
    8. .has_changed()
      是否与初始数据发生变化,form的initial参数指定,也可以在实例化时指定:u = UserForm(initial={'name': 'lczmx!'})
    9. .fields
      访问字段
    10. .as_p().as_ul().as_table()
      贴出源码:
      def as_table(self):
      	"Return this form rendered as HTML <tr>s -- excluding the <table></table>."
      	return self._html_output(
      		normal_row='<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',
      		error_row='<tr><td colspan="2">%s</td></tr>',
      		row_ender='</td></tr>',
      		help_text_html='<br><span class="helptext">%s</span>',
      		errors_on_separate_row=False,
      	)
      
      def as_ul(self):
      	"Return this form rendered as HTML <li>s -- excluding the <ul></ul>."
      	return self._html_output(
      		normal_row='<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>',
      		error_row='<li>%s</li>',
      		row_ender='</li>',
      		help_text_html=' <span class="helptext">%s</span>',
      		errors_on_separate_row=False,
      	)
      
      def as_p(self):
      	"Return this form rendered as HTML <p>s."
      	return self._html_output(
      		normal_row='<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>',
      		error_row='%s',
      		row_ender='</p>',
      		help_text_html=' <span class="helptext">%s</span>',
      		errors_on_separate_row=True,
      	)
      
      .as_table()是__str__的返回值,所以是默认方式
      <!-- 正常 -->
      <tr><th><label for="id_name">用户名:</label></th><td><input type="text" name="name" maxlength="32" required id="id_name"></td></tr>
      
      <!-- 异常 -->
      <tr><th><label for="id_name">用户名:</label></th><td><ul class="errorlist"><li>Ensure this value has at most 2 characters (it has 5).</li></ul><input type="text" name="name" value="lczmx" maxlength="2" required id="id_name"></td></t
      

    自定义form样式

    参见:自定义部件实例

    1. auto_id
      设置字段id

      • Ture自动生成
      • False 不生成
      • 'field_%s' 形如:'field_age'、'field_name'

      但这是参数:f = ContactForm(auto_id=False)

    2. 错误信息类名

      rom django import forms
      
      class ContactForm(forms.Form):
      	error_css_class = 'error'
      	required_css_class = 'required'
      
    3. css

      • 方法1:
      class CommentForm(forms.Form):
      	name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
      	url = forms.URLField()
      	comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
      	
      
      • 方法二:
      class CommentForm(forms.Form):
      	name = forms.CharField()
      	url = forms.URLField()
      	comment = forms.CharField()
      
      	name.widget.attrs.update({'class': 'special'})
      	comment.widget.attrs.update(size='40')
      
      • 对于ModelForm:
      class CommentForm(forms.ModelForm):
      	def __init__(self, *args, **kwargs):
      		super().__init__(*args, **kwargs)
      		self.fields['name'].widget.attrs.update({'class': 'special'})
      		self.fields['comment'].widget.attrs.update(size='40')
      
    4. 更细分
      可以为每个字段(包括label)提供样式,即将form拆开渲染。

      • {% for field in form %} 获得field,一下都是根据field的。
      • {{ field.label }} 字段的label参数
      • {{ field.label_tag }} label标签 包含label_suffix参数
      • {{ field.id_for_label }}该字段的 ID
      • {{ field.value }} 字段的vlaue
      • {{ field.html_name }} 字段的name
      • {{ field.help_text }} 与该字段关联的帮助文本。
      • {{ field.errors }} 输出一个 <ul class="errorlist"> ,其中包含这个字段的所有验证错误信息。你可以使用 {% for error in field.errors %} 循环来自定义错误信息的显示。在这种情况下,循环中的每个对象是包含错误信息的字符串。
      • {{ field.is_hidden }} 如果是隐藏字段,这个属性为 True ,否则为 False
      • {{ field.field }} 访问 Field 的属性, BoundField的API,另外 BoundField

      详见:遍历表单字段

    自定义验证方法

    要自定义验证方法,那么就要先指定验证的流程,然后我们才知道要操作什么数据,返回什么数据。

    1. .is_valid方法
      return self.is_bound and not self.errors,即是否绑定数据,以及是否有errors
    2. .errors方法
      is_valid调用的是errors方法,源码:
      def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        if self._errors is None:
          self.full_clean()
      return self._errors
      
      可以看到,其实form就验证一次,没有error的话调用full_clean方法
    3. .full_clean方法
      该方法主要是调用这三个方法验证:
      self._clean_fields()
      self._clean_form()
      self._post_clean()
      
      • self._clean_fields()
        主要工作:
        a. 获得当前的值(空的时候为初始化的值)
        b. 将值转换为python格式
        c. 检验是否符合required参数
        d. 找到对应的验证器进行验证,通过返回对应值, 保存到cleaned_data,否则抛出ValidationError
        f. 执行clean_xx构子函数(我们定义的),通过返回对应值保存到cleaned_data(自动),否则抛出ValidationError
      • self._clean_form()
        调用我们自定义的clean钩子函数
      • self._post_clean()
        调用_post_clean钩子函数
            def _post_clean(self):
           """
           An internal hook for performing additional cleaning after form cleaning
           is complete. Used for model validation in model forms.
           """
           pass
        
        暂时不知道怎么用。

    自定义验证器

    该部分内容见使用验证器

    验证某一字段

    1. 定义clean_xxx方法
    2. self.cleaned_data["xxx"]中取值
    3. 检验
      • 通过,返回值
      • 不通过,抛出django.core.exceptions.ValidationError,code参数默认invalid
    from django import forms
    from django.core.exceptions import ValidationError
    
    
    class USER(forms.Form):
        name = forms.CharField(max_length=32, label="用户名")
    
        def clean_name(self):
            val = self.cleaned_data["name"]
            if not val.startswith("lcz"):
                raise ValidationError("必须以lcz开头", code="invalid")
            # 通过
            return val
    

    验证全部字段

    1. 定义clean方法
    2. 执行父类的clean方法得到cleaned_data
    3. 验证cleaned_data中自己想要验证的字段
    4. 不通过就抛出异常,否则不用做

    抛出的异常在: .errors["__all__"],可通过.non_field_errors()获取,因为:源码:return self.errors.get("__all__", self.error_class(error_class='nonfield'))

    from django import forms
    from django.core.exceptions import ValidationError
    
    
    class USER(forms.Form):
        name = forms.CharField(max_length=32, label="用户名")
    
        def clean(self):
            cleaned_data = super().clean()
            name = cleaned_data.get("name")
    
            if name:
                # Only do something if both fields are valid so far.
                if "mx" not in name:
                    raise ValidationError("没有mx")
    

    ModelForm

    重点,由于前后端分离,直接生成HTML的用途并不是很大,但是通过model验证POST的数据还是很有用途的。

    流程:

    1. 定义model
    2. 定义ModelForm类(继承django.forms.ModelForm
    3. class Meta指定哪个类,已经哪些字段
    4. 在views中使用ModelForm,然后调用save方法(save方法隐式验证)

    Form字段和Model字段

    常用:

    模型字段 表单字段
    AutoField 不呈现在表单中
    BigAutoField 不呈现在表单中
    IntegerField IntegerField
    BigIntegerField IntegerField 将 min_value 设置为-9223372036854775808,将 max_value 设置为9223372036854775807。
    FloatField FloatField
    CharField CharField 将 max_length 设置为模型字段的 max_length ,如果模型中设置了 null=True ,会将 empty_value 设置为 None 。
    TextField CharField 设置中 widget=forms.Textarea
    BinaryField CharField ,如果在模型字段上的 editable 被设置为 True ,则不在表单中显示。
    BooleanField BooleanField, 或 NullBooleanField (如果 null=True )。
    DateTimeField DateTimeField
    EmailField EmailField
    DateField DateField
    TimeField TimeField
    FileField FileField
    FilePathField FilePathField
    ImageField ImageField
    URLField URLField
    UUIDField UUIDField
    ForeignKey ModelChoiceField (见下文)
    ManyToManyField ModelMultipleChoiceField (见下文)

    全部对应关系见:字段类型

    • ForeignKey
      是一个ChoiceField,值是选项是一个模型的 QuerySet
    • ManyToManyField
      是一个MultipleChoiceField,其选项为一个模型 QuerySet
      如: authors = models.ManyToManyField(Author)在ModelForm中就相当于:authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())

    这两个如何使用,见下文

    简单使用

    定义model和modelform:

    from django.db import models
    from django.forms import ModelForm, ChoiceField
    
    TITLE_CHOICES = (
        ('v1', 'Mr.'),
        ('v2', 'Mrs.'),
        ('v3', 'Ms.'),
    )
    
    
    class Author(models.Model):
        name = models.CharField(max_length=100)
        title = models.CharField(max_length=3, choices=TITLE_CHOICES)
        birth_date = models.DateField(blank=True, null=True)
    
        def __str__(self):
            return self.name
    
    
    class Book(models.Model):
        name = models.CharField(max_length=100)
        authors = models.ManyToManyField(Author)
    
    
    class AuthorForm(ModelForm):
        class Meta:
            model = Author
            fields = ['name', 'title', 'birth_date']
    
    
    class BookForm(ModelForm):
        class Meta:
            model = Book
            fields = ['name', 'authors']
    

    在view 中使用:

    # web/views.py
    from django.shortcuts import render
    from django.http import HttpResponse, JsonResponse
    from django.core.serializers.json import DjangoJSONEncoder
    from django.core.exceptions import ValidationError
    from web.models import BookForm
    
    
    # Create your views here.
    
    class ValidationEncoder(DjangoJSONEncoder):
        def default(self, val):
            if isinstance(val, ValidationError):
                return str(val)
            super().default(val)
    
    
    def register(request):
        if request.method == "GET":
            form = BookForm()
            return render(request, "web/register.html", {"form": form})
    
        if request.method == "POST":
            form = BookForm(request.POST)
            try:
                form.save()
            except ValueError:
                return JsonResponse(form.errors.as_data(), encoder=ValidationEncoder)
        return HttpResponse("ok!")
     
    

    meta

    meta类可以为ModelForm提供约束和修饰

    1. fields:要用ModelForm验证的字段,当值为"__all__"时为全部字段
    2. exclude:除了哪些字段不验证
    3. model:指定是哪个Model 的ModelForm
    4. widgets:字典,{'name': Textarea(attrs={'cols': 80, 'rows': 20}), },自定义widgets。
    5. error_messages:错误信息提示
      error_messages = {
      		'name': {
      			'max_length': "This writer's name is too long.",
      		},
      	}
      
    6. help_texts:帮助信息
       help_texts = {
      			'name': 'Some useful help text.',
      		}
      
    7. labels:input标签的label内容
       labels = {
      			'name': 'Writer',
      		}
      
    8. field_classes设置表单字段类型:field_classes = {'slug': MySlugFormField,}

    验证

    验证ModelForm主要分两步:

    1. 表单本身的验证:见这里或上文
    2. 验证模型实例:见这里

    模型的验证(Model.full_clean())紧跟在表单的clean()方法调用之后。(表单的验证流程在上文)
    我们可以和form一样自定义clean。
    关于错误信息:

    1. 在 表单字段 级别或者 表单 Meta 级别定义的错误信息优先级总是高于在 模型字段 级别定义的。
    2. 在 模型字段 上定义的错误信息只有在 模型验证步骤引发ValidationError时才会使用,并且没有在表单级定义相应的错误信息
      模型字段有内置的default_error_messages类属性
    3. ModelForm自定义错误信息
      from django.core.exceptions import NON_FIELD_ERRORS
      from django.forms import ModelForm
      
      class ArticleForm(ModelForm):
      	class Meta:
      		error_messages = {
      			NON_FIELD_ERRORS: {
      				'unique_together': "%(model_name)s's %(field_labels)s are not unique.",
      			}
      		}
      

    操作数据

    增加和修改数据都需要调用.save()方法,调用 save() 将通过检查form.errors来实现验证。如果表单验证不过,则会抛出ValueError

    1. 增加

      • 普通字段
        form = BookForm(request.POST)
        try:
        	form.save()
         except ValueError:
        	 return HttpResponse("error")
        
      • 多对多字段
        可以直接保存,但是也可以让其先验证,然后增加一些其它字段,再保存。
        调用 save() 的时候使用 commit=False ,那么它会返回一个尚未保存到数据库的对象
        f = AuthorForm(request.POST)
        # 调用 save() 的时候使用 commit=False ,那么它会返回一个尚未保存到数据库的对象
        new_author = f.save(commit=False)
        # 增加一些其它字段
        new_author.some_field = 'some_value'
        # 保存多对多的表单数据
        new_author.save()
        f.save_m2m()
        
    2. 修改
      通过指定instance参数实现

      a = Article.objects.get(pk=1)
      f = ArticleForm(request.POST, instance=a)
      f.save()
      

    ModelForm的内容很多,本文写了部分内容,更多可以查看官网:从模型创建表单

    本文来自博客园,作者:忞翛,转载请注明原文链接:https://www.cnblogs.com/lczmx/p/15204015.html

  • 相关阅读:
    Visual Studio使用阿里云Code Git服务器的常见问题
    使用Quartz.net来执行定时任务
    DirectorySearcher.Filter 属性(转)
    angular2的ElementRef在组件中获取不到
    angular2 ngfor循环
    angular2 日期格式化
    angular2在模板中使用属性引发Cannot read property 'xxx' of undefined
    Java ConcurrentHashMap存入引用对象时也是线程安全的
    FtpHelper实现ftp服务器文件读写操作(C#)
    Window服务项目脚手架
  • 原文地址:https://www.cnblogs.com/lczmx/p/15204015.html
Copyright © 2020-2023  润新知