Django ModelForm操作及验证方式
作者:马小跳_ 发布时间:2021-04-22 18:11:44
一、内容回顾
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"
来源:https://www.jianshu.com/p/dee5806b27a1


猜你喜欢
- 导语暗恋让人受尽委屈!一开始,你是我的秘密,我怕你知道,又怕你不知道,又怕你知道装作不知道!这大概就是暗恋的感受吧,可若是双向奔赴,那简更是
- 什么是循环? —> 循环是有着周而复始的运动或变化的规律;在 Python 中,循环的操作也叫做 &lsq
- 在我们开始一个项目的设计的时候,脑子里肯定有无数的构想。是做视觉冲击强烈、夺人眼球的绚丽风格?还是复古的拼贴风?又或者目前最in的极简主义设
- 在日常的编程中,我经常需要标识存在于文本文档中的部件和结构,这些文档包括:日志文件、配置文件、定界的数据以及格式更自由的(但还是
- 本文实例讲述了MySQL查看、创建和删除索引的方法。分享给大家供大家参考。具体如下:1.索引作用在索引列上,除了上面提到的有序查找之外,数据
- 本文实例为大家分享了小程序自定义弹出框效果的具体代码,供大家参考,具体内容如下my-pop----api:title ------字符串--
- 生活中经常会碰到多个excel表格汇总成一个表格的情况,比如你发放了一份表格让班级所有同学填写,而你负责将大家的结果合并成一个。诸如此类的问
- 年前帮manager 招GUI设计实习生 (PS. 这个实习生职位依然open,欢迎有兴趣的同学来投,邮箱jj.ying [at] hp.c
- 前言最近发现有些东西长时间不用就要忘了,坚持每天复习总结一个小知识点吧~异常是什么呢?就是在代码执行过程中非预期的执行结果,随着代码越来越复
- SOAP.py 客户机和服务器SOAP.py 包含的是一些基本的东西。没有 Web 服务描述语言(Web Services Descript
- 本文主要包括三大方面,大家仔细学习。1、导航栏中的表单导航栏中的表单不是使用 Bootstrap 表单 章节中所讲到的默认的 class,它
- 引言python中的模块、库、包有什么区别?module:一个 .py 文件就是个 modulelib:抽象概念,和另外两个不是一类,只要你
- floor()方法返回不大于x的最大整数(向下取整)。语法以下是floor()方法的语法:import mathmath.flo
- 作者:敖士伟 Email:ikmb@163.com 转载注明作者 说明: 1、js根据表单元素class属性,把表单元素的name和valu
- 前言由于一直用Linux系统,对于词典的支持特别不好,对于我这英语渣渣的人来说,当看英文文档就一直卡壳,之前用惯了有道词典,感觉很不错,虽然
- 在项目里碰到需要把类似'450000'的数字转换为会计记账所用的格式,'450,000.00',分隔千分位和
- 一、问题描述使用idea操作代码进行VCS很是方便,向github进行push和pull操作非常方便,但是,最近频繁提示需要重新输入用户名和
- 概述Golang 是一个跨平台的新生编程语言. 今天小白就带大家一起携手走进 Golang 的世界. 常量常量 (Constant) 是指程
- 本文实例讲述了Python实现文件内容批量追加的方法。分享给大家供大家参考,具体如下:#coding:utf-8import os#----
- 本文列举了所有关于PHP语言中使用socket相关服务的一些函数。注意使用如下函数之前,你需要确保你的socket已打开,如果你没有打开,请