ModelForm组件的所有参数

ModelForm
a.  class Meta:    # 生成html标签
        model,                           # 对应Model的
        fields=None,                     # 字段
        exclude=None,                    # 排除字段
        labels=None,                     # 提示信息
        help_texts=None,                 # 帮助提示信息
        widgets=None,                    # 自定义插件(modelform自己没有插件:from django.forms import widgets as Fwidgets 避免重名)
        error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
                                          字典形式{
                                            "__all__":{# 定制整体的错误信息}
                                            "email":{'required': '',定制单独字段的错误信息}
                                          }
        field_classes=None               # 自定义字段类(也可以自定义字段,modelform自己没有插件:from django.forms import fields as Ffields 避免与上面字段fields重名)
        localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
        如:
            数据库中
                2016-12-27 04:10:57
            setting中的配置
                TIME_ZONE = 'Asia/Shanghai'
                USE_TZ = True
            则显示:
                2016-12-27 12:10:57
b. 验证执行过程
    is_valid -> full_clean -> 钩子 -> 整体错误

c. 字典字段验证
    def clean_字段名(self):
        # 可以抛出异常
        # from django.core.exceptions import ValidationError
        return "新值"
d. 用于验证
    model_form_obj = XXOOModelForm()
    model_form_obj.is_valid()
    model_form_obj.errors.as_json()
    model_form_obj.clean()
    model_form_obj.cleaned_data
e. 用于创建
    model_form_obj = XXOOModelForm(request.POST)
    #### 页面显示,并提交 #####
    # 默认保存多对多
        obj = form.save(commit=True)
    # 不做任何操作,内部定义 save_m2m(用于保存多对多)
        obj = form.save(commit=False)
        obj.save()      # 保存单表信息
        obj.save_m2m()  # 保存关联多对多信息

f. 用于更新和初始化
    obj = model.tb.objects.get(id=1)
    model_form_obj = XXOOModelForm(request.POST,instance=obj)
    ...

    PS: 单纯初始化
        model_form_obj = XXOOModelForm(initial={...})

实例:

导入模块:

from app06 import models
from django.shortcuts import render,HttpResponse, redirect
from django import forms
from django.forms import fields as Ffields
from django.forms import widgets as Fwidgets

views(创建表单html和数据):

# 写之前,先创建数据库,否则无法创建数据库
# django.db.utils.OperationalError: no such table: app06_usertype
'''
class UserInfoForm(forms.Form):
    username = Ffields.CharField(max_length=32)
    email = Ffields.EmailField()
    user_type = Ffields.ChoiceField(
        choices=models.UserType.objects.values_list('id','caption')
        # user_type = models.ForeignKey(to='UserType', to_field='id', on_delete=models.CASCADE)
        # fields没有ForeignKey方法
    )

    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)
        self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')
def form(request):
    if request.method == "GET":
        obj = UserInfoForm()
        return render(request, 'form.html', {'obj': obj})
    elif request.method == "POST":
        obj = UserInfoForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)
            return redirect('/cohui/form')
        else:
            print(obj.errors)
            return render(request, 'form.html', {'obj': obj})
'''

class UserInfoModalForm(forms.ModelForm):

    # 自定义字段: 不需要保存到数据库的
    is_login = Ffields.CharField(
        widget=Ffields.CheckboxInput()
    )
    # is_login2 = Ffields.CheckboxInput()  # X

    class Meta:   # 生成html标签
        # model, fields 名字写死
        # modal = models.UserInfo   X
        # ValueError: ModelForm has no model class specified.

        model = models.UserInfo   # 绑定一个model类
        fields = '__all__'    # 所有字段
        # fields = ['username', 'email'] # 仅显示
        # exclude = ['username', 'email'] # 排除

        # 显示中文           verbose_name='用户名'或者:
        labels = {
            'username': '用户名',
            'email': '邮箱',
        }
        help_texts = {
            'username': '...'
        }
        widgets = {
            'username': Fwidgets.Textarea(attrs={'class': 'c1'})
        }
        error_messages = {
            '__all__': {    # 定制整体的错误信息

            },
            'email': {
                'required': '邮箱不能为空',  # 定制单独字段的错误信息
                'invalid': '邮箱格式错误..',
            }
        }
        field_classes = {
            # 'email': Ffields.URLField
        }

        # localized_fields=('ctime',)

    # 利用钩子验证判断,对username进行clean
    def clean_username(self):
        # 获取原来的值
        # 利用原来的值进行各种操作
        old_value = self.cleaned_data['username']
        # ....
        return old_value

def form(request):
    if request.method == "GET":
        obj = UserInfoModalForm()
        return render(request, 'day24-app06/form.html', {'obj': obj})
    elif request.method == "POST":
        obj = UserInfoModalForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)
            # obj.save()

            # 保存当前类
            instance = obj.save(False)
            instance.save()
            # 保存manytomany
            obj.save_m2m()

            return redirect('/cohui/forminfo')
        else:
            print(obj.errors)
            return render(request, 'day24-app06/form.html', {'obj': obj})

form.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/cohui/forminfo" method="post">
        {% csrf_token %}
        {{ obj.as_p }}
        <input type="submit" value="提交">
    </form>
</body>
</html>

views(ModelForm新url编辑):

##############model form 实现新url添加编辑#################
def user_list(request):
    # 多表关联查询 select_related不支持多对多查询
    # li = models.UserInfo.objects.all().select_related('user_type', 'u2g')
    # Invalid field name(s) given in select_related: 'u2g'. Choices are: user_type
    li = models.UserInfo.objects.all().select_related('user_type')
    return render(request, 'day24-app06/user_list.html', {'li': li})

def useredit(request, nid):
    # 获取用户id对象的所有信息
    # 显示用户已经存在的数据
    if request.method == "GET":
        user_obj = models.UserInfo.objects.filter(id=nid).first() # 一行数据

        # mf = UserInfoModalForm()   #编辑跳转是空数据
        # mf = UserInfoModalForm(user_obj) # 把指定行数据传过来
        # AttributeError: 'UserInfo' object has no attribute 'get'
        mf = UserInfoModalForm(instance=user_obj) # 把指定行数据传过来
        return render(request, 'day24-app06/useredit.html', {'mf': mf, 'nid': nid})
    elif request.method == "POST":
        user_obj = models.UserInfo.objects.filter(id=nid).first()  # 一行数据
        mf = UserInfoModalForm(request.POST, instance=user_obj) # 更新,需要指定更新的数据, 创建,不需要指定数据
        if mf.is_valid():
            print(mf.cleaned_data)
            mf.save()
        else:
            print(mf.errors.as_json())
        return redirect('/cohui/user_list')

user_list.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/cohui/user_list" method="post">
        <ul>
            {% for row in li %}
                <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/cohui/useredit-{{ row.id }}">编辑</a></li>
            {% endfor %}
        </ul>
    </form>
</body>
</html>

useredit.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/cohui/useredit-{{ nid }}" method="post">
        {% csrf_token %}
        <ul>
            {{ mf.as_p }}   <!-- mf -->
            <input type="submit" value="提交" />
        </ul>
    </form>
</body>
</html>

后记

1、modelform的验证规则是,我们写的modelform,继承的父类ModelForm,ModelForm又是继承其父类的BaseModelForm,BaseModelForm又是继承BaseForm:is_valid....

2、生成HTML标签:class Meta: ...
3、生成默认值(编辑):mf = xxxModelForm(instance=Model obj)   # 创建数据不需要instance,更新数据需要
4、额外的标签(免登录单选框), is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
5、各种验证 is_valid() -> 各种钩子clean...
6、保存数据(ModelForm相比model+Form的最大不同点,也是其亮点):

mf.save()
# 查看源码分析:
# mf.save()等价于下面的两步操作
instance = mf.save(False)
instance.save()  # 保存当前的model类

mf.save_m2m() # 保存多对多第三张表的数据

源码解析:
1、点击save()查看源码:def save(self, commit=True):
2、if commit: 为True情况
   self.instance.save()  保存当前表(model obj对象)
   self._save_m2m()  保存多对多表
3、else: 为False情况
       self.save_m2m = self._save_m2m  只是将第三张表另存一份,并改名为self.save_m2m==>obj.save_m2m
   return self.instance ==>返回的是obj.instance 还是当前model类的对象
4、所以,第三张表需要再保存一次,obj.save_m2m()

6.model与form紧耦合,适用于小程序,大程序不要用它。

12-31 16:34
查看更多