Django ModelForm组件原理及用法详解


Posted in Python onOctober 12, 2020

这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来。先来一个简单的例子来看一下这个东西怎么用:

比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?

首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存起来。

其实,重点不是这些,而是合法性验证,我们需要在前端判断用户输入是否合法,比如姓名必须在多少字符以内,电话号码必须是多少位的数字,邮箱必须是邮箱的格式这些。当然可以一点一点手动写限制,各种判断,这毫无问题,不过比较麻烦。

我们现在有个更优雅(以后在Python相关的内容里,要多用“优雅”这个词,并且养成习惯)的方法:ModelForm

先来简单的,生硬的把它用上,再来加验证条件:

首先导入ModelForm

from django.forms import ModelForm

在视图函数中,定义一个类,比如就叫StudentList,这个类要继承ModelForm,在这个类中再写一个原类Meta(规定写法,并注意首字母是大写的)

在这个原类中,有以下属性(部分):

class StudentList(ModelForm):
  class Meta:
    model = models.Student #对应的Model中的类
    fields = "__all__"   #字段,如果是__all__,就是表示列出所有的字段
    exclude = None     #排除的字段
    labels = None      #提示信息
    help_texts = None    #帮助提示信息
    widgets = None     #自定义插件
    error_messages = None  #自定义错误信息
#error_messages用法:
    error_messages = {
      'name':{'required':"用户名不能为空",},
      'age':{'required':"年龄不能为空",},
    }

#widgets用法,比如把输入用户名的input框给为Textarea
#首先得导入模块
    from django.forms import widgets as wid #因为重名,所以起个别名
    widgets = {
      "name":wid.Textarea(attrs={"class":"c1"}) #还可以自定义属性
    }
#labels,自定义在前端显示的名字


labels= {
      "name":"用户名"
    }

如果想增加一个字段,就在class Meta 之前写这个字段。然后在url对应的视图函数中实例化这个类,把这个对象传给前端。

def student(request):
  if request.method == 'GET':
    student_list = StudentList()
    return render(request,'student.html',{'student_list':student_list})

然后前端只需要 {{ student_list.as_p }} 一下,所有的字段就都出来了,可以用as_p显示全部,也可以通过for循环这student_list,拿到的是一个个input框,现在我们就不用as_p,手动把这些input框搞出来,as_p拿到的页面太丑。

首先 for循环这个student_list,拿到student对象,直接在前端打印这个student,是个input框;student.label ,拿到数据库中每个字段的verbose_name ,如果没有设置这个属性,拿到的默认就是字段名;还可以通过student.errors.0 拿到错误信息,还有student.field,是拿到每个字段,如果这个字段是多对多字段,还能stuent.field.queryset,拿到所有关联的字段,还能stuent.field.queryset.model,拿到所有关联的字段的类。有了这些,我们就可以通过bootstrap,自己拼出来想要的样式了

比如:

<body>
  <div class="container" >
    <h1>student</h1>
    <form method="POST" novalidate>
      {% csrf_token %}
{#      {{ student_list.as_p }}#}
      {% for student in student_list %}
        <div class="form-group col-md-6">
         {# 拿到数据字段的verbose_name,没有就默认显示字段名 #}
          <label class="col-md-3 control-label">{{ student.label }}</label>
          <div class="col-md-9" style="position: relative;">{{ student }}</div>
        </div>
      {% endfor %}

      <div class="col-md-2 col-md-offset-10">
        <input type="submit" value="提交" class="btn-primary">
      </div>
    </form>
  </div>
</body>

现在还缺一个input框的form-control样式,可以考虑在后台的widget里面添加。

比如这样:

from django.forms import widgets as wid #因为重名,所以起个别名
    widgets = {
      "name":wid.TextInput(attrs={'class':'form-control'}),
      "age":wid.NumberInput(attrs={'class':'form-control'}),
      "email":wid.EmailInput(attrs={'class':'form-control'})
    }

当然也可以在js中,找到所有的input框,加上这个样式,也行。

保存数据的时候,不用挨个取数据了,只需要save一下。

def student(request):

  if request.method == 'GET':
    student_list = StudentList()
    return render(request,'student.html',{'student_list':student_list})
  else:
    student_list = StudentList(request.POST)
    if student_list.is_valid():
      student_list.save()
    return redirect(request,'student_list.html',{'student_list':student_list})

编辑数据:

如果不用ModelForm,编辑的时候得显示之前的数据吧,还得挨个取一遍值,如果ModelForm,只需要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就可以得到同样的效果。

保存的时候要注意,一定要注意有这个对象(instance=obj),否则不知道更新哪一个数据。

代码示例:

from django.shortcuts import render,HttpResponse,redirect
from django.forms import ModelForm
# Create your views here.
from app01 import models

def test(request):

  # model_form = models.Student
  model_form = models.Student.objects.all()
  return render(request,'test.html',{'model_form':model_form})


class StudentList(ModelForm):
  class Meta:
    model = models.Student #对应的Model中的类
    fields = "__all__"   #字段,如果是__all__,就是表示列出所有的字段
    exclude = None     #排除的字段
    labels = None      #提示信息
    help_texts = None    #帮助提示信息
    widgets = None     #自定义插件
    error_messages = None  #自定义错误信息
#error_messages用法:
    error_messages = {
      'name':{'required':"用户名不能为空",},
      'age':{'required':"年龄不能为空",},
    }

#widgets用法,比如把输入用户名的input框给为Textarea
#首先得导入模块
    from django.forms import widgets as wid #因为重名,所以起个别名
    widgets = {
      "name":wid.Textarea
    }
#labels,自定义在前端显示的名字
    labels= {
      "name":"用户名"
    }
def student(request):

  if request.method == 'GET':
    student_list = StudentList()
    return render(request,'student.html',{'student_list':student_list})
  else:
    student_list = StudentList(request.POST)
    if student_list.is_valid():
      student_list.save()
    return render(request,'student.html',{'student_list':student_list})

def student_edit(request,pk):
  obj = models.Student.objects.filter(pk=pk).first()
  if not obj:
    return redirect('test')
  if request.method == "GET":
    student_list = StudentList(instance=obj)
    return render(request,'student_edit.html',{'student_list':student_list})

  else:
    student_list = StudentList(request.POST,instance=obj)
    if student_list.is_valid():
      student_list.save()
    return render(request,'student_edit.html',{'student_list':student_list})

使用ModelForm编辑数据

对于验证规则,很多浏览器都比较智能,会自动帮我们做一些验证,可以在form表单上加 novalidate 属性就可以不让浏览器为我们做验证

ModelForm还支持所有form的功能,比如钩子,所以我们就可以通过钩子来自定义验证规则

写法和forms的写法一样:

class AuthorForm(forms.ModelForm):
  class Meta:
    model = Author
    fields = ('name', 'title')

  def clean_name(self):
    if ...
       return self.clean_data['name']
    else:
       raise ValidationError(‘sdgsadga')
    ...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python的struct模块中进行数据格式转换的方法
Jun 17 Python
python基础之入门必看操作
Jul 26 Python
Python3实现发送QQ邮件功能(文本)
Dec 15 Python
Python实现的线性回归算法示例【附csv文件下载】
Dec 29 Python
Python PyCharm如何进行断点调试
Jul 05 Python
Python寻找路径和查找文件路径的示例
Jul 10 Python
解决tensorflow打印tensor有省略号的问题
Feb 04 Python
Python 实现日志同时输出到屏幕和文件
Feb 19 Python
Python PIL库图片灰化处理
Apr 07 Python
Python内置异常类型全面汇总
May 28 Python
Python通过len函数返回对象长度
Oct 22 Python
Python卷积神经网络图片分类框架详解分析
Nov 07 Python
Python ConfigParser模块的使用示例
Oct 12 #Python
python修改微信和支付宝步数的示例代码
Oct 12 #Python
教你如何用python操作摄像头以及对视频流的处理
Oct 12 #Python
Django限制API访问频率常用方法解析
Oct 12 #Python
Python confluent kafka客户端配置kerberos认证流程详解
Oct 12 #Python
Django如何使用asyncio协程和ThreadPoolExecutor多线程
Oct 12 #Python
使用Python中tkinter库简单gui界面制作及打包成exe的操作方法(二)
Oct 12 #Python
You might like
php中的注释、变量、数组、常量、函数应用介绍
2012/11/16 PHP
php计算到指定日期还有多少天的方法
2015/04/14 PHP
jQuery Tips 为AJAX回调函数传递额外参数的方法
2010/12/28 Javascript
一些主流JS框架中DOMReady事件的实现小结
2011/02/12 Javascript
JS实现标签页效果(配合css)
2013/04/03 Javascript
JS去除字符串两端空格的简单实例
2013/12/27 Javascript
用js控制组织结构图可以任意拖拽到指定位置
2014/01/17 Javascript
浅谈页面装载js及性能分析方法
2014/12/09 Javascript
你有必要知道的25个JavaScript面试题
2015/12/29 Javascript
js+css绘制颜色动态变化的圈中圈效果
2016/01/27 Javascript
详解JavaScript设计模式开发中的桥接模式使用
2016/05/18 Javascript
不得不看之JavaScript构造函数及new运算符
2017/08/21 Javascript
React教程之封装一个Portal可复用组件的方法
2018/01/02 Javascript
详解原生JS回到顶部
2019/03/25 Javascript
浅谈JavaScript中你可能不知道URL构造函数的属性
2020/07/13 Javascript
[54:27]TNC vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[41:05]Serenity vs Pain 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
Flask解决跨域的问题示例代码
2018/02/12 Python
python中remove函数的踩坑记录
2021/01/04 Python
使用CSS媒体查询(Media Queries)和JavaScript判断浏览器设备类型的方法
2014/04/03 HTML / CSS
HTML5之SVG 2D入门7—SVG元素的重用与引用
2013/01/30 HTML / CSS
最便宜促销价格订机票:Airpaz(总部设在印尼,支持中文)
2018/11/13 全球购物
what is the difference between ext2 and ext3
2015/08/25 面试题
老教师工作总结的自我评价
2013/09/27 职场文书
计算机专业个人简短的自我评价
2013/10/23 职场文书
大学生入党思想汇报
2014/01/01 职场文书
《口技》教学反思
2014/02/21 职场文书
我爱我的祖国演讲稿
2014/05/04 职场文书
义和团口号
2014/06/17 职场文书
开业庆典活动策划方案
2014/09/21 职场文书
民事赔偿协议书
2014/11/02 职场文书
推广普通话的宣传语
2015/07/13 职场文书
学校运动会感想
2015/08/10 职场文书
财务人员入职担保书
2015/09/22 职场文书
Python+Appium自动化测试的实战
2021/06/30 Python
win10键盘驱动怎么修复?Win10键盘驱动修复小技巧
2022/04/06 数码科技