user_info_dict
字典
def home(request):
# 每次后刷新这个信息字典
user_info_dict = dict()
if request.method == "POST":
# 获取并保存信息
username = request.POST.get("username")
password = request.POST.get("password")
user_info_dict["username"] = username
user_info_dict["password"] = password
# 信息验证
if not username[0].isupper():
user_info_dict["name_message"] = "用户名必须以英文大写字母开头"
if len(password) <= 3:
user_info_dict["pwd_message"] = "密码必须大于三位数"
return render(request, 'home.html', locals())
user_info_dict
span
<form action="" method="post">
<p><lable>用户名:
<input type="text" name="username"
value="{{ user_info_dict.username }}">lable>
<span style="color: red">{{ user_info_dict.name_message }}span>
p>
<p><lable>密码:
<input type="password" name="password"
value="{{ user_info_dict.password }}">lable>
<span style="color: red">{{ user_info_dict.pwd_message }}span>
p>
<input type="submit" class="btn btn-success">
form>
user_dict_info
保存所有信息from django import forms
class MyForm(forms.Form):
# 字符串类型 最长10位,最短4位
username = forms.CharField(max_length=10, min_length=4)
# 必须满足邮箱格式 [email protected]
email = forms.EmailField()
在模型层的学习中
tests.py
文件进行测试实际上还可以在
pycharm
底部的Python Console
中进行测试导入视图层文件
from app01 import views
form_obj = views.MyForm({"username":"bruce", "email":"bruce"})
is_valid()
from_obj.is_valid()
# False
changed_data
form_obj.changed_data
# ['username', 'email']
errors
from_obj.errors
# {'email': ['Enter a valid email address.']}
form_data = views.MyForm({"username":"Bruce", "email":"[email protected]", "age":18})
form_data.is_valid()
# True
form_data.errors
# {}
form_data = views.MyForm({"username":"bruce"})
form_data.is_valid()
# False
form_data.errors
# {'email': ['This field is required.']}
class MyFrom(forms.Form):
username = forms.CharField(max_length=10, min_length=4)
class MyFrom(forms.Form):
# 初始值是Bruce
username = forms.CharField(initial="Bruce")
:冒号
class MyFrom(forms.Form):
# label标签内容
username = forms.CharField(initial="用户名:")
captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =')
class MyFrom(forms.Form):
# 不能为空
username = forms.CharField(required=True)
.help_text
才可以显示class MyForm(forms.Form):
username = forms.CharField(
help_text="请输入3-8个字符的用户名",
)
class MyFrom(forms.Form):
username = forms.CharField(
max_length=10,
min_length=4,
error_message ={
"required":"输入框不能为空",
"max_length":"输入内容不能超过10个字符",
"min_length":"输入内容不能少于4个字符"
}
)
validators
import re
from django import forms
from django.core.exceptions import ValidationError
def mobile_validate(value):
mobile_re = re.compile(r"^0?(13|14|15|17|18|19)[0-9]{9}$")
if not mobile_re.match(value):
raise ValidationError("手机号格式错误")
class MyForm(forms.Form):
phone = forms.CharField(
validators=[mobile_validate, ],
error_messages={"required": "手机号不能为空"}
)
# 另一个方式
from django import forms
from django.core.validators import RegexValidator
class MyForm(forms.Form):
phone = forms.CharField(
validators=[
RegexValidator(r"^[0-9]+$", "只能输入数字"),
RegexValidator(r"^123[0-9]+$", "只接受123开头的电话")
]
)
disbaled
:是否禁用readonly
:是否只读TextInput
PasswordInput
class MyForm(forms.Form):
username = forms.CharField(
widget=forms.TextInput()
)
password = forms.CharField(
widget=forms.PasswordInput()
)
attrs
attrs
**按照字典的格式传递class MyForm(forms.Form):
username = forms.CharField(
widget=forms.TextInput(attrs={
"class": "btn btn-success",
"style": "color: red; font-size: 10px"
})
)
RadioSelect
:按钮形式单选Select
:下拉菜单单选class MyForm(forms.Form):
gender_choices = ((1, "男"), (2, "女"), (3, "保密"))
radioselect = forms.ChoiceField(
choices=gender_choices,
widget=forms.RadioSelect
)
select = forms.ChoiceField(
choices=gender_choices,
widget=forms.Select
)
SelectMultiple
:按钮形式复选CheckboxSelectMultiple
:下拉菜单复选class MyForm(forms.Form):
hobby_choices = (("s", "swimming"), ("d", "dancing"), ("r","running"))
select_multiple = forms.MultipleChoiceField(
choices=hobby_choices,
widget=forms.SelectMultiple
)
checkbox_select_multiple = forms.MultipleChoiceField(
choices=hobby_choices,
widget=forms.CheckboxSelectMultiple
)
CheckboxInput
:
class LoginForm(forms.Form):
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
ModelChoiceField
单选ModelMultipleChoiceField
多选# 重写构造方法
from django import forms
from models import Gender
class MyForm(forms.Form):
gender = forms.ChoiceField(initial=3,
widget=forms.Select)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['gender'].choices = Gender.objects.all().values_list('id', 'value')
# 使用新的方法
from django import forms
from models import Gender
class MyForm(forms.Form):
gender = forms.ModelChoiceField(
initial=2,
widget=forms.Select,
queryset=models.Gender.objects.all()
)
# 模型层
from django.db import models
class Gender(models.Model):
value = models.CharField(max_length=10)
def __str__(self):
return self.value
class MyForm(forms.Form):
name = forms.CharField(
label="用户名:",
error_messages={"required": "用户名不能为空"}
)
password = forms.CharField(
max_length=10,
min_length=4,
label="密码:",
error_messages={"max_length": "密码最长10位", "min_length": "密码最短4位"},
widget=forms.PasswordInput(),
)
phone = forms.CharField(
label="手机号",
validators=[mobile_validate, ],
error_messages={"required": "手机号不能为空"}
)
def home(request):
# 每次访问都产生一个空对象
form_obj = MyForm()
# 对post请求进行特殊处理
if request.method == "POST":
# 可以将request.POST看成一个字典对象
# 再次创建有值的对象保存信息
form_obj = MyForm(request.POST)
if form_obj.is_valid():
# 数据合法操作
return HttpResponse("OK")
else:
# 数据不合法处理
pass
return render(request, 'home.html', locals())
{{ form_obj }}
<form action="" method="post">
{{ form_obj.as_p }}
<input type="submit" class="btn btn-success">
form>
<form action="" method="post">
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}p>
{% endfor %}
<input type="submit" class="btn btn-success">
form>
novalidate
<form action="" method="post" novalidate>
.0
出来<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>{{ form.label }}{{ form }} <span style="color: red">{{ form.errors.0 }}span>p>
{% endfor %}
<input type="submit" class="btn btn-success">
form>
clean
clean_字段值
clean_字段值
from django import forms
class MyForm(forms.Form):
email = forms.EmailField(
label="邮箱地址",
error_messages={
"invalid": "邮箱格式不正确",
"required": "邮箱地址不能为空",
}
)
def clean_email(self):
# 勾出数据
email = self.cleaned_data.get("email")
# 逻辑判断
if "qq.com" in email:
# 添加报错信息
self.add_error("email", "不可以使用QQ邮箱")
# 放回到原来的位置中
return email
# 自定义验证器版本
from django import forms
from django.core.exceptions import ValidationError
def validate_email(value):
if "qq.com" in value:
raise ValidationError("不可以使用QQ邮箱")
class MyForm(forms.Form):
email = forms.EmailField(
label="邮箱地址",
error_messages={
"invalid": "邮箱格式不正确",
"required": "邮箱地址不能为空",
},
validators=[validate_email]
)
clean
对密码进行二次检验
class MyForm(forms.Form):
password = forms.CharField(
label="密码",
min_length=6,
widget=forms.PasswordInput,
error_messages={
"required": "密码不能为空",
"min_length": "密码至少含有6位",
}
)
confirm_pwd = forms.CharField(
label="确认密码",
min_length=6,
widget=forms.PasswordInput,
error_messages={
"required": "密码不能为空",
"min_length": "密码至少含有6位",
}
)
def clean(self):
# 勾出想要的数据
password = self.cleaned_data.get("password")
confirm_pwd = self.cleaned_data.get("confirm_pwd")
# 逻辑判断
if password != confirm_pwd:
# 添加报错信息
self.add_error("confirm_pwd", "两次输入的密码不一致")
# 勾出的数据放回
return self.cleaned_data
as_view
.is_valid()
入手份分析def is_valid(self):
return self.is_bound and not self.errors
self.is_bound
是Truenot self.errors
是True
self.errors
是Falsedef __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
self.is_bound = data is not None or files is not None
is_bound
需要是True
__init__
参数中的data和file@property
def errors(self):
if self._errors is None:
self.full_clean()
return self._errors
.属性
访问_errors
self._errors = None
full_clean()
方法一定会执行 def full_clean(self):
self._errors = ErrorDict()
if not self.is_bound:
return
self.cleaned_data = {}
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()
self._errors = ErrorDict()
if not self.is_bound:
is_bound
就不是Falseself.cleaned_data = {}
if self.empty_permitted and not self.has_changed():
empty_permitted
默认是Falsereturn
self._clean_fields() self._clean_form()
self._post_clean()
for name, field in self.fields.items():
def _clean_fields(self):
for name, field in self.fields.items():
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
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)
value
get_initial_for_field
value_from_datadict
处理if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
pass
# 数据清洗
except ValidationError as e:
self.add_error(name, e)
get_initial_for_field
获取初始值clean
方法,清洗数据报包括初始值clean
方法,清洗数据cleaned_data
中if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value)
self.cleaned_data[name] = value
cleaned_data
中if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
def _clean_form(self):
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
cleaned_data
cleaned_data = self.clean()
clean()
函数内是什么都没有的只返回了cleaned_data
cleaned_data
cleaned_data
ModelForm
是一个非常强大的功能,它允许直接从模型定义中创建表单。ModelForm
会自动为你做这个工作。from django import forms
from .models import YourModel
# 创建表单类
class YourModelForm(forms.ModelForm):
class Meta:
model = YourModel
fields = '__all__' # Use all fields from the model
# 创建表单
form = YourModelForm()
# 根据已有模型创建表单
your_model = YourModel().object.get(k=1)
form = YourModelForm(instance=your_model)
模型字段 | 表单字段 |
---|---|
AutoField | 在Form类中无法使用 |
BigAutoField | 在Form类中无法使用 |
BigIntegerField | IntegerField,最小-9223372036854775808,最大9223372036854775807. |
BooleanField | BooleanField |
CharField | CharField,同样的最大长度限制。如果model设置了null=True,Form将使用empty_value |
CommaSeparatedIntegerField | CharField |
DateField | DateField |
DateTimeField | DateTimeField |
DecimalField | DecimalField |
EmailField | EmailField |
FileField | FileField |
FilePathField | FilePathField |
FloatField | FloatField |
ForeignKey | ModelChoiceField |
ImageField | ImageField |
IntegerField | IntegerField |
IPAddressField | IPAddressField |
GenericIPAddressField | GenericIPAddressField |
ManyToManyField | ModelMultipleChoiceField |
NullBooleanField | NullBooleanField |
PositiveIntegerField | IntegerField |
PositiveSmallIntegerField | IntegerField |
SlugField | SlugField |
SmallIntegerField | IntegerField |
TextField | CharField,并带有widget=forms.Textarea参数 |
TimeField | TimeField |
URLField | URLField |
model字段和表单字段存在大量的相似和重复之处
ManyToManyField和 ForeignKey字段类型属于特殊情况
ForeignKey
ForeignKey
在模型中表示一对多关系。ModelForm
中,ForeignKey
会被转化为 django.forms.ModelChoiceField
。ManyToManyField
ManyToManyField
在模型中表示多对多关系。ModelForm
中,ManyToManyField
会被转化为 django.forms.ModelMultipleChoiceField
。__str__
方法,要不然前端显示的是对象blank=True
null=True
default
choices
help_text:
verbose_name
ModelForm
的 Meta
类中使用 fields
属性来明确指出哪些字段应该被包含在表单中。__all__
'__all__'
来表示所有字段fields
。editable=False
, 则任何使用 ModelForm
给该模型创建的表单都不会包含这个字段。exclude
也需要谨慎,因为它可能会意外地包含一些不希望包含的字段。class YourModelForm(forms.ModelForm):
class Meta:
model = YourModel
# 添加全部
fields = '__all__'
# 指定添加
fields = ['field1', 'field3']
# fields和exclude不能混用
# 除了指定之外的全部添加
exclude = ['field2']
ModelForm
会自动为每个字段生成相应的验证。CharField
,那么生成的表单会包含一个必填的文本输入字段。ModelForm
还会验证模型实例。clean()
方法,以及所有的 unique
和 unique_together
约束。from django import forms
from django.core.exceptions import ValidationError
class YourModelForm(forms.ModelForm):
class Meta:
model = YourModel
def clean(self):
cleaned_data = super().clean()
if condition:
raise ValidationError({"field": "some message"})
return cleaned_data
Article
模型和对应的 ArticleForm
from django import forms
from myapp.models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content']
instance
参数ModelForm
的 save()
方法可以根据表单中的数据创建并保存一个数据库对象。
instance
参数
可以在调用 save()
方法时传入一个已经存在的模型实例作为 instance
参数,这样 save()
方法将会更新这个实例。
如果没有传入 instance
参数,save()
方法将会创建一个新的模型实例。
在调用 save()
方法时,如果表单还没有进行验证,save()
方法将会先进行验证。如果验证失败,将会抛出 ValueError
。
form = ArticleForm(request.POST)
if form.is_valid():
# 保存新的 Article 实例
new_article = form.save()
# 获取已存在的 Article 实例
article = Article.objects.get(pk=1)
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
# 更新已存在的 Article 实例
form.save()
commit
参数save()
方法接受一个可选的 commit
参数,默认为 True
。
如果传入 commit=False
,save()
方法将返回一个还没有保存到数据库的模型实例。
可以在这个实例上进行任何需要的修改,然后再次手动调用模型实例的 save()
方法将其保存到数据库。
如果模型有多对多关系,并且在调用 save()
方法时传入了 commit=False
,需要在手动保存模型实例后调用表单的 save_m2m()
方法来保存多对多数据。
除了 save()
和 save_m2m()
方法,ModelForm
的其他使用方式与普通的 Form
完全一样。
is_valid()
方法来验证表单is_multipart()
方法来检查表单是否需要多部分文件上传,等等。form = ArticleForm(request.POST)
if form.is_valid():
# 创建但不保存新的 Article 实例
new_article = form.save(commit=False)
# 修改实例的某个属性
new_article.title = 'New Title'
# 手动保存实例到数据库
new_article.save()
form = AuthorForm(request.POST)
if form.is_valid():
# 创建但不保存新的 Author 实例
new_author = form.save(commit=False)
# 修改实例的某个属性
new_author.name = 'New Name'
# 手动保存实例到数据库
new_author.save()
# 保存多对多数据
form.save_m2m()
modelform
的Meta
类中使用一些特殊的属性来实现widgets
属性来改变 name
字段的 widget
Textarea
widget,并添加了一个 CSS 类 “special”error_messages
属性来为 name
字段自定义错误信息
name
字段的长度超过最大长度时,会提示指定信息help_texts
属性为 name
字段添加了一个帮助文本
labels
属性来为 name
字段设置一个自定义标签 作者名称name
字段,并为它设置了一个最小长度验证器 MinLengthValidator(3)
Meta
类中的设置from django import forms
from django.core.validators import MinLengthValidator
from .models import Author
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ['name', 'title', 'birth_date']
widgets = {
'name': forms.TextInput(attrs={'class': "special"})
}
error_messages = {
'name': {
'max_length': "名字太长了"
},
}
help_texts = {
'name':"作者的名字字段"
}
labels = {
'name':"作者名称"
}
name = forms.CharField(validators=[MinLengthValidator(3),])
TIME_ZONE
设置为 'Asia/Shanghai'
USE_TZ
设置为 True
。from django.forms import ModelForm
from .models import Author
class AuthorForm(ModelForm):
class Meta:
model = Author
localized_fields = ('birth_date',)
# 所有字段本地化
localized_fields = '__all__'
class Person(models.Model):
name = models.CharField(max_length=100, default='张三')
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ['name']
# 创建实例
person = Person.objects.create()
# 创建表单实例
form = PersonForm(instance=person)
name
字段的值将会是 '张三'
form = PersonForm(initital={'name': '李四'}, instance=person)
结果
name
字段的值将会是 '李四'
initial
参数提供的初始值覆盖了模型实例的当前值。注意:
django
自带有模型user
和表单UserCreationForm
继承表单UserCreationForm使用模型user重写注册表单
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms
class CustomUserCreationForm(UserCreationForm):
# 重写 user模型有
email = forms.EmailField(required=True)
# 重写 UserCreationForm表单有
password1 = forms.CharField(label='密码', widget=forms.PasswordInput)
password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)
class Meta:
# 模型
model = User
# 字段
fields = ('username', 'email', 'password1', 'password2')
# 实际父表单有 重写
def clean(self):
password = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password2')
if not password or not password2 or password != password2:
self.add_error("password2", "两次密码不一致")
return self.cleaned_data
# 重写save方法 保存邮箱
def save(self, commit=True):
user = super().save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
UserCreationForm
中添加一个 terms_of_service
字段class CustomUserCreationForm(UserCreationForm):
# ...
terms_of_service = forms.BooleanField()
def clean_terms_of_service(self):
accepted = self.cleaned_data.get('terms_of_service')
if not accepted:
raise forms.ValidationError('你必须接受服务条款。')
return accepted
terms_of_service
字段并不对应到用户模型的任何一个字段,而是用来获取用户是否接受服务条款的信息。save
方法中进行处理。