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标签
<form action="/" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ obj.user }} {{ obj.user.errors }}</p>
<p>{{ obj.user }} {{ obj.errors.user.0 }}</p> # 视频中的用法
{{ obj.as_p }} {{ obj.user.errors }} # 一次性以<p>生成所有标签
{{ obj.as_ul }} {{ obj.user.errors }} # 一次性以<ul>生成所有标签
<table>
{{ obj.as_table }} {{ obj.user.errors }} # 一次性以<table>生成所有标签
</table>
{{ 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 }} # 错误信息文本
<input type="submit"/>
</form>
字段
用于对用户请求数据的验证
详情见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'^[0-9]+$', '请输入数字'),
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'^[0-9]+$', '请输入数字')],
)
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字段默认生成的多的“---------”选项
-
在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: