• Django 之ModelForm


    1.Form表单的回顾

        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
    View Code

    2.Form表单的基本操作

    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')
    View Code

    3.ModelForm

    所谓的ModelForm是将Model和Form结合在了一起,虽然减少了重复的代码,但是它的耦合性比较低.

    那么来看看ModelForm的基本操作:

    #####ModelForm########
    from django.forms import ModelForm
    class UserInfoModelForm(forms.ModelForm):
    
        class Meta:
            model = models.UserInfo    # 与models建立了依赖关系
            fields = "__all__"
    
    #########view##########
    def index(request):
        if request.method == "GET":
            obj = UserInfoModelForm()  #实例化这个ModelForm类
            return render(request,"index.html",{'obj':obj})
        
        #提交数据
        elif request.method == "POST":
           #将数据放到ModelForm里
            obj = UserInfoModelForm(request.POST)
           #做验证
            print(obj.is_valid())  # 这是方法,别忘记了加括号
            print(obj.cleaned_data)  #验证后的数据
            print(obj.errors)
            return render(request,"index.html",{'obj':obj})
    View Code

    展示指定的列

            fields = ['username','email']   # 显示指定列
            exclude = ['username']          # 排除指定列
    View Code

    ModelForm和Form的验证流程

    # Form验证:
        UserInfoForm -> Form -> BaseForm( 包含is_valid等方法)
    
    # ModelForm验证:
        UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm
    View Code

    4.ModelForm的参数

    odelForm
        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)
            ...
    
            PS: 单纯初始化
                model_form_obj = XXOOModelForm(initial={...})
    View Code

    注意:

    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',)  # 哪些字段做本地化
    View Code

    5.ModelForm的数据库操作部分

    1.1、创建数据save

    如果数据验证是ok的,那么save,就直接在数据库中创建完数据了

     if form.is_valid():
                obj.save()      # 创建数据

    在如下一对多、多对多关系中:

    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)
    View Code

    这样的话,执行上面的obj.save()会在UserInfo表和多对多关系表里都增加数据。

    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})
    View Code

    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 类的对象
    View Code

    所以instance = obj.save(False)时,什么都不会操作。

    if obj.is_valid():
        instance = obj.save(False)
        instance.save()     # 当前对象表数据创建
        obj.save_m2m()      # 多对多表数据创建
        # 上面这三句完成的是和上面 obj.save 一样的操作。拆开就可以自定制操作了
    View Code

    修改表数据是,记得把instance信息也传进去,不然是新建数据,而不是对某行数据进行修改。

    编辑用户信息,新url方式保留默认数据

    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, 'nid': nid})
    View Code

    6.ModelForm钩子、额外字段

    数据验证钩子

    从上面的Form和ModelForm中,他们都是继承了BaseForm,而is_valid是在BaseForm中定义的,所以ModelForm也能和Form一样使用各种钩子

    额外字段

    像网页上的checkbox,一个月内免登陆,用提交到数据库么?这个只需要设置session和cookie就可以了。

    class UserInfoModelForm(forms.ModelForm):
    
        is_rmb = fields.CharField(widget=widgets.CheckboxInput())  # 额外字段
    
        class Meta:
            model = models.UserInfo
            fields = '__all__'
    View Code

    总结

       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()
    View Code

    批量添加class

    class EnrollmentForm(ModelForm):
        def __new__(cls, *args, **kwargs):
            print("__new__",cls,args,kwargs)
            for field_name in cls.base_fields:
                filed_obj = cls.base_fields[field_name]
                filed_obj.widget.attrs.update({'class':'form-control'})
                if field_name in cls.Meta.readonly_fields:
                    filed_obj.widget.attrs.update({'disabled': 'true'})
            return  ModelForm.__new__(cls)
    
        class Meta:
            model = models.StudentEnrollment
            #fields = ['name','consultant','status']
            fields = "__all__"
            exclude = ['contract_approved_date']
            readonly_fields = ['contract_agreed',]
    
        def clean(self):
            '''form defautl clean method'''
            # print("33[41;1mrun form defautl clean method...33[0m",dir(self))
            # print(self.Meta.admin.readonly_fields)
            print("cleaned_dtat:",self.cleaned_data)
    
            if self.errors:
                raise forms.ValidationError(("Please fix errors before re-submit."))
            if self.instance.id is not None :#means this is a change form ,should check the readonly fields
                for field in self.Meta.readonly_fields:
                    old_field_val = getattr(self.instance,field) #数据库里的数据
                    form_val = self.cleaned_data.get(field)
                    print("filed differ compare:",old_field_val,form_val)
                    if old_field_val != form_val:
                        self.add_error(field,"Readonly Field: field should be '{value}' ,not '{new_value}' ".
                                             format(**{'value':old_field_val,'new_value':form_val}))

    转载请务必保留此出处:http://blog.csdn.net/fgf00/article/details/54917396

  • 相关阅读:
    zabbix api
    cassandra学习笔记
    loki grafana
    logstash配置 filebeat配置
    k8s prometheus api
    51cto 先超Prometheus+Grafana监控告警系统实战
    51cto 张岩峰 Prometheus企业级监控系统:零基础入门
    imooc 使用Prometheus实践基于Spring Boot监控告警体系 笔记
    python3 django migrate
    python3虚拟环境
  • 原文地址:https://www.cnblogs.com/chenxuming/p/9303271.html
Copyright © 2020-2023  润新知