model form

ModelForm 能允许我们通过一个 Model 直接创建一个和该模型的字段一一对应的表单,大大方便了表单操作。

下面来看一个例子。

首先我们有这样的 model:

from django.db import models

TITLE_CHOICES = (
    ('MR', 'Mr.'), ('MRS', 'Mrs.'), ('MS', 'Ms.'), ) class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): return self.name 



一、ModelForm的用法

ModelForm对用户提交的数据有验证功能,但比Form要简单的多

from  django.forms  import  ModelForm  # 导入ModelFormclass
customerModelForm(ModelForm):
     class  Meta:
         model = models.UserInfo
         fields = "__all__"
         labels = {
             'name' : '用户名' ,
             'password' : '密码' ,
             'qq' : 'qq号' ,
             '微信' : '微信号' ,
             'roles' : '角色'
         }

说明:

  1.model:对应得表名

  2.fields:选择字段列表,'__all__'是选择所有字段

  3.exclude:排除字段列表

  4.widgets:插件列表

  5.labels:前端显示字段名

  6.error_messages:自定义错误提示

  7.localized_field:本地化,如:根据不同时区显示数据

除了这些参数,ModelForm和Form一样可以定义局部钩子和全局钩子

2.实例化表单对象,传入模板,同Form

model_form  = customerModelForm()
return  render(request, "index.html" ,{ "model_form" :model_form})

3、前端通过传递的model_form展示


{{ model_form.errors }}

{% csrf_token %}
{% for field in model_form %}



{{ field.errors }}
{{ field }}


{% endfor %}

4、form提交后,后端view通过以下方式,验证提交的表单

model_form  =  customerModelForm(request.POST)
if  model_form.is_valid():
     model_form.save()

5.如果是修改记录,则需要传入记录对象

obj = customerModel.objects.get(id=obj_id) #查到原来的值
model_form  =  customerModelForm(instance = user_obj) #传入值
model_form  = customer ModelForm(request.POST, instance = obj) #将原来的值生成新值
if  model_form.is_valid():
     model_form.save()
 
 

 

二、有多个Model,不想要每个model写一个ModelForm的,采用动态生成ModelForm类的方法

A、动态生成类的函数:type('classname',(object,),dict(funname=fun))

参数:

1、class的名称,字符串形式;

2、继承的父类集合,注意Python支持多重继承,如果只有一个父类,注意tuple的单元素写法;

3、class的方法名称与函数绑定,这里我们把函数fun绑定到方法名funname上,可以采用:

setattr(classname,"funname",fun)方式替代

例子:
def CreateModelForm(request,admin_obj):
class Meta:
model = admin_obj.model
fields = "__all__"

def __new__(cls, *args, **kwargs):
for field_name,field_obj in cls.base_fields.items():
field_obj.widget.attrs['class'] = 'form-control'
if field_name in admin_obj.readonly_fields:
field_obj.widget.attrs['disabled'] = True #在后台不能通过这种方式修改外键对应的字段,比如consultant
return forms.ModelForm.__new__(cls)

def default_clean(self):
print("cleaned data:",self.cleaned_data)

dynamic_model_form = type("DynamicModelForm",(forms.ModelForm,), {"Meta":Meta})
setattr(dynamic_model_form,"__new__",__new__)
setattr(dynamic_model_form,"clean",default_clean)
return dynamic_model_form

上述例子就可以根据需求动态生成dynamic_model_form了,需要使用的时候调用CreateModelForm函数即可
model_form = forms.CreateModelForm(request,admin_obj=admin_obj)









class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) def __str__(self): return self.name

我们根据上面的 Model 用 ModelForm 来直接创建表单,下面编写 forms.py:

from django.forms import ModelForm
from myApp.models import Author, Book class AuthorForm(ModelForm): class Meta: model = Author # 根据 Author 模型创建表单 fields = ['name', 'title', 'birth_date'] # 该表单包含的字段 class BookForm(ModelForm): class Meta: model = Book fields = ['name', 'authors'] 

现在看看表单在前端的效果。

编写 views.py:

from django.shortcuts import render
from myApp.forms import AuthorForm, BookForm def test(request): context = {} author_form = AuthorForm() book_form = BookForm() context['author_form'] = author_form context['book_form'] = book_form return render(request, 'test.html', context) 

编写 test.html:


    <h2>Author Form:h2> {{ author_form }} 
<br> <h2>Book Form:h2> {{ book_form }} body>

页面如下:

 
model form_第1张图片
 

 


 

ModelForm 的 save() 方法

ModelForm 的 save() 方法大体上和普通 Form 的 save() 没有区别,但 ModelForm 对象可以接受 commit 和 instance 参数来处理一些特殊的情况。

  • **instance **

ModelForm 的子类可以用关键字参数 instance 接收一个已经存在的 Model 实例;如果使用 instance 参数,save() 将更新这个实例;如果不使用,save() 将创建一个新的 Model 实例

下面我们在 shell 中演示一下 instance 的用法:

from myApp.models import Author, Book
from myApp.forms import AuthorForm, BookForm # 获取主键=1的 Model 对象 a = Author.objects.get(pk=1) # 使用 instance 参数,调用 save() 方法将更新 a 对象 af = AuthorForm({'name':'鲁迅', 'title':'MR', }, instance=a) af.save() # 不使用 instance 参数,调用 save() 方法将创建一个新的对象 af = AuthorForm({'name':'鲁迅', 'title':'MR', }) af.save() 

注意,如果表单没有验证,save() 调用将通过检查 form.errors 来进行验证。如果表单中的数据不合法,将引发 ValueError —— 例如,如果 form.errors 为True。

我们来试试构造一个错误的表单,看看 errors 信息:

af = AuthorForm({'name':'鲁迅', 'title':'12345', }) af.errors >>> {'title': ['Select a valid choice. 12345 is not one of the available choices.']} 
  • **commit **

save() 有一个可选的 commit 关键字参数,其值为 True 或 False(默认为 True)。

如果调用 save() 时 commit=False,那么它将返回一个还没有保存到数据库的 Model 对象。这种情况下,你需要调用 Model 实例本身的 save() 方法。 如果你想在保存之前自定义一些处理,或者你想使用特定的模型保存选项,就可以使用 commit=False

下面在 shell 中演示:

af = AuthorForm({'name':'鲁迅', 'title':'MR', }) # 用 commit=False 调用 save() 方法,这时 af 并不会保存到数据库 af = af.save(commit=False) # 查看 af 的类型,可见 af 是一个 Model 的实例 af >>> <Author: 鲁迅> type(af) >>> <class 'myApp.models.Author'> # 查看该 Model 实例的属性 af.name >>> '鲁迅' # 手动修改 Model 实例的属性 af.name = '周树人' # 调用 Model 实例本身的 save() 方法,这时实例被保存到数据库了 af.save() 

 


 

选择用到的字段

使用 fields 属性表示需要使用 Model 的哪些字段:

class AuthorForm(ModelForm): class Meta: model = Author # 表示该模型的全部字段都被表单使用 fields = '__all__' 

如果只要使用某些字段,可以这样:

fields = ['name', 'title', ]

使用 exclude 属性可以排除不需要用的字段:

class AuthorForm(ModelForm): class Meta: model = Author # 排除掉 birth_date 字段 exclude = ['birth_date'] 

使用上面两种方法,表单中字段出现的顺序将和字段在模型中定义的顺序一致。

如果设置了 Model 字段的 editable=False,那么使用 ModelForm 从该模型创建的任何表单都不会包含该字段。

 


 

重写(覆盖)默认的字段

使用内部类 Meta 的 widgets 属性可以指定一个字段的自定义 Widget。

例如,Author 的 name 属性为 CharField,如果你希望它表示成一个