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


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:

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

页面如下:

  model form  

 


 

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,如果你希望它表示成一个 <textarea> 而不是默认的 <input type="text">,你可以覆盖字段默认的 Widget:

from django.forms import ModelForm, Textarea
from myApp.models import Author, Book

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ['name', 'title', 'birth_date']
        widgets = {
            'name': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

类似地,如果你希望进一步自定义字段,你可以指定内部类 Meta 的 labels、help_texts 和 error_messages:

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ['name', 'title', 'birth_date']

        labels = {
            'name': '作者',
            'title': '头衔',
            'birth_date': '出生日期',
        }

        error_messages = {
            'name': {
                'max_length': "名字长度应在15个字符以内",
            },
        }

最后,如果你希望完全控制字段 —— 包括它的类型、验证器等等,你可以像在普通的表单那样显式指定字段。

例如,如果你想为 slug 字段使用 SlugField,可以像下面这样:

class AuthorForm(ModelForm):
    slug = forms.SlugField()

    class Meta:
        model = Author
        fields = ['name', 'title', 'birth_date']

如果想要指定字段的验证器,可以显式定义字段并设置它的 validators 参数:

class AuthorForm(ModelForm):
    slug = forms.SlugField(validators=[validate_slug])

    class Meta:
        model = Author
        fields = ['name', 'title', 'birth_date']

 


 

modelform_factory 函数

可以单独使用 modelform_factory() 函数来直接从 Model 创建表单。在不需要很多自定义的情况下是更方便的:

from myApp.models import Author, Book
from django.forms.models import modelform_factory

BookForm = modelform_factory(Book, fields=("name", "authors"))

这样就根据 Book 的模型创建了表单 BookForm。

还可以对表单类做简单的修改,比如,对给出的字段指定 widgets :

BookForm = modelform_factory(
    Book, 
    fields=("name", "authors"),
    widgets={"name": Textarea()}
    )


作者:SingleDiego
链接:https://www.jianshu.com/p/3d04309445e4
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
上一篇:自定义注解,将业务模块中某个字段值记录到日志中


下一篇:python 操作Sqlite3