Python学习笔记Day23 - Django_Form

Form操作

orm(model)提供了数据库操作,但是验证的功能较弱
form专门用来做验证

参考文档

a.创建form类

from django.forms import Form
from django.forms import widgets
from django.forms import fields

class MyForm(Form):
user = fields.CharField(
widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'}),
error_messages={'required':'用户名不能为空'}, # 自定义错误信息
)
gender = fields.ChoiceField(
choices=((1, '男'), (2, '女'),),
widget=widgets.RadioSelect
)
pwd = fields.CharField(
widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
)

b.View函数处理

from ... import MyForm
def index(request):
if request.method == "GET":
obj = MyForm()
return render(request, 'index.html', {'form': obj})
elif request.method == "POST":
obj = MyForm(request.POST, request.FILES) # 创建form对象
if obj.is_valid(): # 判断form验证是否通过
values = obj.cleaned_data # 获取正确数据
models.UserInfo.objects.create(**values)
else:
errors = obj.errors # 获取错误信息,dict对象
print(obj.errors['user'][0]) # 获取指定错误信息
print(obj.errors.as_json()) # 打包为字典的错误信息
return render(request, 'index.html', {'form': obj})

c.生成HTML标签

{{ obj.user }} {{ obj.user.errors }}


{{ obj.user }} {{ obj.errors.user.0 }}

# 视频中的用法

{{ obj.as_p }} {{ obj.user.errors }} # 一次性以

生成所有标签
{{ obj.as_ul }} {{ obj.user.errors }} # 一次性以

    生成所有标签


    {{ obj.as_table }} {{ obj.user.errors }} # 一次性以
    生成所有标签

    {{ form.xxoo.label }} # label值
    {{ form.xxoo.id_for_label }} # label的for值,id_xxx
    {{ form.xxoo.label_tag }} # 自动生成label(包含上述两点)
    {{ form.xxoo.errors }} # 错误信息列表ul
    {{ form.xxoo.errors.0 }} # 错误信息文本

    字段

    用于对用户请求数据的验证

    详情见http://www.cnblogs.com/wupeiqi/articles/6144178.html

    Field
    required=True, 是否允许为空
    widget=None, HTML插件
    label=None, 用于生成Label标签或显示内容,相当于verbose_name
    initial=None, 初始值
    help_text='', 帮助信息(在标签旁边显示)
    error_messages=None, 自定义错误信息 {'required': '不能为空', 'invalid': '格式错误'} 用法类似ORM
    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, 最小值

    model中的foreignkey字段在Form中没有对应的字段,可用ChoiceField+choices从model里用valuelist(...)获取

    插件

    用于自动生成需要的HTML标签类型,可定制标签的属性

    widgets = {
    'name': forms.TextInput(attrs={'class': "form-control"}),
    'authors': forms.Select(attrs={'class': "form-control"}),
    'publish_date': forms.DateInput(attrs={'class': "form-control",'placeholder': "YYYY-MM-DD"})
    }

    字段:

    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

    常用选择插件

    单选radio,值为字符串

    user = fields.CharField(
    widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
    )
    user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    widget=widgets.RadioSelect
    )

    单选select,值为字符串

    user = fields.CharField(
    widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
    )
    user = fields.ChoiceField(
    choices=((1, '上海'), (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
    )

    choices的选项可以从数据库中获取

    由于是静态字段,获取的值无法实时更新,可自定义构造方法从而达到此目的。

    • 方法一:

      class MyForm(Form):
      user = fields.ChoiceField(widget=widgets.Select)
      def init(self, args, **kwargs):
      super(MyForm,self).init(
      args, **kwargs) # 继承父类
      self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

    • 方法二:

      使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

      authors = models.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
      # authors = models.ModelChoiceField(queryset=models.NNewType.objects.all())

    初始化数据

    values = {'user': 'root', 'city': 2}
    obj = MyForm(values) #在views创建obj时传入字典
    #obj = MyForm(initial=values) # 视频中的用法

    自定义验证规则

    方式一:使用RegexValidator模块

    from django.core.validators import RegexValidator
    class MyForm(Form):
    user = fields.CharField(
    validators=[
    RegexValidator(r'[1]+$', '请输入数字'),
    RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
    ],
    )

    方式二:

    from django.core.exceptions import ValidationError
    def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
    raise ValidationError('手机号码格式错误') # 符合则抛验证异常

    phone = fields.CharField(validators=[mobile_validate, ],)

    方式三:利用钩子自定义验证方法

    class MyForm(forms.Form):
    username = fields.CharField(
    validators=[RegexValidator(r'[2]+$', '请输入数字')],
    )
    def clean_username(self):
    # Form中字段定义的格式匹配完之后,执行此方法进行验证
    value = self.cleaned_data['username']
    if "666" in value: # 符合则抛验证异常
    raise ValidationError('666已经被玩烂了...', 'invalid')
    return value

    方式四:

    自定义字段和插件,详见地址
    http://www.cnblogs.com/wupeiqi/articles/6144178.html

    is_valid校验的过程--源码分析

    - 验证 is_valid -> 字段内置正则 + clean_字段 -> clean(all) -> _post_clean

    form_obj.is_valid()

    self._errors = ErrorDict() -> {}
    self.cleand_data = {} -> {}
    full_clean()
    # 在Form.full_clean()中初始化了Form._errors属性,
    # 并调用了_clean_fields(),_clean_form(),_post_clean()完成对表单的验证,
    # 这三个方法分别对应字段层次的验证,表单层次的验证,ModelForm层次的额外验证。

    两个钩子hook

    1. _clean_fields() 里面的 clean_字段() 局部钩子

    自定义字段验证规则

    if hasattr(self, 'clean_%s' % name): -> form_obj里是否自定义了clean_字段方法
    value = getattr(self, 'clean_%s' % name) -> 有则赋值,否则异常触发
    self.cleand_data[name] = value

    重写username字段的局部钩子

    def clean_username(self):
    username = self.cleaned_data.get("username")
    is_exist = models.Student.objects.filter(username=username)
    if is_exist:
    raise ValidationError("用户名已存在!") -> _clean_fields()内部会将异常捕捉并加入errors
    return username

    2. _clean_form() 里面的全局clean()钩子

    自定义表单层次验证,例如确认密码校验

    _clean_form() 会调用对象继承的clean()方法,但是默认什么也没干

    重写父类的clean()方法,全局钩子

    def clean(self):
    password = self.cleaned_data.get("password")
    re_password = self.cleaned_data.get("re_password")
    if re_password and re_password != password:
    self.add_error("re_password", ValidationError("两次密码不一致"))
    else:
    return self.cleaned_data

    在Form.errors中指定一个存放表单层次的ValidationError的ErrorList,而它的field name则为__all__

    错误信息写入none -> all -> {{ form.non_field_errors }}

    其他

    去除select字段默认生成的多的“---------”选项

    1. 在model中设置默认值:

      user = models.ForeignKey(....,default=1) # 可设置成默认0,则默认不选中,前端提交时仍为required

    model中null=True 和 blank=True的区别

    null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空,即在Null字段显示为YES。多对多字段没有该属性
    blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填,但是对数据库来说,没有任何影响

    修改样式属性

    • 第一种:直接修改

      class BookForm(forms.Form):
      name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
      url = forms.URLField()
      comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

    • 第二种:在表单定义中修改widget属性

      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')

    自定义datetime日期格式

    自定义显示格式的配置如下,更改Django的setting.py文件:

    USE_L10N = False
    DATE_FORMAT = 'Y-m-d'
    DATETIME_FORMAT = 'Y-m-d H:i:s'

    注意事项:如果USE_L10N设置为了True,那么语言环境规定的格式具有更高的优先级并将被应用,即DATE_FORMAT不生效。

    这里可用的格式化字符串的其他写法参见Django官方文档

    作业

    ============= 作业:xxxoo管理 =============
    用户验证:session
    新URL:Form验证
    中间件:IP过滤
    信号:记录操作
    CSRF:


    1. 0-9 ↩︎

    2. 0-9 ↩︎

posted @ 2020-07-05 23:27  Jerome12138  阅读(281)  评论(0)    收藏  举报