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
models 表结构

简单介绍

利用 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})

 

posted @ 2018-11-06 10:43  葡萄想柠檬  Views(295)  Comments(0)    收藏  举报
目录代码