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使用scrapy抓取网站sitemap信息的方法
Apr 08 Python
Python中数字以及算数运算符的相关使用
Oct 12 Python
全面解析Python的While循环语句的使用方法
Oct 13 Python
浅谈python新手中常见的疑惑及解答
Jun 14 Python
Python中django学习心得
Dec 06 Python
python使用tensorflow深度学习识别验证码
Apr 03 Python
pandas string转dataframe的方法
Apr 11 Python
在pycharm中使用git版本管理以及同步github的方法
Jan 16 Python
python GUI库图形界面开发之PyQt5窗口布局控件QStackedWidget详细使用方法
Feb 27 Python
Django+boostrap 美化admin后台的操作
Mar 11 Python
python3从网络摄像机解析mjpeg http流的示例
Nov 13 Python
Python+tkinter实现高清图片保存
Mar 13 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
php 文件上传后缀名与文件类型对照表(几乎涵盖所有文件)
2010/05/16 PHP
ajax在joomla中的原生态应用代码
2012/07/19 PHP
PHP递归删除目录几个代码实例
2014/04/21 PHP
Laravel 4 初级教程之Pages、表单验证
2014/10/30 PHP
写的htc的数据表格
2007/01/20 Javascript
JavaScript判断窗口是否最小化的代码(跨浏览器)
2010/08/01 Javascript
JS动态创建Table,Tr,Td并赋值的具体实现
2013/07/05 Javascript
javascript中call和apply方法浅谈
2013/09/27 Javascript
jquery ezUI 双击行记录弹窗查看明细的实现方法
2016/06/01 Javascript
jQuery EasyUI提交表单验证
2016/07/19 Javascript
jquery select2的使用心得(推荐)
2016/12/04 Javascript
Ajax和Comet技术总结
2017/02/19 Javascript
jQuery插件HighCharts实现的2D面积图效果示例【附demo源码下载】
2017/03/15 Javascript
NodeJS简单实现WebSocket功能示例
2018/02/10 NodeJs
jquery动态添加带有样式的HTML标签元素方法
2018/02/24 jQuery
详解vue axios二次封装
2018/07/22 Javascript
vue-image-crop基于Vue的移动端图片裁剪组件示例
2018/08/28 Javascript
vue+iview/elementUi实现城市多选
2019/03/28 Javascript
搭建一个nodejs脚手架的方法步骤
2019/06/28 NodeJs
JavaScript队列结构Queue实现过程解析
2020/03/07 Javascript
vue 中的动态传参和query传参操作
2020/11/09 Javascript
[52:29]DOTA2上海特级锦标赛主赛事日 - 2 胜者组第一轮#3Secret VS OG第三局
2016/03/03 DOTA
[00:33]2018DOTA2亚洲邀请赛TNC出场
2018/04/04 DOTA
python实现TCP服务器端与客户端的方法详解
2015/04/30 Python
python logging日志模块的详解
2017/10/29 Python
Python3+PyInstall+Sciter解决报错缺少dll、html等文件问题
2019/07/15 Python
详谈tensorflow gfile文件的用法
2020/02/05 Python
关于Tensorflow 模型持久化详解
2020/02/12 Python
多个版本的python共存时使用pip的正确做法
2020/10/26 Python
python中的yield from语法快速学习
2020/11/06 Python
Python从文件中读取数据的方法步骤
2020/11/18 Python
全球最大的跑步用品商店:Road Runner Sports
2016/09/11 全球购物
来自南加州灵感的工作和娱乐服装:TravisMathew
2019/05/01 全球购物
建筑工程毕业生自我鉴定
2014/01/14 职场文书
《女娲补天》教学反思
2016/02/20 职场文书
APP界面设计技巧和注意事项
2022/04/29 杂记