ModelForm组件的所有参数
ModelForm a. class Meta: # 生成html标签 model, # 对应Model的 fields=None, # 字段 exclude=None, # 排除字段 labels=None, # 提示信息 help_texts=None, # 帮助提示信息 widgets=None, # 自定义插件(modelform自己没有插件:from django.forms import widgets as Fwidgets 避免重名) error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) 字典形式{ "__all__":{# 定制整体的错误信息} "email":{'required': '',定制单独字段的错误信息} } field_classes=None # 自定义字段类(也可以自定义字段,modelform自己没有插件:from django.forms import fields as Ffields 避免与上面字段fields重名) 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={...})
实例:
导入模块:
from app06 import models from django.shortcuts import render,HttpResponse, redirect from django import forms from django.forms import fields as Ffields from django.forms import widgets as Fwidgets
views(创建表单html和数据):
# 写之前,先创建数据库,否则无法创建数据库 # django.db.utils.OperationalError: no such table: app06_usertype ''' class UserInfoForm(forms.Form): username = Ffields.CharField(max_length=32) email = Ffields.EmailField() user_type = Ffields.ChoiceField( choices=models.UserType.objects.values_list('id','caption') # user_type = models.ForeignKey(to='UserType', to_field='id', on_delete=models.CASCADE) # fields没有ForeignKey方法 ) def __init__(self, *args, **kwargs): super(UserInfoForm,self).__init__(*args, **kwargs) self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption') def form(request): if request.method == "GET": obj = UserInfoForm() return render(request, 'form.html', {'obj': obj}) elif request.method == "POST": obj = UserInfoForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) return redirect('/cohui/form') else: print(obj.errors) return render(request, 'form.html', {'obj': obj}) ''' class UserInfoModalForm(forms.ModelForm): # 自定义字段: 不需要保存到数据库的 is_login = Ffields.CharField( widget=Ffields.CheckboxInput() ) # is_login2 = Ffields.CheckboxInput() # X class Meta: # 生成html标签 # model, fields 名字写死 # modal = models.UserInfo X # ValueError: ModelForm has no model class specified. model = models.UserInfo # 绑定一个model类 fields = '__all__' # 所有字段 # fields = ['username', 'email'] # 仅显示 # exclude = ['username', 'email'] # 排除 # 显示中文 verbose_name='用户名'或者: 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',) # 利用钩子验证判断,对username进行clean def clean_username(self): # 获取原来的值 # 利用原来的值进行各种操作 old_value = self.cleaned_data['username'] # .... return old_value def form(request): if request.method == "GET": obj = UserInfoModalForm() return render(request, 'day24-app06/form.html', {'obj': obj}) elif request.method == "POST": obj = UserInfoModalForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) # obj.save() # 保存当前类 instance = obj.save(False) instance.save() # 保存manytomany obj.save_m2m() return redirect('/cohui/forminfo') else: print(obj.errors) return render(request, 'day24-app06/form.html', {'obj': obj})
form.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/cohui/forminfo" method="post"> {% csrf_token %} {{ obj.as_p }} <input type="submit" value="提交"> </form> </body> </html>
views(ModelForm新url编辑):
##############model form 实现新url添加编辑################# def user_list(request): # 多表关联查询 select_related不支持多对多查询 # li = models.UserInfo.objects.all().select_related('user_type', 'u2g') # Invalid field name(s) given in select_related: 'u2g'. Choices are: user_type li = models.UserInfo.objects.all().select_related('user_type') return render(request, 'day24-app06/user_list.html', {'li': li}) def useredit(request, nid): # 获取用户id对象的所有信息 # 显示用户已经存在的数据 if request.method == "GET": user_obj = models.UserInfo.objects.filter(id=nid).first() # 一行数据 # mf = UserInfoModalForm() #编辑跳转是空数据 # mf = UserInfoModalForm(user_obj) # 把指定行数据传过来 # AttributeError: 'UserInfo' object has no attribute 'get' mf = UserInfoModalForm(instance=user_obj) # 把指定行数据传过来 return render(request, 'day24-app06/useredit.html', {'mf': mf, 'nid': nid}) elif request.method == "POST": user_obj = models.UserInfo.objects.filter(id=nid).first() # 一行数据 mf = UserInfoModalForm(request.POST, instance=user_obj) # 更新,需要指定更新的数据, 创建,不需要指定数据 if mf.is_valid(): print(mf.cleaned_data) mf.save() else: print(mf.errors.as_json()) return redirect('/cohui/user_list')
user_list.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/cohui/user_list" method="post"> <ul> {% for row in li %} <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/cohui/useredit-{{ row.id }}">编辑</a></li> {% endfor %} </ul> </form> </body> </html>
useredit.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/cohui/useredit-{{ nid }}" method="post"> {% csrf_token %} <ul> {{ mf.as_p }} <!-- mf --> <input type="submit" value="提交" /> </ul> </form> </body> </html>
后记
1、modelform的验证规则是,我们写的modelform,继承的父类ModelForm,ModelForm又是继承其父类的BaseModelForm,BaseModelForm又是继承BaseForm:is_valid....
2、生成HTML标签:class Meta: ...
3、生成默认值(编辑):mf = xxxModelForm(instance=Model obj) # 创建数据不需要instance,更新数据需要
4、额外的标签(免登录单选框), is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
5、各种验证 is_valid() -> 各种钩子clean...
6、保存数据(ModelForm相比model+Form的最大不同点,也是其亮点):
mf.save() # 查看源码分析: # mf.save()等价于下面的两步操作 instance = mf.save(False) instance.save() # 保存当前的model类 mf.save_m2m() # 保存多对多第三张表的数据 源码解析: 1、点击save()查看源码:def save(self, commit=True): 2、if commit: 为True情况 self.instance.save() 保存当前表(model obj对象) self._save_m2m() 保存多对多表 3、else: 为False情况 self.save_m2m = self._save_m2m 只是将第三张表另存一份,并改名为self.save_m2m==>obj.save_m2m return self.instance ==>返回的是obj.instance 还是当前model类的对象 4、所以,第三张表需要再保存一次,obj.save_m2m()
6.model与form紧耦合,适用于小程序,大程序不要用它。