python之路_django ModelForm组件

  在前面我们介绍了过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参数,参数值为当前要编辑的数据对象。编辑完成后,服务器在进行数据保存时同样也要有这样的一个参数。具体见如上实例。

 

posted @ 2018-01-03 22:14  骑猪走秀  阅读(197)  评论(0编辑  收藏  举报