一、form组件的作用
1、对用户请求的验证
2、生成HTML代码
3、初始化默认值
二、一个简单的form组件
需要先引入所需要的库:
from django import forms
from django.forms import fields
通过类的方式实现form组件:
class checkForm(forms.Form): # 需要继承这个类
# 这里的变量值是前端页面的name属性的值,为了方便使用,这里的名字也要和数据库的字段名一致。
name = field.CharField(max_length=8) # 定义了一个文本框,返回值类型是字符型,最大长度是8
age = field.IntegerField(min_length=2) # 定义了一个文本框,返回值类型是整型,最大长度是8
Email = field.EmailField(required=True) # 定义了一个文本框,要求是必填项
视图函数的调用:
def index(request):
f1 = checkForm(request.GET) # 初始化一个表单验证对象,验证get方式提交的数据
if f1.is_valid(): # 判断数据是否符合验证
print(f1.cleaned_data) # 打印合格的数据
else:
print(f1.errors) # # 打印错误信息
前端的页面:
def home(request):
obj = checkForm() # 通过checkForm生成一个表单
return render(request, "index.html", locals())
---------------------------------------------------
<body>
<form method="get" action="">
{{obj.name}}
{{obj.age}}
</form>
</body>
对于form组件也可传入初始化值
两种传值的方式:1、通过initial参数。2、通过data参数
form = CheckForm(data={"title": obj.title})
这种方式传递的参数,会先进行form验证然后显示在页面上,如果有错,在页面上一并显示错误信息
只要页面上要显示错误信息就会进行验证
form = CheckForm(initial={"title": obj.title})
这种方式不会进行form的验证,直接在页面上进行显示
三、各字段的详细含义
CharField() 字段下的属性。 返回的类型是字符串类型。
name = field.CharField(
max_length=8, # 定义最大长度为8
min_length=2, # 定义最小长度为2
requried=True, # 是否为必填项
disable=True, # 定义是否可以编辑
localize=True, # 定义是否支持本地化
error_message={"required":"请填写该选项"}, # 自定义错误信息,格式:{"属性名":"提示信息"}
initial="请填写", # 第一种设置默认值的方式
label="姓名", # 设置标签名,需要前端调用。
help_text="填写汉字", # 帮助信息,需要前端调用。
validators="", # 用来放正则表达式进行对字符串的验证。
show_hidden_initial=True, # 创建一个默认的隐藏框在当前框的旁边用来比较值。
label_suffix=":" # 显示label 内容后缀。
)
一些属性的前端调用:
label属性:
{{obj.name.label}} # 先调用字段再调用其属性
help_text属性:
{{obj.name.help_text}}
error_messages 属性:
{{obj.errors.name.0}} # 因为有可能有多个错误,所以就显示一个
前端一句话显示所有内容:
{{obj.as_p}} 所有内容展示在P标签内
还有其他的方式 {{obj.as_ul}} 、{{obj.as_table} 但是不推荐这种方式
IntegerForm() 字段的属性。 返回的是整型。
因为IntegerForm 和 CharField 都是继承同一个父类form类,所以有一些相同的参数。下面介绍一些不同的参数:
age = field.IntegerField(
max_value=99, # 定义最大值
min_value=10 # 定义最小值
)
DecimalField()字段的属性。
salary = field.decimalField(
max_digitals=5, # 定义最大的位数。
decimal_place=2 # 定义小数点的位数。
}
FileField() 字段的属性。
text = filed.FileField(allow_empty_file="True" # 是否允许上传空文件)
ChoiceField()字段。 下拉框
classes = filed.ChoiceField(
choices = [(1,"一班"),(2,"二班"),(3,"三班")], # choices是数据源,后面跟一个列表,1代表value的值, "一班"代表文本内容。
initial = 1 # 设置初始值
)
MultipleChoiceField()。 多选框。
city = field.MultipleChoiceField(
choices=[(1,"xxx"),(2,"ooo")],
initial = [1,2] # 默认参数
)
TypedChoiceField()。 和ChoiceField() 一样,多了一个可以处理数据的参数。
city = fields.TypedChoiceField(
coerce=lambda x:int(x), # coerce 对类型的处理,这里可以自定义,不进行处理拿到的是字符
choices=[(1,"村长"),(2,"Alex"),(3,"元昊")],
initial=2 # 默认选中value为2的
)
四、form的内置插件
需要设置widgets的参数,每个字段都有widget这个参数。我们先导入
from django.forms import widgets
widgets的使用:
# 创建一个下拉菜单
name = field.CharField( widget=widgets.Select(choices=[(1,"A"),(2,"B"),(3,"C")])) # choices 是数据源
# 创建多选菜单
name = MultipleChiceField( widget=widgets.SelectMultiple(choices=[(1,"A"),(2,"B"),(3,"C")]) )
# 单选框
name = field.CharField(widget=widgets.RadioSelect(choices=[(1,"A"),(2,"B"),(3,"C")]))
# 复选框
name=field.CharField(widget=widgets.CheckboxSelectMultiple(choices=[(1,"A"),(2,"B"),(3,"C")]))
对于django帮我们自动创建好的HTML如何添加class或者其他属性呢:
name = field.CharField(widget=widgets.TextInput(attrs={"class":"active"})) # 通过attr来添加class
特殊字段数据实时更新的问题:
由于类的属性在一开始加载时就分配空间, 当数据库有数据更新时,就无法获取到
class Love(forms.Form):
price = fields.IntegerField()
user_id = fields.IntegerField(widget=widgets.Select(Teacher.objects.all().values_List("id","name"))) # 这里仅在加载时执行查询
第一种解决方案(推荐):由于每次创建表单时,都会调用此类,所以在__init__()方法内执行。
class Love(forms.Form):
price = fields.IntegerField()
user_id = fields.IntegerField(widget=widgets.Select())
def __init__(self):
super().__init__()
self.fields["user_id"].widget=widgets.Select(choices=Author.objects.values_list("id", "name")) # 这里要用values_list
super方法会把自己的类属性都复制到self.fields下,所以super().__init__()必须方法到最前面,通过对self.fields进行值的修改。
第二种解决方案:
class Love(forms.Form):
price = fields.IntegerField()
user_id2 = ModelChoiceField(queryset=Author.objects.all()) # ModelChoiceField()字段进行更新,queryset为数据库查询语句
然而这个方法无法打印属性值,所以需要结合__str__()方法进行打印
def __str__(self):
return self.name
总结:在插件中,数据源(choices)都是写在插件里。
五、form组件的扩展(hook)
在源码中有三处可以进行扩展的钩子
def full_clean(self):
# 对单个字段进行验证
self._clean_fields()
# 对整体进行验证
self._clean_form()
# 一个备用的钩子,一般不用,此处也是看了扩展的地方
self._post_clean()
_clean_fields() :
关键代码和扩展的关键:
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): # 通过反射的方式进行查找以clean_开头的函数
value = getattr(self, 'clean_%s' % name)() # 执行该方法并获取返回值
self.cleaned_data[name] = value # 重新赋值
except ValidationError as e:
self.add_error(name, e)
所以可以在这里对各个字段单独进行处理。
from django.core.exceptions import NON_FIELD_ERRORS,ValidationError
class checkForm(forms.Form):
name=field.CharField(max_length=8,min_length=2,required=True)
age=field.IntegerField()
city=filed.ChoiceField(widget=widgets.Select(choices=[(1,"A"),(2,"B"),(3,"C")]))
def clean_name(self): # 对name字段进行验证
v = self.cleaned_data["name"]
if Author.objects.filter(name=v).count(): # 可以在这里抛出异常
raise ValidationError("用户已存在","valid")
return v
clean(): 在这里进行整体的验证
def _clean_form(self):
try:
cleaned_data = self.clean() # 扩展点,可以自定义clean函数
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
可以这样做:
def clean(self):
user = self.cleaned_data["name"]
if user == "asd":
raise ValidationError("error")
return self.cleaned_data
_post_clean():
这里可以自定义验证,由于没有可提供参考的异常,所以一般不在这里进行异常处理。
六、Django的序列化
对于Django的Queryset进行查询
from django.core import serializers # 适用queryset的类型数据【obj,obj,obj】
import json
def te(request):
response = {}
response["mes"] = serializers.serialize("json",Author.objects.all()) # 参数为:格式,数据
print(json.dumps(response)) # 然后在dumps,对数据序列化了两次,前端反序列化两次
return HttpResponse("ok")
对于values和values_list的查询结果:
import json
def te(request):
response = {}
# 只需要进行list()格式转换就好
response["mes"] = list(Author.objects.all().values("name", "id"))
# response["mes"] = list(Author.objects.all().values_list("name", "id"))
print(json.dumps(response))
return HttpResponse("ok")
对于平常的返回值,也可以直接返回一个json对象
from django.http import JsonResponse
def index(request):
# JsonResponse()等价于HttpResponse+json.dumps
return JsonResponse({"k1":"v1"}) 返回值必须是一个字典