Django中的ModelForm

一、ModelForm的基本用法示例:

from django import forms
from app01 import models


class BookModelForm(forms.ModelForm):
    #必须是这个类名
    class Meta:
        # 告诉Django这个form类和那个model类对应
        model = models.Book
        # 告诉Django这个form类里面有哪些字段
        fields = "__all__"
        widgets = {
            "publish_date": forms.widgets.DateInput(
                # 给日期字段添加日期类型
                attrs={"type": "date"}
            )
        }
        labels = {
            "title": "书名",
            "price": "价格",
            "publish_date": '出版日期',
            "publisher": "出版社名称",
            "authors": "作者"
        }
        error_messages = {
            "title": {
                "required": "书名不能为空"
            }
        }

    # 通过修改类的初始化方法达到批量添加共同属性的目的
    def __init__(self, *args, **kwargs):
        super(BookModelForm, self).__init__(*args, **kwargs)
        # for field_name in self.base_fields:
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })

ModelForm所有属性:

class Meta:
            model,                           # 对应Model的
            fields=None,                     # 字段
            exclude=None,                    # 排除字段
            labels=None,                     # 提示信息
            help_texts=None,                 # 帮助提示信息
            widgets=None,                    # 自定义插件
            error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
            field_classes=None               # 自定义字段类 (也可以自定义字段)
            localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据

ModelForm用于验证用户数据:is_valid()

        model_form_obj = XXOOModelForm()   //实例化对象
        model_form_obj.is_valid()   // 校验数据
        model_form_obj.errors.as_json()  //错误
        model_form_obj.clean()   //同Form组件中的属性
        model_form_obj.cleaned_data  // 同Form组件中的属性

ModelForm用于创建数据:save()

def add_book(request):
    if request.method == "POST":
        #直接传request.POST,进行ModelForm的实例化传参
        form_obj = forms.BookModelForm(request.POST)
        if form_obj.is_valid(): # 校验数据
            form_obj.save()   #直接就可以保存数据到数据库,包括多对多,多对一,一对一的关系
            return redirect("/book_list/")
    #ModelForm实例化对象
    form_obj = forms.BookModelForm()
    return render(request, "v2/add_book.html", locals())

ModelForm用于初始化:ModelForm(instance=model_obj)

def edit_book(request, pk):
    book_obj = models.Book.objects.filter(id=pk).first()
    #form_obj通过initial设置初始化的值,例如,图书管理系统中的编辑书籍功能,
    #点击编辑后跳转到编辑书籍页面,跳转后需要用要编辑的书籍信息填充页面对应信息。
    #不同于Form组件的是,ModelForm直接就可以传实例化的对象,而不需要将对象转化成字典的形式传。
    form_obj = forms.BookModelForm(instance=book_obj)  
    return render(request, "v2/edit_book.html", locals())

ModelForm用于更新 :ModelForm(request.POST, instance=book_obj)

def edit_book(request, pk):
    book_obj = models.Book.objects.filter(id=pk).first()
    if request.method == "POST":
        #修改数据时,直接可以将用户数据包request.POST传进去,
        #再传一个要修改的对象的示例,ModelForm就可以自动完成修改数据了。
        form_obj = forms.BookModelForm(request.POST, instance=book_obj)
        if form_obj.is_valid():  // 数据校验
            form_obj.save()   // 直接保存
        return redirect("/book_list/")
    #form_obj通过initial设置初始化的值,例如,图书管理系统中的编辑书籍功能,
    #点击编辑后跳转到编辑书籍页面,跳转后需要用要编辑的书籍信息填充页面对应信息。
    #不同于Form组件的是,ModelForm直接就可以传实例化的对象,而不需要将对象转化成字典的形式传。
    form_obj = forms.BookModelForm(instance=book_obj)  
    return render(request, "v2/edit_book.html", locals())

二、ModelForm和Form组件的应用场景:

  • ModelForm ---- 中小型应用程序。因为ModelForm是依赖于models的。
  • Form ------- 大型应用程序

三、通过ModelForm实现的书籍、作者、出版社的管理代码示例:

  1. 表结构:
from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=12)
    address = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=12)
    gender = models.SmallIntegerField(
        choices=((0, '女'), (1, '男'), (2, '保密'))
    )
    age = models.IntegerField()

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)  # 0~999.99
    publish_date = models.DateField()
    publisher = models.ForeignKey(to="Publisher", on_delete=models.CASCADE)
    authors = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title
  1. ModelForm类:
from django import forms
from app01 import models

class BookModelForm(forms.ModelForm):
    class Meta:
        # 告诉Django这个form类和那个model类对应
        model = models.Book
        # 告诉Django这个form类里面有哪些字段
        fields = "__all__"
        widgets = {
            "publish_date": forms.widgets.DateInput(
                # 给日期字段添加日期类型
                attrs={"type": "date"}
            )
        }
        labels = {
            "title": "书名",
            "price": "价格",
            "publish_date": '出版日期',
            "publisher": "出版社名称",
            "authors": "作者"
        }
        error_messages = {
            "title": {
                "required": "书名不能为空"
            }
        }

    # 通过修改类的初始化方法达到批量添加共同属性的目的
    def __init__(self, *args, **kwargs):
        super(BookModelForm, self).__init__(*args, **kwargs)
        # for field_name in self.base_fields:
        for field_name in iter(self.fields):
            field = self.base_fields[field_name]
            field.widget.attrs.update({
                "class": "form-control",
            })

  1. views 视图函数:
from django.shortcuts import render,redirect
from app01 import modelform
from app01 import models


def book_list(request):
    book_list = models.Book.objects.all()
    return render(request, 'book_list.html', {'book_list': book_list})


def add_book(request):
    if request.method == "POST":
        # modelform 直接可以将从前端拿到的数据组request.POST
        #当参数传给实例化的Modelform
        form_obj = modelform.BookModelForm(request.POST)
        # 调用is_valid()方法校验数据
        if form_obj.is_valid():
            # 直接保存到数据库中
            form_obj.save()
            return redirect("/book_list/")
    modelform_obj = modelform.BookModelForm()
    return render(request, 'add_book.html', {"modelform_obj": modelform_obj})


def edit_book(request,pk):
    book_obj = models.Book.objects.filter(id=pk).first()
    if request.method == "POST":
        #修改数据时,直接可以将用户数据包request.POST传进去,
        #再传一个要修改的对象的示例,ModelForm就可以自动完成修改数据了。
        form_obj = modelform.BookModelForm(request.POST, instance=book_obj)
        if form_obj.is_valid():
            form_obj.save()
        return redirect("/book_list/")
    # form_obj通过initial设置初始化的值,例如,图书管理系统中的编辑书籍功能,
    # 点击编辑后跳转到编辑书籍页面,跳转后需要用要编辑的书籍信息填充页面对应信息。
    # 不同于Form组件的是,ModelForm直接就可以传实例化的对象,而不需要将对象转化成字典的形式传。
    form_obj = modelform.BookModelForm(instance=book_obj)
                               #locals()是将本地数据键值对的简写形式
    return render(request, "edit_book.html", locals())
  1. book_list.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>书籍列表</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-2">
            <a href="/add_book/">添加书籍</a>
            <table border="1">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>id</th>
                    <th>书名</th>
                    <th>价格</th>
                    <th>出版日期</th>
                    <th>出版社</th>
                    <th>作者</th>
                    <th>操作</th>
                </tr>
                </thead>
                {% for book in book_list %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ book.id }}</td>
                        <td>{{ book.title }}</td>
                        <td>{{ book.price }}</td>
                        <td>{{ book.publish_date }}</td>
                        <td>{{ book.publisher }}</td>
                        <td>{{ book.authors.all }}</td>
                        <td>
                            <a href="/edit_book/{{ book.id }}/">编辑</a>
                        </td>
                    </tr>
                {% endfor %}
            </table>
        </div>
    </div>
</div>
</body>
</html>
  1. add_book.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加书籍</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-offset-3">
            <form role="form" action="" method="post">
                {% csrf_token %}
                {% for modelform in modelform_obj %}
                    <p>
                        {{ modelform.label }}
                        {{ modelform }}
                    </p>
                {% endfor %}
            <button type="submit" class="btn btn-success">提交</button>
            </form>
        </div>
    </div>
</div>
</body>
</html>
  1. edit_book.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加书籍</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-offset-3">
            <form role="form" action="" method="post">
                {% csrf_token %}
                {% for filed in form_obj %}
                    <p>
                        {{ filed.label }}
                        {{ filed }}
                    </p>
                {% endfor %}
                <button type="submit" class="btn btn-success">提交</button>
            </form>
        </div>
    </div>
</div>
</body>
</html>

四、通过对Form组件和ModelForm的对比

  • ModelForm增加了Form组件和对应数据库之间的联系,进而简化了Form类的代码
  • ModelForm继承了Form组件中的所有方法的同时,简化了前端获取数据和对应数据库之间的数据对接,不需要在对数据的内容做过多的操作。

五、ModelForm中的骚操作(同Form组件)

  • 批量添加样式:
    通过重写ModelForm类的init方法来实现。
    # 通过修改类的初始化方法达到批量添加共同属性的目的
    def __init__(self, *args, **kwargs):
        super(BookModelForm, self).__init__(*args, **kwargs)
        # for field_name in self.base_fields:
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })

更多Django的Modelform相关内容请参考django的官方文档:
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • 版权: https://github.com/haiiiiiyun/awesome-django-cn Aweso...
    若与阅读 23,037评论 3 241
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,972评论 3 119
  • 咖啡还是老样子。今天剩的虽然有点多,但应该也算不上浪费吧! 常规的练声今天是完成了。但是长尾阶段的练习今天没有做。...
    冯毓阅读 134评论 0 0