在前面我们介绍了过django的forms组件,他可以很方便的实现我们前端的input框渲染和数据校验,但是我们使用过会发现,models组件在定义的时候我们需要定义相应的一些字段内容,但是有时候页面的表单form类与Model类是一一对应,因此分别定义Form类和Model类会比较麻烦,最简单的方式就是通过Model来生成一个Form类,因此,Django内置的ModelForm就是为此而生的.
一、ModelForm类的定义
如下我们定义了个modelform类:
from .models import * from django.forms import ModelForm from django.forms import widgets class BookForm(ModelForm): class Meta: model=Book fields=["title","publishDate","price","publish","authors"] widgets={ "title":widgets.TextInput(attrs={"class":"form-control"}), # "publishDate":widgets.DateInput(attrs={"class":"form-control"}), "publishDate":widgets.TextInput(attrs={"class":"form-control","type":"Date"}), "price":widgets.TextInput(attrs={"class":"form-control"}), "publish":widgets.Select(attrs={"class":"form-control"}), "authors":widgets.SelectMultiple(attrs={"class":"form-control"}) }
model 文件:
from django.db import models class Book(models.Model): nid=models.AutoField(primary_key=True) title=models.CharField(max_length=32,verbose_name="书名") publishDate=models.DateField(verbose_name="出版日期") price=models.DecimalField(max_digits=5,decimal_places=2,verbose_name="价格") publish=models.ForeignKey("Publish",verbose_name="出版社") #与Publish表建立多对一关系 authors=models.ManyToManyField("Author",verbose_name="作者") #与Author表建立多对多关系 def __str__(self): return self.title class Publish(models.Model): nid=models.AutoField(primary_key=True) name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): nid=models.AutoField(primary_key=True) name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
说明一:
(1)引入方式:from django.forms import ModelForm;
(2)model=Book:Book为model中Book类;
(3)fields=["title","publishDate","price","publish","authors"]:指定BookForm类中需要Book中字段,若fields="__all__"表示将取Book类中的所有字段;
(4)widgets:通过它补充定义相应字段在渲染出来的input标签的类型,并可以给他传一些属性。
说明二:
上述两个字段在我们Book类中是两个关联字段,分别是多对一和多对多关系,即一本书只能有一个出版社,但是有多个作者,所以对于我们将其标签类型设置为select和selectMultiple,modelform的牛逼之一就是:在页面渲染此两个标签时,不但会渲染出单选框和多选框,还能自动查询Publish和Author表,将相应的已有的对象数据渲染,供用户选择。
说明三:
另外一点,对于日期字段,无论我们将标签设置成何种类型(DateInput还是TextInput),我们看到的还是一个需要我们自己输入内容的输入框,但是如何渲染出我们可以选年月日的哪一种呢?如上,需要我们设置type:Date属性。
说明四:
如上我们定义的modelform类,我们也可以像form组件一样,继续在modelform类下定义局部钩子和全局钩子,进一步对字段做约束,方便校验。
二、ModelForm类的使用
1、添加书籍实例
视图函数:
def addbook(request): if request.method=="POST": bookForm = BookForm(data=request.POST) if bookForm.is_valid(): bookForm.save() #save方法直接将接收的数据作为一条新数据添加 return redirect("/index/") else: pass else: bookForm=BookForm() return render(request,"add_book.html",locals())
add_book.html:
方式一:
<div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form action="/add_book/" method="post"> {% csrf_token %} {% for foo in book_form %} <p> <label for="">{{ foo.label }}</label> {{ foo }} </p> {% endfor %} <input type="submit" value="submit"> </form> </div> </div> </div>
如上实例,我们在前端页面直接可以对服务器传来的modelform类实例化的对象进行循环,循环的每一个foo对象就会按照按照顺序渲染出相应字段的输入标签,但是和form组件一样,form标签和submit标签需要我们事先定义。注意一点:通过如上<label for="">{{ foo.label }}</label>可以渲染出foo对应字段中定义的verbose_name值,作为label标签的名称。如若foo对应的是title=models.CharField(max_length=32,verbose_name="书名")字段,则会渲染出相应的label标签<label for="">书名</label>。
显然一点,上述通过循环的方式,按照顺序的方式渲染对应的字段输入标签显然不是很灵活,我们是否有其他的办法来解决这个问题呢?必须的有啊。通过实例化的modelform对象直接取自己包含的字段名,就可以渲染出相应的输入标签。具体实例如下:
方式二:
<form class="form-horizontal" action="/addbook/" method="post"> {% csrf_token %} <div class="form-group"> <label for="" class="col-sm-2 control-label">{{ bookForm.title.label }}</label> <div class="col-sm-6"> {{ bookForm.title }} </div> </div> <div class="form-group"> <label for="" class="col-sm-2 control-label">{{ bookForm.price.label }}</label> <div class="col-sm-6"> {{ bookForm.price }} </div> </div> <div class="form-group"> <label for="" class="col-sm-2 control-label">{{ bookForm.publishDate.label }}</label> <div class="col-sm-6"> {{ bookForm.publishDate }} </div> </div> <div class="form-group"> <label for="" class="col-sm-2 control-label">{{ bookForm.publish.label }}</label> <div class="col-sm-6"> {{ bookForm.publish }} </div> </div> <div class="form-group"> <label for="" class="col-sm-2 control-label">{{ bookForm.authors.label }}</label> <div class="col-sm-6"> {{ bookForm.authors }} </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-6"> <button type="submit" class="btn btn-success btn-lg center-block">提交</button> </div> </div> </form>
2、编辑书籍实例
视图函数:
def editbook(request,id):
book_obj=Book.objects.get(nid=id)
if request.method=="POST":
bookForm = BookForm(data=request.POST,instance=book_obj)
if bookForm.is_valid():
bookForm .save()
return redirect("/index/")
else:
bookForm = BookForm(instance=book_obj)
return render(request,"edit_book.html",locals())
我们知道,编辑一条信息和添加一条信息的不同在于,不论在渲染页面还是保存编辑后的数据,我们都需要知道是在对那一条数据进行编辑,因此在渲染编辑页面时,我们不但要渲染出相应字段的输入标签,还要渲染出各字段编辑前的数据,所以用于渲染页面的mdelform实例对象必须要有个instance参数,参数值为当前要编辑的数据对象。编辑完成后,服务器在进行数据保存时同样也要有这样的一个参数。具体见如上实例。