• Day24-ModelForm操作及验证


    Day23内容回顾--缺失,遗憾成狗。

    一:Model(2个功能)

      -数据库操作;

      -验证,只有一个clean方法可以作为钩子。

    二:Form(专门来做验证的)--------根据form里面写的类,类里面有字段,这个字段是内置的正则表达式。

      例如:-class LoginForm(Form): email=fields.EmailField()

      -is_valid(触发验证功能) ->每一个字段进行正则(用的是字段内置的正则)+clean_字段 -> clean(__all__) -> _post_clean。

          form的钩子,先进行每一个字段正则表达式验证,然后每一个字段的方法,然后进行clean,clean完了以后是post clean

          email的错误信息放在email [大列表里面了], 整体的错误信息放在了 __all__  里面了。

      -cleaned_data

      -error

    建议分开使用上面的两个模块:Model+Form,这样可以降低耦合。但是代码有重复。

    Day24:  环境搭建,参考http://www.cnblogs.com/momo8238/p/7508677.html

    一,基于form来做。

    创建2张表备用,2张表之间用ForeignKey进行关联:

    models.py

    from django.db import models
    
    # Create your models here.
    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')
    

     创建

    python manage.py makemigrations
    python manage.py migrate
    

    2. 如果我想在index.html页面上对UserInfo表进行操作,那么应该把UserInfo 表中的所有信息从后台传送过来。把所有的字段重新创建一遍。

    用Form方法的话,需要自己写field字段。 用ModelForm的话,字段是自己从类里面提取过来的。

    views.py

    # Create your views here. 用Form方式实现
    from django.shortcuts import render
    from django import forms
    from django.forms import fields
    from app01 import models
    
    
    class UserInfoForm(forms.Form):
        username = fields.CharField(max_length=32)
        email=fields.EmailField()
        user_type=fields.ChoiceField(
            choices=models.UserType.objects.values_list('id','caption')
            #forms下面是用field字段来进行验证的。forms下面没有foreignKey字段,只有choicefield,然后通过choices把另外一张表里面的内容都关联过来。在页面上把用户类型作为一个列表展示出来
        )
    
    
    def index(request):
        obj=UserInfoForm()
        return render(request,'index.html',{'obj':obj})
    
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
            {% csrf_token %}
            {{obj.as_p}}
    </body>
    </html>
    

     运行效果:

    3. 让user_type每一次都做更新操作,需要重构函数

    说明见下图:

    具体说明可以参考day23

       def __init__(self,*args,**kwargs):
            super(UserInfoForm,self).__init__(*args,**kwargs)
            self.fields['user_type'].choices=models.UserType.objects.values_list('id','caption')
    

     

    4. 完善index.html函数,用form表单提交

    {% csrf_token %} 密文显示
    {{csrf_token }} 在前端页面生成字符串
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="/index/" method="POST">
            {% csrf_token %}
            {{obj.as_p}}
            <input type="submit" value="提交"/>
        </form>
    </body>
    </html>
    

     页面效果:

    5. 一点提交,数据就传到了后台。

    obj.is_valid() #是否通过验证
    obj.cleaned_data() #所有的正确信息
    obj.errors() #所有的错误信息
    models.UserInfo.objects.create(**obj.cleaned_data) #如果数据正确,则创建。是字典格式。
    models.UserInfo.objects.filter(id=1).update(**obj.cleaned_data) #找到主键后自动更新

    程序粘贴:

    views.py
    # Create your views here. 用Form方式实现
    from django.shortcuts import render
    from django import forms
    from django.forms import fields
    from app01 import models
    
    
    class UserInfoForm(forms.Form):
        username = fields.CharField(max_length=32)
        email=fields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id') fields中没有foreignkey
        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')
    
    def index(request):
        if request.method=='GET':
            obj=UserInfoForm()
            return render(request,'index.html',{'obj':obj})
        elif request.method=='POST':
            obj=UserInfoForm(request.POST)
            obj.is_valid()
            obj.cleaned_data()
            obj.errors()
            #models.UserInfo.objects.create(**obj.cleaned_data)
            #models.UserInfo.objects.filter(id=1).update(**obj.cleaned_data)
            return render(request, 'index.html', {'obj': obj})
    

    index.html

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

     models.py

    from django.db import models
    
    # Create your models here.
    class UserType(models.Model):
        caption=models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        username = models.CharField(verbose_name='用户名',max_length=32)
        email=models.EmailField()
        user_type=models.ForeignKey(to='UserType',to_field='id')
    

     urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
    ]
    

    二,基于ModelForm来做。

    1. class UserInfoModelForm(forms.ModelForm): 它也能帮我们自动生成HTML标签。



    views.py
    # 基于ModelForm方式来实现
    from django.shortcuts import render
    from app01 import models
    
    from django import forms
    from django.forms import fields
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
    
    def index(request):
        if request.method=='GET':
            obj=UserInfoModelForm()
            return render(request,'index.html',{'obj':obj})
        elif request.method=='POST':
            obj=UserInfoModelForm(request.POST)
            return render(request, 'index.html', {'obj': obj})
    

      效果:




    2.用Form时,字段是自己定义的。所以如果想显示中文的话,用label标签就可以了。

    views.py
    # 基于Form方式来实现
    from django.shortcuts import render
    from django import forms
    from django.forms import fields
    from app01 import models
    
    
    class UserInfoForm(forms.Form):
        username = fields.CharField(max_length=32,label='用户名')
        email=fields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id') fields中没有foreignkey
        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')
    
    def index(request):
        if request.method=='GET':
            obj=UserInfoForm()
            return render(request,'index.html',{'obj':obj})
        elif request.method=='POST':
            obj=UserInfoForm(request.POST)
            obj.is_valid()
            obj.cleaned_data()
            obj.errors()
            #models.UserInfo.objects.create(**obj.cleaned_data)
            #models.UserInfo.objects.filter(id=1).update(**obj.cleaned_data)
            return render(request, 'index.html', {'obj': obj})
    
    效果如下:

    用ModelForm时,前端页面中的字段是从相关联的类里面直接提取过来的,两者之间有耦合。自己不用再写了。




    如果想要显示中文字段名的话,需要verbose_name='用户'

    效果:



    3.
    model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
    fields='__all__'       #代指的是所有的field字段 
    fields=['username'] #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
    exclude=['username'] #把username列排除在外了。

    效果展示1:

    效果展示2:

    4. 往UserType里面写一条数据备用:

    用form方法的时候,验证功能的函数其实是写在BaseForm里的:UserInfoForm-->继承了Form--->继承了BaseForm(is_valid......)

    点击提交的时候,modelform也可以做验证。UserInfoModelForm-->继承了ModelForm--->继承了BaseModelForm--->继承了BaseForm(is_valid......)

    UserInfoModelForm下面也应该有obj.is_valid(), obj.cleaned_data, obj.errors 等方法。

    ModelForm 做验证的时候与Form方法是一样的。

    效果1:

    效果2:

    三,程序粘贴:

    1. 基于form方法的views.py

    # 基于Form方式来实现
    from django.shortcuts import render
    from django import forms
    from django.forms import fields
    from app01 import models
    
    
    class UserInfoForm(forms.Form):
        username = fields.CharField(max_length=32,label='用户名')
        email=fields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id') fields中没有foreignkey
        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')
    
    def index(request):
        if request.method=='GET':
            obj=UserInfoForm()
            return render(request,'index.html',{'obj':obj})
        elif request.method=='POST':
            obj=UserInfoForm(request.POST)
            obj.is_valid()
            obj.cleaned_data()
            obj.errors()
            #models.UserInfo.objects.create(**obj.cleaned_data)
            #models.UserInfo.objects.filter(id=1).update(**obj.cleaned_data)
            return render(request, 'index.html', {'obj': obj})
    

    2. 基于modelform方法的views.py

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    
    from django import forms
    from django.forms import fields
    
    def orm(request):
        #models.UserType.objects.create(caption='Alex')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
    
    class UserInfoForm(forms.Form):
        username = fields.CharField(max_length=32)
        email=fields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        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')
    
    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})
    

    3.index.html

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

    4.models.py

    from django.db import models
    
    # Create your models here.
    class UserType(models.Model):
        caption=models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        username = models.CharField(verbose_name='用户',max_length=32)
        email=models.EmailField()
        user_type=models.ForeignKey(to='UserType',to_field='id')
    

     5.urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
        url(r'^orm/', views.orm),
    ]
    

    四,Django之ModelForm组件

    4.1 labels=None,                     # 提示信息

    如果不想在Models原表中写verbose_name的话,可以在定义类的时候在ModelForm中写提示信息。

    原来的效果:

     

    现在也可以在modelform中写提示信息:labels是个字典格式,可以写多个。

    4.2  help_texts=None,                 # 帮助提示信息

    4.3 widgets=None,                    # 自定义插件以及样式。

    4.4 错误信息 error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)

    前端:

    后台收集的错误信息:

    错误信息的显示:

    整体的错误信息也可以在这里用同样的方法定义,用的是__all__:{  }。 每一项的错误信息应该放到一个字典中。

    4.5  field_classes=None               # 自定义字段类 (也可以自定义字段)

    我想把邮箱格式的正则表达式改为url的格式进行验证。

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    def orm(request):
        #models.UserType.objects.create(caption='Alex')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
            #labels={'username':'请填入用户名'}
            #help_texts={'username':'请把你的名字写对'}
            #widgets={'username':Fwidgets.Textarea(attrs={'class':'c1'})}
            error_messages={
                '__all__':{},
                'email':{
                    'required':'邮箱不能为空',
                    'invalid':'邮箱格式错误',
                }
            }
            field_classes={
                'email':Ffields.URLField  #这里只能填类,不能加括号,加上括号就变成对象了。
            }
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email=Ffields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        user_type=Ffields.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')
    
    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.as_json())
            return render(request, 'index.html', {'obj': obj})
    

     效果:

     4.6 把时间本地化的时候,需要在setting里面进行一下当前时区的设置。

                localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
                如:
                    数据库中
                        2016-12-27 04:10:57
                    setting中的配置
                        TIME_ZONE = 'Asia/Shanghai'
                        USE_TZ = True
                    则显示:
                        2016-12-27 12:10:57

       

    4.7 切记 class Meta里面最容易犯的错误就是加逗号。千万不要加逗号

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

    五  第一个省事的地方是不用写字段了。第二个省事的地方是验证通过以后,用save方式就可以直接添加到数据库中了。

    程序:

    效果:

    六,再新建一张表,做多对多的示例。

    6.1 models.py

    from django.db import models
    
    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(verbose_name='用户',max_length=32)
        username = models.CharField(max_length=32)
        email=models.EmailField()
        user_type=models.ForeignKey(to='UserType',to_field='id')
        u2g=models.ManyToManyField(UserGroup)
    

     在usergroup表中新建几条数据

    def orm(request):
        models.UserGroup.objects.create(name='CEO')
        models.UserGroup.objects.create(name='CFO')
        models.UserGroup.objects.create(name='COO')
        return HttpResponse('orm')
    

     如下:

    页面效果:发现多对多也显示出来了。

    6.2 页面上新建然后保存数据

    点击提交,就保存到数据库表中了。

    多对多表中数据也增加了。

    这个save的功能太强大了。

    6.3 views.py

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    def orm(request):
        #models.UserGroup.objects.create(name='CEO')
        #models.UserGroup.objects.create(name='CFO')
        #models.UserGroup.objects.create(name='COO')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
            #labels={'username':'请填入用户名'}
            #help_texts={'username':'请把你的名字写对'}
            #widgets={'username':Fwidgets.Textarea(attrs={'class':'c1'})}
            error_messages={
                '__all__':{},
                'email':{
                    'required':'邮箱不能为空',
                    'invalid':'邮箱格式错误',
                }
            }
            #field_classes={'email':Ffields.URLField}
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email=Ffields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        user_type=Ffields.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')
    
    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()
            #print(obj.is_valid())
            #print(obj.cleaned_data)
            #print(obj.errors.as_json())
            return render(request, 'index.html', {'obj': obj})
    

     七,obj.save()的拆分

    查看源码可知,save包含两部分:

           instance.save(当前这个model对象,意思就是可以把当前表save一下)

           obj.save_m2m( )  save ManyToMany,其实就是把两步save拆开了。

    7.1 测试 instance.save

    测试效果:

    7.2 小结:ModelForm可以帮我们生成HTML标签,也可以帮忙做验证,验证完了以后还可以直接保存在数据库中。

    八,基于modelform实现添加数据的功能

    models.UserInfo.objects.all().select_related('user_type','u2g') #跨表

    8.1 添加user_list函数

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    def orm(request):
        #models.UserGroup.objects.create(name='CEO')
        #models.UserGroup.objects.create(name='CFO')
        #models.UserGroup.objects.create(name='COO')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
            #labels={'username':'请填入用户名'}
            #help_texts={'username':'请把你的名字写对'}
            #widgets={'username':Fwidgets.Textarea(attrs={'class':'c1'})}
            error_messages={
                '__all__':{},
                'email':{
                    'required':'邮箱不能为空',
                    'invalid':'邮箱格式错误',
                }
            }
            #field_classes={'email':Ffields.URLField}
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email=Ffields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        user_type=Ffields.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')
    
    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()#这个save实现的时候,其实内部包含了2个步骤。可以拆开。
                instance=obj.save(False) #什么也没干
                instance.save() #只会保存当前这个类,而不会保存manytomany
                obj.save_m2m() #保存manytomany
            #print(obj.is_valid())
            #print(obj.cleaned_data)
            #print(obj.errors.as_json())
            return render(request, 'index.html', {'obj': obj})
    
    def user_list(request):
        li=models.UserInfo.objects.all().select_related('user_type') #先把userinfo中的数据取出来
        return render(request,'user_list.html',{'li':li})
    

    user_list.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <ul>
            {% for row in li %}
                <li>{{row.username}}-{{row.user_type.caption}}</li>
            {% endfor %}
        </ul>
    </body>
    </html>
    

     页面效果:

    8.2  最终想要达到的效果:

    程序粘贴:

    models.py

    from django.db import models
    
    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(verbose_name='用户',max_length=32)
        username = models.CharField(max_length=32)
        email=models.EmailField()
        user_type=models.ForeignKey(to='UserType',to_field='id')
        u2g=models.ManyToManyField(UserGroup)
    

     views.py

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    def orm(request):
        #models.UserGroup.objects.create(name='CEO')
        #models.UserGroup.objects.create(name='CFO')
        #models.UserGroup.objects.create(name='COO')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
            #labels={'username':'请填入用户名'}
            #help_texts={'username':'请把你的名字写对'}
            #widgets={'username':Fwidgets.Textarea(attrs={'class':'c1'})}
            error_messages={
                '__all__':{},
                'email':{
                    'required':'邮箱不能为空',
                    'invalid':'邮箱格式错误',
                }
            }
            #field_classes={'email':Ffields.URLField}
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email=Ffields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        user_type=Ffields.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')
    
    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()#这个save实现的时候,其实内部包含了2个步骤。可以拆开。
                instance=obj.save(False) #什么也没干
                instance.save() #只会保存当前这个类,而不会保存manytomany
                obj.save_m2m() #保存manytomany
            #print(obj.is_valid())
            #print(obj.cleaned_data)
            #print(obj.errors.as_json())
            return render(request, 'index.html', {'obj': obj})
    
    def user_list(request):
        li=models.UserInfo.objects.all().select_related('user_type') #先把userinfo中的数据取出来
        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) #instance传进来表示更新,不加的话表示新建了一条数据。
            if mf.is_valid():
                mf.save()
            else:
                print(mf.errors.as_json())
            return render(request, 'user_edit.html', {'mf': mf, 'nid': nid})
    

     user_edit.html

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

     user_list.html

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

     index.html

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

     urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
        url(r'^user_list/', views.user_list),
        url(r'^edit-(d+)/', views.user_edit),
        url(r'^orm/', views.orm),
    ]
    

     8.3 补充

    ModelForm:通过配置生成标签,用is_valid 进行验证,通过save进行保存。

    ModelForm也可以自己定义钩子,与Form的用法一样。

    在UserInfoModelForm中也可以定义新的字段。比如 username+password 可以从数据库中获取,但是像一个月内免登陆就不需要提交到数据库。像这个字段我们就可以在UserInfoModelForm中单独定义。

    views.py

    # 基于ModelForm方式来实现
    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django import forms
    from django.forms import fields as Ffields
    from django.forms import widgets as Fwidgets
    def orm(request):
        #models.UserGroup.objects.create(name='CEO')
        #models.UserGroup.objects.create(name='CFO')
        #models.UserGroup.objects.create(name='COO')
        return HttpResponse('orm')
    
    class UserInfoModelForm(forms.ModelForm):
        #自定义额外的字段,比如是否要保存一个月内免登陆。
        is_remember=Ffields.CharField(
            widget=Fwidgets.CheckboxInput()
        )
        class Meta:
            model=models.UserInfo  #指去哪个类里面去获取字段,以后也可以做增删改查。
            fields='__all__'       #代指的是所有的field字段
            #fields=['username']   #还可以是一个列表,可以选择要展示哪几列,只展示username这一列
            #exclude=['username']   #把username列排除在外了。
            #labels={'username':'请填入用户名'}
            #help_texts={'username':'请把你的名字写对'}
            #widgets={'username':Fwidgets.Textarea(attrs={'class':'c1'})}
            error_messages={
                '__all__':{},
                'email':{
                    'required':'邮箱不能为空',
                    'invalid':'邮箱格式错误',
                }
            }
            #field_classes={'email':Ffields.URLField}
        def clean_username(self):
            old=self.cleaned_data['username']
            return old
    
    class UserInfoForm(forms.Form):
        username = Ffields.CharField(max_length=32)
        email=Ffields.EmailField()
        #user_type=fields.ForeignKey(to='UserType',to_field='id')
        user_type=Ffields.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')
    
    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()#这个save实现的时候,其实内部包含了2个步骤。可以拆开。
                instance=obj.save(False) #什么也没干
                instance.save() #只会保存当前这个类,而不会保存manytomany
                obj.save_m2m() #保存manytomany
            #print(obj.is_valid())
            #print(obj.cleaned_data)
            #print(obj.errors.as_json())
            return render(request, 'index.html', {'obj': obj})
    
    def user_list(request):
        li=models.UserInfo.objects.all().select_related('user_type') #先把userinfo中的数据取出来
        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) #instance传进来表示更新,不加的话表示新建了一条数据。
            if mf.is_valid():
                mf.save()
            else:
                print(mf.errors.as_json())
            return render(request, 'user_edit.html', {'mf': mf, 'nid': nid})
    

     效果:

    九,总结

    一,ModelForm => 可以做验证,可以做数据库操作。
    	Model+Form => 验证+数据库操作
    	-class LoginModelForm(XXXX):
    		利用model.A中的字段
    	1.生成html标签: class Meta:...
    	2.mf=xxxModelForm(instance=ModelObj)
    	3.额外的标签,
    	4.各种验证 is_valid()->各种钩子...
    	    跟form的钩子一模一样,先进行每一个字段正则表达式验证,然后每一个字段的方法,然后进行clean,clean完了以后是post clean
    	5.mf.save()
    	  ############或者下面的###############
    	  instance=mf.save(False)
    	  instance.save()
    	  mf.save_m2m()
    
  • 相关阅读:
    6.11 修饰符的适用范围
    5.10 对象与垃圾回收
    5.9 枚举类
    5.8 java 11增强的Lambda表达式
    5.7 内部类
    5.6 接口
    5.5 抽象类
    5.4 final修饰符
    5.3 类成员
    5.2 Object类中两个需要被重写的方法
  • 原文地址:https://www.cnblogs.com/momo8238/p/7743004.html
Copyright © 2020-2023  润新知