zoukankan      html  css  js  c++  java
  • django ModelForm

    什么是ModelForm

    Django中Model负责操作数据库,并且具有简单的数据库验证功能;Form用于用户请求的验证,具有强悍的数据库验证功能;ModelForm是将二者合二为一,即可用于数据库操作(部分),也可用于用户请求的验证(部分)!

    其实Django Admin就是利用ModelForm的功能实现的。

    Form组件和ModelForm的区别

    ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;

    ModelForm应用场景

    我们来假设这样一个场景,假如,我们有一个类需要保存org的一些信息如下面这些字段。这个类我们叫做ChangOrgModel

    course_num = models.IntegerField(default=0, verbose_name=u'课程数')
    students_num = models.IntegerField(default=0, verbose_name=u'学习人数')
    address = models.CharField(max_length=200, verbose_name=u'地址')

    如果我们使用form我们需要怎么做呢。我们大概需要这样做。

    class OrgModelForm(forms.Form):
        course_num = forms.IntegerField()
        students_num = forms.IntegerField()
        address = forms.CharField(max_length=200)

    我们不难发现,我们的Form定义跟Model的定义基本没什么区别,应该加max_length的时候还是需要加,我们相当于是重写了一遍,其次,这里只有三个,并没有感觉,当有7、8甚至更多的时候,这样重复操作就很让人受不了了。所以这里我们可以使用ModelForm。

    定义ModelForm类

    from django import forms
    from app01 import models
     
    class UserModelForm(forms.ModelForm):
        class Meta:
            model = models.User     #关联的model类
            fields = "__all__"      #或('name','email','user_type')    #验证哪些字段,"__all__"表示所有字段
            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 = ()   #本地化,根据settings中TIME_ZONE设置的不同时区显示时间

    ModelForm验证执行的过程

    Form所有的钩子ModelForm都有。

    is_valid()-->self.errors-->full_clean()-->self._clean_fields() -->  clean_字段名(自定义方法)
                            self._clean_form() -->  clean(self) 
                            self._post_clean() (整体错误)
    
    clean_字段名(自定义方法)

    举例

    新建一个项目untitled1,注意: django版本为1.11

    修改models.py

    from django.db import models
    
    class Depart(models.Model):  # 部门
        caption = models.CharField(verbose_name='部门',max_length=32)
    
        def __str__(self):
            return self.caption
    class Role(models.Model):  # 角色
        title = models.CharField(verbose_name='角色名',max_length=32)
    
        def __str__(self):
            return self.title
    
    class User(models.Model):  # 用户
    
        name = models.CharField(verbose_name='姓名',max_length=32)
        depart = models.ForeignKey(verbose_name='部门',to='Depart',on_delete=models.CASCADE)
    
        gender_choices = (
            (1,''),
            (2,''),
        )
        gender = models.IntegerField(verbose_name='性别',choices=gender_choices,default=1)
    
        roles = models.ManyToManyField(verbose_name='角色',to='Role')

    执行命令,生成表

    python manage.py makemigrations
    python manage.py migrate

    修改admin.py,注册表

    from django.contrib import admin
    from app01 import models
    # Register your models here.
    
    admin.site.register(models.Depart)
    admin.site.register(models.Role)
    admin.site.register(models.User)

    创建超级用户

    登录admin后台,录入数据。这样做的目的是为了渲染页面时,不会出现空页面!

    录入基本数据

     修改urls.py,增加路由

    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/list/$', views.user_list),
    ]

    修改views.py,增加视图

    from django.shortcuts import render
    from app01 import models
    # Create your views here.
    def user_list(request):
        user_queryset = models.User.objects.all()
        return render(request,'user_list.html',{'user_queryset':user_queryset})

     在templates目录下,创建文件user_list.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <a href="/user/add/" class="btn btn-primary">添加</a>
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>名称</th>
                        <th>性别</th>
                        <th>部门</th>
                        <th>角色</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for row in user_queryset %}
                        <tr>
                            <td>{{ row.name }}</td>
                            <td>{{ row.get_gender_display }}</td>
                            <td>{{ row.depart.caption }}</td>
                            <td>
                                {% for node in row.roles.all %}
                                    <span>{{ node.title }}</span>
                                {% endfor %}
                            </td>
                            <td>
                                <a href="/user/edit/{{ row.id }}/">编辑</a>
                            </td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </body>
    </html>
    View Code

    访问页面:http://127.0.0.1:8000/user/list/

    默认是空的。需要写添加页面

    添加页面,怎么写呢?写4个input框?假如有10个表呢?累成狗!

    使用ModelForm,根据models.py定义的字段,自动生成input框!

    添加功能

    修改urls.py,增加路由

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/list/$', views.user_list),
        url(r'^user/add/$', views.user_add),
    ]
    View Code

    修改views.py,增加添加页面,使用ModelForm

    需要导入forms组件

    from django.shortcuts import render,redirect
    from app01 import models
    from django import forms
    
    # Create your views here.
    def user_list(request):
        user_queryset = models.User.objects.all()
        return render(request,'user_list.html',{'user_queryset':user_queryset})
    
    class UserForm(forms.ModelForm):  # 类名最好是表名+Form
        class Meta:
            model = models.User  # user表
            fields = '__all__'  # 所有字段
            # fields = ['name','depart']
            widgets = {
                # 定义name字段的输入框为text
                'name':forms.TextInput(attrs={'class':'form-control'}),
                # 定义depart字段的输入框为Select
                'depart':forms.Select(attrs={'class':'form-control'}),
                'gender':forms.Select(attrs={'class':'form-control'}),
                # 定义roles字段的输入框为多选框
                'roles':forms.SelectMultiple(attrs={'class':'form-control'}),
            }
            # 错误信息
            error_messages = {
                # name字段的错误信息
                'name':{
                    # 英文的required转为中文提示
                    'required':'用户名不能为空'
                }
            }
    
    def user_add(request):
        if request.method == "GET":
            form = UserForm() # 实例化一个空的ModelForm对象
        else:
            form = UserForm(request.POST)  # 接收POST请求数据
            if form.is_valid():  # 进行验证
                print('通过验证')
                form.save()  # 将验证通过的数据插入到数据库中
                return redirect('/user/list/')  # 重定向页面
            
        return render(request,'user_add.html',{'form':form})
    View Code

     在templates目录下,创建文件user_add.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
         <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <h1>添加用户</h1>
            <form method="post" class="form-horizontal" novalidate>
                {% csrf_token %}
                {#for循环form对象#}
                {% for field in form %}
                    <div class="form-group">
                        {#显示字段名#}
                        <label class="col-sm-2 control-label">{{ field.label }} </label>
                        <div class="col-sm-10">
                            {#显示字段的输入框以及错误信息,0表示取第一个错误#}
                          {{ field }} {{ field.errors.0 }}
                        </div>
                      </div>
                {% endfor %}
                <input type="submit" value="提交">
            </form>
        </div>
    </body>
    </html>
    View Code

    刷新页面,点击添加,效果如下:

    直接提交空表单,会有错误提示

    添加一个正常数据

    添加成功后,页面会自动跳转

    修改功能

    修改urls.py , 增加路由

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/list/$', views.user_list),
        url(r'^user/add/$', views.user_add),
        url(r'^user/edit/(?P<uid>d+)/$', views.user_edit),
    ]

    修改views.py,增加视图

    from django.shortcuts import render,redirect
    from app01 import models
    from django import forms
    
    # Create your views here.
    def user_list(request):
        user_queryset = models.User.objects.all()
        return render(request,'user_list.html',{'user_queryset':user_queryset})
    
    class UserForm(forms.ModelForm):  # 类名最好是表名+Form
        class Meta:
            model = models.User  # user表
            fields = '__all__'  # 所有字段
            # fields = ['name','depart']
            widgets = {
                # 定义name字段的输入框为text
                'name':forms.TextInput(attrs={'class':'form-control'}),
                # 定义depart字段的输入框为Select
                'depart':forms.Select(attrs={'class':'form-control'}),
                'gender':forms.Select(attrs={'class':'form-control'}),
                # 定义roles字段的输入框为多选框
                'roles':forms.SelectMultiple(attrs={'class':'form-control'}),
            }
            # 错误信息
            error_messages = {
                # name字段的错误信息
                'name':{
                    # 英文的required转为中文提示
                    'required':'用户名不能为空'
                }
            }
    
    def user_add(request):
        if request.method == "GET":
            form = UserForm() # 实例化一个空的ModelForm对象
        else:
            form = UserForm(request.POST)  # 接收POST请求数据
            if form.is_valid():  # 进行验证
                print('通过验证')
                form.save()  # 将验证通过的数据插入到数据库中
                return redirect('/user/list/')  # 重定向页面
    
        return render(request,'user_add.html',{'form':form})
    
    def user_edit(request,uid):
        # 查询指定id的数据
        obj = models.User.objects.filter(id=uid).first()
        if request.method =='GET':
            # 赋值instance可以使form表单是可以接受对象的数据
            form = UserForm(instance=obj)
            return render(request,'user_edit.html',{'form':form})
        else:
            # instance的参数,如果存在那么save()方法会更新这个实例,否则会创建一个新的实例
            # 由于这里是更新,如果不指定instance。那么它会新增一条数据
            # 我们这里不需要新增,必须要指定instance参数
            form = UserForm(data=request.POST,instance=obj)
            if form.is_valid():
                form.save()  # 更新一条数据
                return redirect('/user/list/')  # 重定向
            else:
                return render(request, 'user_edit.html', {'form': form})
    View Code

    在templates目录下,创建文件user_edit.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
         <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
       <h1>编辑用户</h1>
        <form method="post" novalidate>
            {% csrf_token %}
            {% for field in form %}
                <div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>
            {% endfor %}
            <input type="submit" value="提交">
        </form>
    </div>
    
    </body>
    </html>
    View Code

    刷新页面,点击第一条数据后面的编辑,效果如下:

    上面会自动渲染数据,那么问题来了,它是如何渲染页面的?

    看views中的一段代码

    if request.method =='GET':
            # 赋值instance可以使form表单是可以接受对象的数据
            form = UserForm(instance=obj)
            return render(request,'user_edit.html',{'form':form})

    这里的obj就是,表里面的一条数据,也就是一个对象。那么执行render时,会渲染这个变量。

    看前端页面代码

    <div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>

    这里的field,就是字段对象。注意:这个对象是有值的,forms组件会渲染它!

    修改一下数据

    点击提交,它会自动跳转

    数据就更改了!

    删除功能

    添加路由

    修改user_list.html, 增加删除按钮

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <a href="/user/add/" class="btn btn-primary">添加</a>
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>名称</th>
                        <th>性别</th>
                        <th>部门</th>
                        <th>角色</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for row in user_queryset %}
                        <tr>
                            <td>{{ row.name }}</td>
                            <td>{{ row.get_gender_display }}</td>
                            <td>{{ row.depart.caption }}</td>
                            <td>
                                {% for node in row.roles.all %}
                                    <span>{{ node.title }}</span>
                                {% endfor %}
                            </td>
                            <td>
                                <a href="/user/edit/{{ row.id }}/">编辑</a>
                                <a href="/user/del/{{ row.id }}/">删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </body>
    </html>
    View Code

    刷新页面,点击删除

    参考博客

      https://www.cnblogs.com/xiao987334176/p/9524510.html

  • 相关阅读:
    制作你的第一个HTML 5游戏
    拒绝臆想,让我们脚踏实地做设计!
    HTML 5或者Silverlight?
    如何替换掉回车键
    杀死团队的武器与修复团队的方法
    Google趋势中显示jQuery是最流行的JavaScript框架
    关于脚本载入器的思考
    简化Web开发的12个HTML5CSS框架
    线框图(demo草图)制作的总结
    江苏南通周边经纬度
  • 原文地址:https://www.cnblogs.com/erhao9767/p/10739750.html
Copyright © 2011-2022 走看看