Django:ModelForm
利用form组件渲染标签(普通标签和下拉菜单)
之前学习过的 form 组件可以渲染普通的 input 表单标签,如果在模型类中含有一对多,多对多等关系,或者模型来中某字段只能为true或false,这时候需要通过下来菜单来显示,form组件如何渲染下拉菜单?
1、models模型类中某表含有choices参数时:
class UserInfo(AbstractUser): tel=models.CharField(max_length=32) gender=models.IntegerField(choices=((1,"男"),(2,"女")),default=1)
注意:在模型类中,IntergerField和CharField都有choices参数
要取到具体的值时,比如 user_obj 为具体的摸一个选中的对象,那么要取到user_obj的gender的具体值,要使用
yuan=UserInfo.objects.get(pk=1) #才能取到具体的男或女值
yuan.get_gender_display()
2、利用form组件渲染下拉菜单时:
form组件中也要添加choiceField() 但是里面的值是写死的。

将值传到前台效果如下:

3、创建 Form
以上ChoiceField渲染下拉菜单的方式需要写死,不够灵活,因此提供了ModelChoiceField、
ModelMultipleChoiceField,来渲染模型类(数据库)中的字段。
from django import forms from app01.models import Publish,Author from django.forms import widgets class BookForm(forms.Form): title=forms.CharField(max_length=32) price=forms.IntegerField() pub_date=forms.DateField(widget=widgets.TextInput(attrs={"type":"date"})) # wesgerts可以修改input的type类型 #publish=forms.ChoiceField(choices=[(1,"AAA"),(2,"BBB")]) #需要手动添加值 publish=forms.ModelChoiceField(queryset=Publish.objects.all()) #一对多,可以根据models模型中来渲染单选的下拉菜单 authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all()) #多对多,可以根据models渲染多选的下拉菜单 # 为各字段统一添加类名,form-control def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) for filed in self.fields.values(): filed.widget.attrs.update({'class': 'form-control'})
注意事项:
1、Form组件渲染下拉菜单ChoiceField、ModelChoiceField、ModelMultipleChoiceField的区别?
ChoiceField: 单选,但是字段需要手动添加,(常用的)
ModelChoiceField: 单选,对应数据库中的一对多的关系,select
ModelMultipleChoiceField: 多选,对应数据库中多对多的关系,select
2、传递的queryset对象,在渲染标签的时候是如何传值的?
ModelChoiceField、ModelMultipleChoiceField渲染的下拉菜单中:
以对象为文本值,如果有__str__,那么渲染的标签中文本值显示的就是__str__中返回的内容,以选中的对象的id为value值,进行发送。
3、模型类Book和BookForm的区别?
模型类Book:是与数据库对应进行插入值,对数据库进行操作。
BookForm:是用于数据校验,并可以页面渲染,利用三种ChoicesField可渲染下拉菜单,校验数据是否符合所设规则。
两者之间没有直接的关系。
另外:BOOKForm是在django开始启动的时候加载的,因此在修改了Form中的内容后,要重启才能使用。
4、向数据库中插入值(字段中含有列表值,如authors 一对多)
request.POST #是提交的数据
form = BookForm(request.POST)
form.clean_data #是校验后干净的数据
向数据库中插值,先将 authors 从字典中取出,然后单独插入该值

ModelForm
该知识点结合图书管理系统来介绍:
表结构如下:
from django.db import models # Create your models here. class Book(models.Model): nid=models.AutoField(primary_key=True) title=models.CharField(max_length=32) price=models.DecimalField(max_digits=8,decimal_places=2) # 999999.99 pub_date=models.DateTimeField() # "2012-12-12" publish=models.ForeignKey(to="Publish",on_delete=models.CASCADE) # 级联删除 authors=models.ManyToManyField(to="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.CharField(max_length=32) 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() email=models.CharField(max_length=32) ad=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) #books=models.ManyToManyField("Book") def __str__(self): return self.name class AuthorDetail(models.Model): addr=models.CharField(max_length=32) tel=models.IntegerField() #author=models.OneToOneField("Author",on_delete=models.CASCADE) def __str__(self): return self.addr
简单介绍
利用 ModelForm 可以根据models模型类中的字段来生成需要校验的字段,
__all__ 检验的是所有的字段,安装原生的来的,没有样式:

创建 ModelForm
# models.py
from django.db import models class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # 999999.99 pub_date = models.DateTimeField() # "2012-12-12" # comment_count=models.IntegerField(default=100) # poll_count=models.IntegerField(default=100) publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE) # 级联删除 authors = models.ManyToManyField(to="Author") def __str__(self): return self.title
# form.py 创建校验的类
from django import forms from app01.models import Book,Author,Publish from django.forms import widgets as wid #由于以下widgets与此同名,因此这里可以使用别名 class BookModelForm(forms.ModelForm): class Meta: model=Book # 模型类为 Book fields="__all__" # 校验所有的字段 # fields=["title","price","pub_date"] # 校验部分字段 # exclude=["title"] # 排除要校验的字段 labels={ "title":"书籍名称", "price":"价格" }, error_messages={ "title":{"required":"不能为空"} }, widgets={ "pub_date":wid.TextInput(attrs={"type":"date"}) # 为pub_date字段添加type类型,使用widgets } def clean_price(self): # 定义局部钩子 pass # 统一添加类名 form-control 修改样式 def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) for filed in self.fields.values(): filed.error_messages={"required":"不能为空"} # 其中 错误类型可以在这里进行全局添加,但是label不能因为字段中label的值各不相同 filed.widget.attrs.update({'class': 'form-control'})
注意事项:
1、添加一些字段的属性内容
错误可以通过构建widgets字典一个一个的写,也可以批量的在__init__中写,其中
如 labels 、 error_messages 、 widgets 都是固定的字段不可以更改。
2、ModelForm中的钩子
局部钩子和全局钩子的定义规则和 Form 中是一样的
3、扩展其他的字段
也可以在里面扩展其他模型类中不存在的字段,比如,确认密码等字段
4、渲染表单标签
利用 modelform 创建的校验类,在html中渲染可以根据模型类中的关系(一对多、多对多)渲染出单选或多选的下拉菜单。并且与模型类(数据库)中的字段可以对应起来。
form = BookModelForm( ) # 空的对象响应给前台,渲染的标签不带有默认值 edit_book = Book.objects.filter(pk=edit_book_id).first() form=BookModelForm(instance=edit_book) # 带有参数对象值的 form对象响应给前台,可以渲染该书对象的默认值,
5、form.save()
BookModelForm实例化对象的时候有 instance 参数,表示更新,没有参数的时候表示的插入
无参数:插入
有参数:更新
# 添加 form = BookModelForm(request.POST) if form.is_valid(): obj = form.save() # create return redirect("/books/") #编辑 form = BookModelForm(request.POST, instance=edit_book) if form.is_valid(): form.save() # update操作 ; edit_book.update(**cleandata) return redirect("/books/")
6、以后form 或 modelform 都放在form.py文件中,其他文件用到,,引用即可。
7、涉及邮箱
一般情况下,models中有邮箱直接使用CharField,不要使用 EmailField 字段,因为邮箱在数据库中的保存还是以字符串的形式。
但是如果用 ModelForm 来创建校验类需要用到数据库中的字段,此时 models中就保存成 EmailField,这样 UserModelForm 中就会自动生成对应的 forms.EmailField()。
ModelForm的应用
1、添加书籍
# views.py
from django.shortcuts import render, HttpResponse, redirect from app01.form import BookModelForm from app01.models import Book def book_add(request): if request.method == "GET": form = BookModelForm() return render(request, "book_add.html", {"form": form}) else: form = BookModelForm(request.POST) if form.is_valid(): obj = form.save() # create return redirect("/books/") else: return render(request, "book_add.html", {"form": form})
# book_add.html
<body> <h3>添加书籍</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> {% include 'form.html' %}
</div> </div> </div> </body>
# form.html
<form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="title">{{ field.label }}</label> {{ field }} <span>{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-default pull-right"> </form>
2、编辑书籍
# views.py
from django.shortcuts import render, HttpResponse, redirect from app01.form import BookModelForm from app01.models import Book def book_edit(request, edit_book_id): edit_book = Book.objects.filter(pk=edit_book_id).first() if request.method == "GET": form = BookModelForm(instance=edit_book) return render(request, "book_edit.html", {"form": form}) else: form = BookModelForm(request.POST, instance=edit_book) # 加参数,获取默认值,并且save表示更新 if form.is_valid(): form.save() # update操作 ; edit_book.update(**cleandata) return redirect("/books/") else: return render(request, "book_edit.html", {"form": form})
其中:book_edit.html 同 添加书籍一样
注意事项
1、novalidate 在form表单中表示,阻止浏览器对值进行判断(为空否)
2、form.save() 是create还是update 取决于 校验的时候有没有传参数 instance=edit_obj?
补充:继承 block 和 include?
大页面中,大块地方一样,只有一部分通过点击显示不同的内容,用继承 block。
多个页面中,会有一小部分内容相同,可以使用 {% include "form.html" %}
block:
//继承是在模板中将要替换的内容包在block中
{% block content %}
......要替换的内容
{% endblock %}
//在子页中将内容写在 block
{% extends "base.html" %}
{% block content %}
.....要写的内容
{% endblock %}
include:
// include,在要添加内容的页面中,用 include 来替换要添加的内容, <div class="col-md-6 col-md-offset-3"> {% include 'form.html' %} 要添加的内容 </div> form.html直接写要添加的内容即可不需要添加其他的内容。
modelform应用逻辑总结
添加书籍: def add(reqeust): if GET请求: form = BookModelForm() return render(reqeust, {"form": form}) ''' 渲染页面 <form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="title">{{ field.label }}</label> {{ field }} <span>{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-default pull-right"> </form> ''' else POST请求: form = BookModelForm(request.POST) if form.is_valid(): form.save() # Book.objects.create(clean_data) return redirect("/") else: return render(reqeust, {"form": form}) 编辑书籍: def edit(request, id): edit_obj = Book.objects.get(pk=id) if GET请求: form = BookModelForm(instance=edit_obj) return render(reqeust, {"form": form}) ''' 渲染页面同添加页面 ''' else POST请求: form = BookModelForm(request.POST, instance=edit_obj) if form.is_valid(): form.save() # edit_obj.update(clean_data) return redirect("/") else: return render(reqeust, {"form": form})

浙公网安备 33010602011771号