Django ModelForm操作及验证方式


Posted in Python onMarch 30, 2020

一、内容回顾

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

--------> 推荐Form <---------

二、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 基本操作

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)
...

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 数据库操作

4.1、创建数据save

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

if obj.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)

这样的话,执行上面的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})

4.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 一样的操作。拆开就可以自定制操作了

4.3、修改数据

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

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

urls.py

url(r'^user_list/', views.user_list),
url(r'^edit-(\d+)/', views.user_edit),

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, 'nid': nid})
user_list.html

<body>
<ul>
{% for row in li %}
<li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/" rel="external nofollow" >编辑</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耦合太密切,所以一般写小程序用它。

补充知识:Django——rest序列化(自定义serializers)

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from repository import models
from rest_framework import serializers

class MyField(serializers.CharField):
  #返回数据到to_respresentation
  def get_attribute(self, instance):
    teacher_list = instance.teachers.all()
    return teacher_list
  #格式化数据
  def to_representation(self, value):
    ret = []
    for row in value:
      ret.append({'id': row.id,'name':row.name})
    return ret


class TestSerializer(serializers.ModelSerializer):
  # get_attribute, 去数据库中获取值
  # to_representation,在页面中显示值
  # level_name = serializers.CharField(source='get_level_display') # obj.get_level_display
  # test_char= serializers.CharField(source='xx.xx.xx') # 以.的形式跨表
  x1 = serializers.SerializerMethodField()
  xx = MyField()
  class Meta:
    model = models.Course
    fields = ['name','level_name']

  def get_x1(self,obj):
    pass

在ModelSerializer直接以get_字段的形式

class CouserDetailSerializer(serializers.ModelSerializer):
  course_name = serializers.CharField(source='course.name')
  recommend_courses_list = serializers.SerializerMethodField()
  price_policy_list = serializers.SerializerMethodField()

  class Meta:
    model = models.CourseDetail
    fields = ['id','course_name','recommend_courses_list']

  def get_recommend_courses_list(self,obj):
    ret = []
    course_list = obj.recommend_courses.all()
    for item in course_list:
      ret.append({'id':item.id,'name':item.name})
    return ret

  def get_price_policy_list(self,obj):
    # 当前课程所有的价格策略
    # ret = []
    # price_policy_list = obj.course.price_policy.all()
    # return ret

    # ret = []
    # price_policy_list = models.PricePolicy.objects.filter(content_type__app_label='repository',
    #                  content_type__model='course',
    #                  object_id=obj.couser_id)
    # return ret
    return "ssss"

以上这篇Django ModelForm操作及验证方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python里disconnect UDP套接字的方法
Apr 23 Python
Python多维/嵌套字典数据无限遍历的实现
Nov 04 Python
Python中异常重试的解决方案详解
May 05 Python
Python调用C++,通过Pybind11制作Python接口
Oct 16 Python
解决python打不开文件(文件不存在)的问题
Feb 18 Python
Python安装与基本数据类型教程详解
May 29 Python
Python pandas.DataFrame调整列顺序及修改index名的方法
Jun 21 Python
python实现桌面气泡提示功能
Jul 29 Python
Python3 中作为一等对象的函数解析
Dec 11 Python
Django 解决新建表删除后无法重新创建等问题
May 21 Python
Python实现简单的猜单词小游戏
Oct 28 Python
Python 循环读取数据内存不足的解决方案
May 25 Python
windows10环境下用anaconda和VScode配置的图文教程
Mar 30 #Python
Python GUI编程学习笔记之tkinter控件的介绍及基本使用方法详解
Mar 30 #Python
Python GUI编程学习笔记之tkinter界面布局显示详解
Mar 30 #Python
自定义实现 PyQt5 下拉复选框 ComboCheckBox的完整代码
Mar 30 #Python
动态设置django的model field的默认值操作步骤
Mar 30 #Python
python数据库操作mysql:pymysql、sqlalchemy常见用法详解
Mar 30 #Python
django 实现手动存储文件到model的FileField
Mar 30 #Python
You might like
TP5框架model常见操作示例小结【增删改查、聚合、时间戳、软删除等】
2020/04/05 PHP
浅谈php常用的7大框架的优缺点
2020/07/20 PHP
js操作select控件的几种方法
2010/06/02 Javascript
javascript中Object使用详解
2015/01/26 Javascript
js实现跨域的多种方法
2015/12/25 Javascript
JS拖拽组件学习使用
2016/01/19 Javascript
详解Angular 自定义结构指令
2017/06/21 Javascript
详解基于Vue2.0实现的移动端弹窗(Alert, Confirm, Toast)组件
2018/08/02 Javascript
Vue2实时监听表单变化的示例讲解
2018/08/30 Javascript
解决vue项目使用font-awesome,build后路径的问题
2018/09/01 Javascript
vue自定义底部导航栏Tabbar的实现代码
2018/09/03 Javascript
vue集成百度UEditor富文本编辑器使用教程
2018/09/21 Javascript
详解Vue路由自动注入实践
2019/04/17 Javascript
Layui给switch添加响应事件的例子
2019/09/03 Javascript
JS实现动态倒计时功能(天数、时、分、秒)
2019/12/12 Javascript
JavaScript实现动态留言板
2020/03/16 Javascript
[51:52]Liquid vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.24
2019/09/10 DOTA
[01:10]DOTA2英雄背景故事第四期之混沌法则混沌骑士
2020/07/16 DOTA
Python中用altzone()方法处理时区的教程
2015/05/22 Python
Python写的一个简单监控系统
2015/06/19 Python
小小聊天室Python代码实现
2016/08/17 Python
对Python 2.7 pandas 中的read_excel详解
2018/05/04 Python
Python推导式简单示例【列表推导式、字典推导式与集合推导式】
2018/12/04 Python
简单了解django orm中介模型
2019/07/30 Python
python树的同构学习笔记
2019/09/14 Python
Python3标准库之functools管理函数的工具详解
2020/02/27 Python
Python Numpy中数据的常用保存与读取方法
2020/04/01 Python
python快速安装OpenCV的步骤记录
2021/02/22 Python
设计师珠宝:Ylang 23
2018/05/11 全球购物
SOA面试题:如何在SOA中实现松耦合
2013/07/21 面试题
后勤人员岗位职责
2013/12/17 职场文书
公司感谢信范文
2015/01/22 职场文书
2015年科室工作总结
2015/04/10 职场文书
2015国庆节宣传语
2015/07/14 职场文书
幼儿园国培研修日志
2015/11/13 职场文书
浅谈GO中的Channel以及死锁的造成
2022/03/18 Golang