Django-组件拾遗 from组件 model form组件
modelobj 表单对象的 _meta 的方法>>点我
用model form 创建form表单的效果


详细情况
BoundField类中的源码

在实际中的应用


代码
Book 的模型类代码
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() #外键,多对多 authors=models.ManyToManyField('Author') #外键,一对多 publisher=models.ForeignKey('Publish') def __str__(self): return self.title class Author(models.Model): name=models.CharField(max_length=32) email=models.EmailField(max_length=32) def __str__(self): return self.name class Publish(models.Model): address=models.CharField(max_length=32) name=models.CharField(max_length=32) def __str__(self): return self.name
model form代码
from django.shortcuts import render # Create your views here. from django.forms import ModelForm from .models import * #通过用modelForm组件将内容翻译成from组件 class Bookform(ModelForm): class Meta: #选定要组成组件的model类 model=Book #显示所有的内容 fields='__all__' #设置要显示的字段对应的名称 一一对应关系 labels={'title':'名称',"price":'价格'}
#增加book def book(request): if request.method=='POST': form_obj=Bookform(request.POST) if form_obj.is_valid():
#简单的一句话就可以保存写入到表中用户输入的信息 form_obj.save() # Book.objects.create(title=request.POST.get('title'),title=request.POST.get('title'),title=request.POST.get('title')) else: return render(request,'book.html',{'book_form':form_obj}) #这里使用model form 类对象的方法与使用form 类对象的方法一致 form_obj=Bookform() return render(request,'book.html',{'book_form':form_obj})
通过上边的图可以知道 modelform 方法可以将代码翻译成form类的一个中间的方法 方便构成form表单类
编辑book对象
#编辑book def edit(request,id): #将用于编辑的book对象方在这外边,以便于get和post方法调用 book_obj = Book.objects.filter(pk=id).first() if request.method=='GET': #只需要在Bookform()中写入字段 instance(实例对象)=book_obj(需要更改的book对象) book_form=Bookform(instance=book_obj) return render(request,'edit.html',{'book_form':book_form}) else: #保存修改的这一步骤也需要写入 data=request.post instance=book_obj #与增加区分开了 这句话的意思是将instance中的内容更新为data中的内容 book_form=Bookform(data=request.POST,instance=book_obj) if book_form.is_valid(): #当用户写入的信息无误时, book_form.save() return redirect('/index/') else: #当编辑后的信息有问题的话,把错误信息返回去 return render(request,'edit.html',{'book_form':book_form})
编辑页面HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <form action="" method="post" novalidate> {% csrf_token %} {#{{ book_form.as_p }}#} #可以直接写一句这个就能打印出所有的表单属性信息 <hr>
#或者自定义打印表单属性信息的方法
{% for foo in book_form %} <label for="{{ foo.pk }}"></label> <div>{{ foo.label }}{{ foo }} <span>{{ foo.errors.0 }}</span></div> {% endfor %} <input type="submit" value="提交"> </form> </body> </html>
一 Django的form组件
二 Django的model form组件
这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来,先来一个简单的例子来看一下这个东西怎么用:比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存其实,重点不是这些,而是合法性验证,我们需要在前端判断用户输入是否合法,比如姓名必须在多少字符以内,电话号码必须是多少位的数字,邮箱必须是邮箱的格式这些当然可以一点一点手动写限制,各种判断,这毫无问题,除了麻烦我们现在有个更优雅(以后在Python相关的内容里,要多用“优雅”这个词,并且养成习惯)的方法:ModelForm先来简单的,生硬的把它用上,再来加验证条件。
创建modelform
首先导入ModelForm from django.forms import ModelForm 在视图函数中,定义一个类,比如就叫StudentList,这个类要继承ModelForm,在这个类中再写一个原类Meta(规定写法,并注意首字母是大写的) 在这个原类中,有以下属性(部分): class StudentList(ModelForm): class Meta: model = models.Student #对应的Model中的类 fields = "__all__" #字段,如果是__all__,就是表示列出所有的字段 如['name','price',]等,将信息放到列表中 exclude = None #排除的字段 labels = None #提示信息 {'title':"书名",'price':"价格"} 可以在前端页面用{{obj.label}} 的方法将信息取出 help_texts = None #帮助提示信息 widgets = None #自定义插件 error_messages = None #自定义错误信息 #error_messages用法: error_messages = { 'name':{'required':"用户名不能为空",}, 'age':{'required':"年龄不能为空",}, } #widgets用法,比如把输入用户名的input框给为Textarea #首先得导入模块 from django.forms import widgets as wid #因为重名,所以起个别名 widgets = { "name":wid.Textarea(attrs={"class":"c1"}) #还可以自定义属性 } #labels,自定义在前端显示的名字 labels= { "name":"用户名" }
然后在url对应的视图函数中实例化这个类,把这个对象传给前端
def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list})
然后前端只需要 {{ student_list.as_p }} 一下,所有的字段就都出来了,可以用as_p显示全部,也可以通过for循环这
student_list,拿到的是一个个input框,现在我们就不用as_p,手动把这些input框搞出来,as_p拿到的页面太丑。
首先 for循环这个student_list,拿到student对象,直接在前端打印这个student,是个input框student.label ,拿到数据库中每个字段的verbose_name ,如果没有设置这个属性,拿到的默认就是字段名,还可以通过student.errors.0 拿到错误信息有了这些,我们就可以通过bootstrap,自己拼出来想要的样式了,比如:
<body> <div class="container" > <h1>student</h1> <form method="POST" novalidate> {% csrf_token %} {# {{ student_list.as_p }}#} {% for student in student_list %} <div class="form-group col-md-6"> {# 拿到数据字段的verbose_name,没有就默认显示字段名 #} <label class="col-md-3 control-label">{{ student.label }}</label> <div class="col-md-9" style="position: relative;">{{ student }}</div> </div> {% endfor %} <div class="col-md-2 col-md-offset-10"> <input type="submit" value="提交" class="btn-primary"> </div> </form> </div> </body>
现在还缺一个input框的form-contral样式,可以考虑在后台的widget里面添加
比如这样:
from django.forms import widgets as wid #因为重名,所以起个别名
widgets = {
"name":wid.TextInput(attrs={'class':'form-control'}),
"age":wid.NumberInput(attrs={'class':'form-control'}),
"email":wid.EmailInput(attrs={'class':'form-control'})
}
当然也可以在js中,找到所有的input框,加上这个样式,也行。
添加纪录
保存数据的时候,不用挨个取数据了,只需要save一下
def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list}) else: student_list = StudentList(request.POST) if student_list.is_valid(): student_list.save() return redirect(request,'student_list.html',{'student_list':student_list})
编辑数据
如果不用ModelForm,编辑的时候得显示之前的数据吧,还得挨个取一遍值,如果ModelForm,只需要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就可以得到同样的效果
保存的时候要注意,一定要注意有这个对象(instance=obj),否则不知道更新哪一个数据
代码示例:
from django.shortcuts import render,HttpResponse,redirect from django.forms import ModelForm # Create your views here. from app01 import models def test(request): # model_form = models.Student model_form = models.Student.objects.all() return render(request,'test.html',{'model_form':model_form}) class StudentList(ModelForm): class Meta: model = models.Student #对应的Model中的类 fields = "__all__" #字段,如果是__all__,就是表示列出所有的字段 exclude = None #排除的字段 labels = None #提示信息 help_texts = None #帮助提示信息 widgets = None #自定义插件 error_messages = None #自定义错误信息 #error_messages用法: error_messages = { 'name':{'required':"用户名不能为空",}, 'age':{'required':"年龄不能为空",}, } #widgets用法,比如把输入用户名的input框给为Textarea #首先得导入模块 from django.forms import widgets as wid #因为重名,所以起个别名 widgets = { "name":wid.Textarea } #labels,自定义在前端显示的名字 labels= { "name":"用户名" } def student(request): if request.method == 'GET': student_list = StudentList() return render(request,'student.html',{'student_list':student_list}) else: student_list = StudentList(request.POST) if student_list.is_valid(): student_list.save() return render(request,'student.html',{'student_list':student_list}) def student_edit(request,pk): obj = models.Student.objects.filter(pk=pk).first() if not obj: return redirect('test') if request.method == "GET": student_list = StudentList(instance=obj) return render(request,'student_edit.html',{'student_list':student_list}) else: student_list = StudentList(request.POST,instance=obj) if student_list.is_valid(): student_list.save() return render(request,'student_edit.html',{'student_list':student_list}) 复制代码
总结: 从上边可以看到ModelForm用起来是非常方便的,比如增加修改之类的操作。但是也带来额外不好的地方,model和form之间耦合了。如果不耦合的话,mf.save()方法也无法直接提交保存。 但是耦合的话使用场景通常局限用于小程序,写大程序就最好不用了。
使用表单模板
你需要做的就是将表单实例放进模板的上下文。如果你的表单在Context 中叫做form,那么{{ form }}将正确地渲染它的<label> 和 <input>元素。
表单渲染的选项
对于<label>/<input> 对,还有几个输出选项:
{{ form.as_table }}以表格的形式将它们渲染在<tr>标签中{{ form.as_p }}将它们渲染在<p>标签中{{ form.as_ul }}将它们渲染在<li>标签中
注意,你必须自己提供<table> 或<ul> 元素。
{{ form.as_p }}会渲染如下:
手工渲染字段
我们没有必要非要让Django 来分拆表单的字段;如果我们喜欢,我们可以手工来做(例如,这样允许重新对字段排序)。每个字段都是表单的一个属性,可以使用{{ form.name_of_field }} 访问,并将在Django 模板中正确地渲染。例如:
|
1
2
3
4
5
|
<div class="fieldWrapper"> {{ form.Username.errors }} {{ form.Username.label_tag }} {{ form.Username }}</div> |
渲染表单的错误信息
1、
|
1
2
3
|
registerForm=RegisterForm(request.POST)print(type(registerForm.errors)) #<class 'django.forms.utils.ErrorDict'>print(type(registerForm.errors["username"])) #<class 'django.forms.utils.ErrorList'> |
2、
使用{{ form.name_of_field.errors }} 显示表单错误的一个清单,并渲染成一个ul。看上去可能像:
|
1
2
3
|
<ul class="errorlist"> <li>Sender is required.</li></ul> |
form组件的钩子
def foo(request):
if request.method=="POST":
regForm=RegForm(request.POST)
if regForm.is_valid():
pass
# 可用数据: regForm.cleaned_data,
# 将数据插入数据库表中
else:
pass
# 可用数据: regForm.errors
# 可以利用模板渲染讲errors嵌套到页面中返回
# 也可以打包到一个字典中,用于ajax返回
else:
regForm=RegForm()
return render(request,"register.html",{"regForm":regForm})
'''
实例化时:
self.fields={
"username":"字段规则对象",
"password":"字段规则对象",
}
is_valid时:
self._errors = {}
self.cleaned_data = {}
#局部钩子:
for name, field in self.fields.items():
try:
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)
# 全局钩子:
self.clean() # def self.clean():return self.cleaned_data
return not self.errors # True或者False
'''
form组件补充
1、Django内置字段如下:
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} 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, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:forms.ChoiceFied(choices = ((0,'上海'),(1,'北京'),))这种写法 而model表单中的写法是 required=True, 是否必填 models.IntegerField(choices=((0,"上海"),(1,"北京"))) widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型 ...
2、Django内置插件:
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


浙公网安备 33010602011771号