Django之ModelForm

  • 2020 年 1 月 23 日
  • 笔记

  在前面有篇博客,我写了一个叫forms组件的东西,可以帮助我们完成校验数据、渲染标签功能和在前端页面局部刷新功能,功能封装的已经很好了,当时已经很开心了。但万万没想到,还有比它功能更强大的东西。forms组件只能渲染出type=text类型的标签,而且还要我们写无数多个字段,然后跟上校验条件,用forms组件做编辑页面时,还要手动的把编辑的对象的每一个值写入标签的value,这些等等虽然相较没用forms组件之前更方便了,但跟ModelForm比起来,都显得苍白无力,把ModelForm吹了那么久,现在就让我们来见识见识ModelForm的强大。

  不管是用什么,首先都得有模型类吧,创建模型类是没有变化的,这是往库里创建表的必有步骤,肯定是变不的。

  一、创建模型类,完成数据库迁移

  models.py

from django.db import models    # Create your models here.  class Book(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      price=models.IntegerField(verbose_name='价格')      pub_date=models.DateTimeField(verbose_name='出版时间')      publish = models.ForeignKey('Publish', on_delete=models.CASCADE,verbose_name='出版社')      author=models.ManyToManyField('Author',db_table='book_author',verbose_name='作者')      class Meta:          db_table='Book'          verbose_name='书籍'      def __str__(self):          return self.name  class Publish(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      addr=models.CharField(max_length=15,verbose_name='地址')      phone=models.IntegerField(verbose_name='电话号码')      class Meta:          db_table='Publish'          verbose_name='出版社'      def __str__(self):          return self.name  class Author(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      age=models.IntegerField(verbose_name='年龄')      author_info=models.OneToOneField('Author_Info',on_delete=models.CASCADE,verbose_name='详情')      class Meta:          db_table='Author'          verbose_name='作者'      def __str__(self):          return self.name  class Author_Info(models.Model):      gf_name=models.CharField(max_length=10,verbose_name='女朋友名字')      telephone=models.IntegerField(verbose_name='电话号码')      ShenFenZheng=models.IntegerField(verbose_name='身份证号')      class Meta:          db_table='Author_Info'          verbose_name='作者详情'      def __str__(self):          return str(self.ShenFenZheng)

  二、创建一个ModelForm类

from django import forms  from  django.forms import widgets as wid  class BookModelForm(forms.ModelForm):      class Meta:          model=models.Book             #这相当于给Book模型类创建的          # fields=["title","price"]          # exclude = ["title"]          fields="__all__"             #这是要校验的字段,现在表示所有字段校验,上面两种写法也可以。            widgets={              "name":wid.TextInput(attrs={"class":"form-control"}),              "price":wid.TextInput(attrs={"class":"form-control"})          }                           #这是对用ModelForm生成标签的属性修改          error_messages={              "name":{"required":"该字段不能为空"}          }                          #这是修改错误信息的显示样式          labels={              "name":"书籍名称"          }        def  clean_title(self):       #也可以定义钩子          val=self.cleaned_data.get("title")          if val.startswith("xxx"):              return val          else:              raise  ValidationError("必须以xxx开头!")

  ModelForm校验数据和forms组件是一样,用is_valid()按照校验规则来校验,错误的信息会放在form.errors里面。

  三、渲染标签

  views.py

def addbook(request):      if request.method=="POST":          form=BookModelForm(request.POST)          if form.is_valid():              form.save()           #当数据校验通过后,我们不用写什么create,只需要写上这一句,就完成在表中创建一条记录。              return redirect("/books/")          else:              return render(request, 'addbook.html', locals())      else:            # form=BookForm()   # forms组件          form=BookModelForm()       #  modelforms组件          return render(request,'addbook.html',locals())

  add.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <link rel="stylesheet" href="/static/css/bootstrap.css">      <script src="/static/jquery-3.3.1.js"></script>      <script src="/static/js/bootstrap.js"></script>      <style>          .outer{              margin-top: 200px;          }          .title{              margin-bottom: 50px;          }          ul{              list-style: none;              margin: 0;              padding: 0;          }          .left{              position: fixed;              left: 0;              top: 20px;          }      </style>  </head>  <body>      <div class="left">          <div class="panel panel-primary">              <div class="panel-heading">                  <h3 class="panel-title">操作栏</h3>              </div>              <div class="panel-body">                  <div>                      <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>                  </div>                  <div>                      <a href="" class="">添加作者</a>                  </div>              </div>          </div>      </div>      <div class="container outer">          <div class="row">              <div class="col-md-6 col-md-offset-3">                  <div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">添加{{ zw_name }}</div>                  <form action="" method="post" novalidate>                      {% csrf_token %}                      {% for field in form %}                          <div class="form-group ">                              <label for="id_{{ field.name }}">{{ field.label }}</label>                              {{ field }}<span style="color: red">{{field.errors }}</span>                          </div>                      {% endfor %}                      <button class="submit btn btn-info pull-right">提交</button>                  </form>              </div>          </div>      </div>      <script>      $('input,select').addClass('form-control');      </script>  </body>  </html>

  其实从代码层面上讲,做add校验和标签渲染没多大区别,但在前端页面上看是有区别的,forms组件只能渲染成type=text的input标签,多于一对多和多对多的字段就无能为力了,还得自己去写。但ModelForm就不一样了,它会把你的一对多的字段渲染成单选的select标签,把多对多的字段渲染成多选的select标签,这样就相当方便了。如下图:

  ModelForm可以渲染select标签,它在编辑页面还有更强大的功能,你只需把一个模型类的对象传给他,他就可以帮你把对象每个值取出来,然后赋予标签的value,在更新数据时也不用写update了,用save()就搞定了。这就解决了我们要手动的去把要编辑的对象每个字段的值取出来放入标签中。

  views.py

def editbook(request,edit_book_id):      edit_book = models.Book.objects.filter(pk=edit_book_id).first()      if request.method=="POST":          form = BookModelForm(request.POST,instance=edit_book)          if form.is_valid():              form.save()       #在这里用save()的前提是校验的时候你把要编辑的对象传给ModelForm的instance参数了,不然就相当于重新创建一条表记录              return redirect("/books/")          else:              return render(request, 'editbook.html', locals())      else:          form=BookModelForm(instance=edit_book)     #要把编辑的对象传给ModelForm的instance参数          return render(request,'editbook.html',locals())

  edit.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <link rel="stylesheet" href="/static/css/bootstrap.css">      <script src="/static/jquery-3.3.1.js"></script>      <script src="/static/js/bootstrap.js"></script>      <style>          .outer{              margin-top: 200px;          }          .title{              margin-bottom: 50px;          }          ul{              list-style: none;              margin: 0;              padding: 0;          }          .left{              position: fixed;              left: 0;              top: 20px;          }      </style>  </head>  <body>      <div class="left">          <div class="panel panel-primary">              <div class="panel-heading">                  <h3 class="panel-title">操作栏</h3>              </div>              <div class="panel-body">                  <div>                      <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>                  </div>                  <div>                      <a href="" class="">添加作者</a>                  </div>              </div>          </div>      </div>      <div class="container outer">          <div class="row">              <div class="col-md-6 col-md-offset-3">                  <div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">编辑{{ zw_name }}</div>                  <form action="" method="post" novalidate>                      {% csrf_token %}                      {% for field in form %}                          <div class="form-group ">                              <label for="id_{{ field.name }}">{{ field.label }}</label>                              {{ field }}<span style="color: red">{{field.errors }}</span>                          </div>                      {% endfor %}                      <button class="submit btn btn-info pull-right">提交</button>                  </form>              </div>          </div>      </div>      <script>      $('input,select').addClass('form-control');      </script>  </body>  </html>

  结果:

   总之啊,ModelForm是相当的好用,一直以来,我的叫法应该都有问题,我一直把forms组件下的Form叫成forms组件,之前写的Form和今天写的ModelForm都属于forms组件,大家请原谅我。