zoukankan      html  css  js  c++  java
  • Django的ModelForm

    一、内容回顾
        Model
            - 数据库操作
            - 验证
            class A(MOdel):
                user = 
                email = 
                pwd = 
    
        Form
            - class LoginForm(Form): 
                email = fields.EmailField()
                user = 
                pwd = 
    
            - is_valid -> 每一个字段进行正则(字段内置正则)+clean_字段 -> clean(__all__)  -> _post_clean
            - cleand_data
            - error
        --------> 推荐 <---------
    
    二、ModelForm操作及验证
    Model + Form ==> ModelForm。model和form的结合体,所以有以下功能:
    
    验证
    数据库操作
    model有操作数据库的字段,form验证也有那几个字段,虽然耦合度降低,但是代码是有重复的。如果利用model里的字段,那是不是form里的字段就不用写了。
    
    1、Model + Form (之前的操作)
    models.py
    
    class UserType(models.Model):
        caption = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        email = models.EmailField()
        user_type = models.ForeignKey(to='UserType',to_field='id')
    
    其中forms.py
    
    from django import forms
    from django.forms import fields
    
    class UserInfoForm(forms.Form):
        # username = models.CharField(max_length=32)    <-- models
        username = fields.CharField(max_length=32)
        # email = models.EmailField()    <-- models
        email = fields.EmailField()
        # user_type = models.ForeignKey(to='UserType',to_field='id')    <-- models
        user_type = fields.ChoiceField(
            choices=models.UserType.objects.values_list('id','caption')
        )
    
        # 下面的操作是让数据在网页上实时更新。
        def __init__(self, *args, **kwargs):
            super(UserInfoForm,self).__init__(*args, **kwargs)
            self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')
    
    index.html
    
    <body>
        <form action="/index/" method="POST" novalidate="novalidate">
            {% csrf_token %}
            {{ obj.as_p }}
            <input type="submit" value="提交">
        </form>
    </body>
    
    novalidate 注: HTML5输入类型和浏览器验证
    
    如果表单中包含URLField、EmailField和其他整数字段类似,Django将使用url、email和number这样的HTML5输入类型。默认情况下,浏览器可能会对这些字段进行他们自身的验证,这些验证可能比Django的验证更严格。如果你想禁用这个行为,请设置form标签的novalidate属性,或者制定一个不同的字段,如TextInput。
    
    2、ModelForm 基本操作
    下面通过modelform实现
    
    forms.py
    
    class UserInfoModelForm(forms.ModelForm):
    
        class Meta:
            model = models.UserInfo    # 与models建立了依赖关系
            fields = "__all__"
    
    views.py
    
    def index(request):
        if request.method == "GET":
            obj = UserInfoModelForm()
            return render(request,"index.html",{'obj':obj})
        elif request.method == "POST":
            obj = UserInfoModelForm(request.POST)
            print(obj.is_valid())  # 这是方法,别忘记了加括号
            print(obj.cleaned_data)
            print(obj.errors)
            return render(request,"index.html",{'obj':obj})
    
    自定制字段名
    如何定义http上定义的字段呢,自定义写成中文的?之前的用法是在Form里写上label。Model Form定义要用verbose_name
    
    models.py
    
    class UserInfo(models.Model):
        username = models.CharField(max_length=32, verbose_name='用户')
        email = models.EmailField(verbose_name='邮箱')
        user_type = models.ForeignKey(to='UserType',to_field='id', verbose_name='类型')
    
    如果不在model里定义,在modelForm里实现,利用labels
    
    class UserInfoModelForm(forms.ModelForm):
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            labels = {
                'username':'用户名',
                'email':'邮箱',
            }
    
    展示指定的列
    fields = "__all__"上面展示所有的,可不可以展示指定的列
    
            fields = ['username','email']   # 显示指定列
            exclude = ['username']          # 排除指定列
    为什么modelForm里也能做验证?
    form里面有is_valid,cleaned_data,errors,
    
    # Form验证:
        UserInfoForm -> Form -> BaseForm( 包含is_valid等方法)
    
    # ModelForm验证:
        UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm
    
    3、ModelForm组件
    ModelForm
        a.  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',) # 本地化,如:根据不同时区显示数据
                如:
                    数据库中
                        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)#更新,传入原始的obj对象,新的数据request.post
            ...
    
            PS: 单纯初始化
                model_form_obj = XXOOModelForm(initial={...})
    
    注意:导入模块名(fields、widgets)和字段名重复,所以导入时要起个别名。
    
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    class UserInfoModelForm(forms.ModelForm):
    
        is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
    
        class Meta:
            model = models.UserInfo
            fields = '__all__'
            # fields =  ['username','email']
            # exclude = ['username']
            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',)  # 哪些字段做本地化
    
    4、ModelForm 数据库操作
    1.1、创建数据save
    如果数据验证是ok的,那么save,就直接在数据库中创建完数据了
    
            if obj.is_valid():
                obj.save()      # 创建数据
    1
    2
    在如下一对多、多对多关系中:
    
    class UserType(models.Model):
        caption = models.CharField(max_length=32)
    
    class UserGroup(models.Model):
        name = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        email = models.EmailField()
        user_type = models.ForeignKey(to='UserType',to_field='id')
        u2g = models.ManyToManyField(UserGroup)
    
    这样的话,执行上面的obj.save()会在UserInfo表和多对多关系表里都增加数据。
    
    views.py
    
    def index(request):
        if request.method == "GET":
            obj = UserInfoModelForm()
            return render(request,'index.html',{'obj': obj})
        elif request.method == "POST":
            obj = UserInfoModelForm(request.POST)
            if obj.is_valid():
                obj.save()  # 等价以下三句
                # instance = obj.save(False)
                # instance.save()
                # obj.save_m2m()
            return render(request,'index.html',{'obj': obj})
    
    
    1.2、save 做了哪些操作?
    save源码里:
    
    def save(self, commit=True):
        """"""
        if commit:
            self.instance.save()    # 指的当前model对象
            self._save_m2m()        # 指:保存m2m对象
        else:
            self.save_m2m = self._save_m2m
        return self.instance    # model 类的对象
        """"""
    
    所以instance = obj.save(False)时,什么都不会操作。
    
    if obj.is_valid():
        instance = obj.save(False)
        instance.save()     # 当前对象表数据创建
        obj.save_m2m()      # 多对多表数据创建
        # 上面这三句完成的是和上面 obj.save 一样的操作。拆开就可以自定制操作了
    
    2、修改数据
    修改表数据是,记得把instance信息也传进去,不然是新建数据,而不是对某行数据进行修改。
    
    编辑用户信息,新url方式保留默认数据
    
    urls.py
    
        url(r'^user_list/', views.user_list),
        url(r'^edit-(d+)/', views.user_edit),
    1
    2
    views.py
    
    def user_list(request):
        li = models.UserInfo.objects.all().select_related('user_type')  # 这里只能是外键,多对多字段也不可以
        return render(request,'user_list.html',{'li': li})
    
    def user_edit(request, nid):
        # 获取当前id对象的用户信息
        # 显示用户已经存在数据
        if request.method == "GET":
            user_obj = models.UserInfo.objects.filter(id=nid).first()
            mf = UserInfoModelForm(instance=user_obj)   # 把默认数据传递进去
            return render(request,'user_edit.html',{'mf': mf, 'nid': nid})
        elif request.method == 'POST':
            # 数据修改的信息,给数据库的哪一行做修改?
            user_obj = models.UserInfo.objects.filter(id=nid).first()
            mf = UserInfoModelForm(request.POST,instance=user_obj)  # 指定给谁做修改
            if mf.is_valid():
                mf.save()
            else:
                print(mf.errors.as_json())
            return render(request,'user_edit.html',{'mf': mf, 
    user_list.html
    
    <body>
        <ul>
            {% for row in li %}
                <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/">编辑</a></li>
            {% endfor %}
        </ul>
    </body>
    
    user_edit.html
    
    <body>
        <form method="POST" action="/edit-{{ nid }}/">
            {% csrf_token %}
        {{ mf.as_p }}
            <input type="submit" value="提交" />
        </form>
    </body>
    
    5、ModelForm钩子、额外字段
    数据验证钩子
    从上面的Form和ModelForm中,他们都是继承了BaseForm,而is_valid是在BaseForm中定义的,所以ModelForm也能和Form一样使用各种钩子
    
    额外字段
    像网页上的checkbox,一个月内免登陆,用提交到数据库么?这个只需要设置session和cookie就可以了。
    
    views.py
    
    class UserInfoModelForm(forms.ModelForm):
    
        is_rmb = fields.CharField(widget=widgets.CheckboxInput())  # 额外字段
    
        class Meta:
            model = models.UserInfo
            fields = '__all__'
    
    6、总结
        1. 生成HTML标签:class Meta: ...
        2. mf = xxxModelForm(instance=ModelObj) 生成默认值
        3. 额外的标签, is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
        4. 各种验证 is_valid() -> 各种钩子...
        5.  mf.save()
            #
            instance = mf.save(False)
            instance.save()
            mf.save_m2m()
    
    ModelForm因为model和form耦合太密切,所以一般写小程序用它。
    补充:modelform 或者 form 生成choices需要查询数据库,实时更新还可以有 models下的单选ModelChoiceField,多选ModelMultipleChoiceField
    class TestForm(form.Form):
        user = field.CharField(max_length=12)
        group =ModelChoiceField(queryset = models.UserGroup.objects.all()) #内部自动更新数据,但是依赖对应model的__str__()
    
    class TestForm(form.Form):
        user = field.CharField(max_length=12)
        group =Field.ChoiceField(choices=[])
    
        def __init__(self, *args, **kwargs):#通过初始化 实时更新
            super(TestForm, self).__init__(*args, **kwargs)
            self.field['group'].choices = models.UserGroup.objects.values_list('id', 'name')
    
    

     我的项目总结关于ModelForm的应用

    目的:根据提交的用户信息,抽取数据库的单独某一列,用于用户填充提交

     1 import copy
     2 import json
     3 import urllib.parse
     4 from django.template.response import TemplateResponse, SimpleTemplateResponse
     5 from django.shortcuts import redirect,render,HttpResponse
     6 from django.urls import reverse
     7 from django.utils.safestring import mark_safe
     8 from django.http.request import QueryDict
     9 from django.forms import Form, ModelForm
    10 from django import forms
    11 from django.forms import fields as fd
    12 from django.forms import widgets
    13 from django.db.models import ForeignKey, ManyToManyField
    14 from crm.utils.pagination import Page
    15 from types import FunctionType
    16 from django.http.request import QueryDict
    17 
    18 from my_projects import models
    19 class Myform(ModelForm):
    20     contract = forms.ChoiceField(choices=models.Contract.objects.values_list('id','contract_name'),label='项目名称')#choice必须穿两个字段,一个是key,一个是显示文本
    21     # num1 = forms.CharField(empty_value='请输入工作时长',label='周期工作时长') #重复model中字段,就覆盖Meta中的,优先级比较高,没有就生成额外字段,但是不会保存到数据库,需要自己手动操作
    22     class Meta:
    23         model=models.User
    24         fields = ['date']
    25         help_texts = {'date':'如:2019-01-1'}
    26         labels = {'date': '周期'}
    27 
    28     def __init__(self,name,*args,**kwargs):
    29         super().__init__(*args, **kwargs) #这里的args kwargs都是供前端使用的,额外需要增加变量接受参数 修改form的属性
    30         #self.fields有序字典 包括生成表单和form类
    31         self.fields[name] = forms.CharField(label='时长',help_text='这周哪个项目的工作时间')
    32         #     print(f] = forms.fields.CharField(label='时长') #self.fields类似于字典格式(字段名作为key,field中的作为对象)
    my_modelform
    
    
     1     def add_view(self, request):
     2         """
     3         添加页面
     4         :param request: 
     5         :return: 
     6         """
     7         if request.path == '/crm/my_projects/user/add/':
     8             self.page_model_form=Myform
     9         if request.method == 'GET':
    10             #obj=models.User.objects.get(id=4) 传querset参数,便于生成默认值
    11             form = self.get_model_form_cls(request.GET.get('name'))#get请求加instance,会在页面填充实例的值,字典传入在相应的form(前端页面)生成value值 如self.get_model_form_cls({'date':'2000-01-01'}) 也可以是实例instance=queryset对象
    12         elif request.method == "POST":
    13             form = self.get_model_form_cls(data=request.POST, files=request.FILES)
    14             if form.is_valid():
    15                 obj = form.save()
    16                 if request.path == '/crm/my_projects/user/add/':  #区别员工提交考勤特殊性
    17                     pk = form.cleaned_data.pop('contract')
    18                     models.Contract.objects.filter(pk=pk).first().user.add(obj.pk)
    19                 popup_id = request.GET.get("_popup")
    20                 if popup_id:
    21                     context = {'pk': obj.pk, 'value': str(obj), 'popup_id': popup_id}
    22                     return SimpleTemplateResponse('crm/popup_response.html',
    23                                                   {"popup_response_data": json.dumps(context)})
    24                 else:
    25                     _change_filter = request.GET.get('_change_filter')
    26                     if _change_filter:
    27                         change_list_url = "{0}?{1}".format(self.changelist_url(), _change_filter)
    28                     else:
    29                         change_list_url = self.changelist_url()
    30                     return redirect(change_list_url)
    31         else:
    32             raise Exception('当前URL只支持GET/POST请求')
    33         context = {
    34             'form': form
    35         }
    36         return TemplateResponse(request, self.add_form_template or [
    37             'crm/%s/%s/add.html' % (self.app_label, self.model_name),
    38             'crm/%s/add.html' % self.app_label,
    39             'crm/add.html'
    40         ], context)
    views.py
    
    
    
     
  • 相关阅读:
    《算法导论》读书笔记之第16章 贪心算法—活动选择问题
    C语言综合实验1—学生信息管理系统
    《算法导论》读书笔记之第15章 动态规划[总结]
    《算法导论》读书笔记之第11章 散列表
    模板类中定义list<T>::iterator iter在g++下不识别的解决办法
    C语言参考程序—无符号一位整数的四则运算
    《算法导论》读书笔记之第15章 动态规划—最优二叉查找树
    C语言综合实验2—长整数运算
    递归与尾递归总结
    《算法导论》读书笔记之第13章 红黑树
  • 原文地址:https://www.cnblogs.com/qlshao/p/10051071.html
Copyright © 2011-2022 走看看