使用ModelForm创建
接下来要使用的表结构如下:
# 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', on_delete=models.CASCADE)
表结构写好之后,生成数据库:
···
python manage.py makemigrations
python manage.py migrate
···
ModelForm
上节就说过,ModelForm很简单,所以就下面几行,没太多内容:
# forms.py 文件
class UserInfo(forms.ModelForm):
class Meta:
model = models.UserInfo
fields = '__all__'
处理函数
# views.py 文件
def user_info(request):
if request.method == 'GET':
obj = forms.UserInfo()
return render(request, 'user_info.html', {'obj': obj})
elif request.method == 'POST':
obj = forms.UserInfo(request.POST)
return render(request, 'user_info.html', {'obj': obj})
HTML
最后再页面上显示出来
最后打开页面查看一下,也是可以自动生成html标签的,并且效果上和之前用的Form基本没什么区别。
自定义label
上面在页面中显示的名称是默认的变量名,如果要显示中文,需要在models里定一个变量 verbose_name。
之前在 Python自动化开发学习19-Django 里讲过models的参数。verbose_name :Admin中显示的字段名称,默认显示为变量名。我们用ModelForm也是一样的,因为Admin里用的就是ModelForm。
修改的代码并行下面要将的内容一起了。
显示选项的内容
因为现在usertype表里是空的,所以select下拉列表里没有其他选项。添加几条数据后,页面上会有相应的选项了,但是显示的内容是对象。为了让他显示内容,还要在类里定一个 __str__
方法。
连同上面显示中文标签一起,修改为下面的样子:
# modelspy 文件
class UserType(models.Model):
caption = models.CharField(max_length=32)
def __str__(self):
return self.caption
class UserInfo(models.Model):
username = models.CharField(verbose_name="用户名", max_length=32)
email = models.EmailField(verbose_name="电子邮件")
user_type = models.ForeignKey(verbose_name="用户类型", to='UserType', on_delete=models.CASCADE)
定制页面显示的字段
上面的ModelForm里面的元类的fields变量,设置的是显示所有的字段,也可以传入一个列表,只显示要求的那些字段。或者是使用exclude,表示排除哪些字段:
# fields = '__all__'
# fields = ['username', 'user_type']
exclude = ['email']
ModelForm 中 Form 的功能
先了解一下ModelForm和Form的关系。
之前学习使用的Form,继承的是BaseForm。我这里用的ModelForm,父类是BaseModelForm,再网上找继承的还是BaseForm。
之前学习Form的时候,讲到Form的2个功能,验证和生成html标签。全部都是在BaseForm这个类里实现的。所以这些功能在ModelForm里一样都有,并且用起来和Form几乎也是一样的。
元类里的参数
讲师的博客地址:http://www.cnblogs.com/wupeiqi/articles/6229414.html
在上面的例子中,已经用的了3个参数了,model、fields、exclude。下面看见一共有哪些常用的参数:
model
:对应models的哪张表fields
:显示的字段,__all__
表示全部字段exclude
:排除的自动labels
:自定义标签名,字典类型labels = {'username': "名字", 'email': "邮箱", 'user_type': "类型"}
对应上面的例子。如何还在models里设置了verbose_name,还是以这里的labels为准。help_texts
:提示信息,显示在输入框后面。字典类型和上面一样。widgets
:自定义插件,用的还是form的插件,如果直接导入会重名,要加别名from django.forms import widgets as my_widgets
。用法举例:widgets = {'username': my_widgets.Textarea(attrs={'class': 'c1'})}
error_messages
:自定义错误信息,整体错误信息的key是from django.core.exceptions import NON_FIELD_ERRORS
也就是'__all__'
field_classes
:自定义Form验证的类。默认models里是CharField,那么对于Form的类也是CharField。这个设置可以改掉实现自定义。用法举例:field_classes = {'username': forms.fields.EmailField}
。如果直接导入fields依然会有重名的问题,用as改掉localized_fields
:本地化,根据不同时区显示数据。参数是需要本地化的字段名的元祖,比如:localized_fields=('create_time',)
元类里的很多字段设置都和Form里的用法是一样的。但是Form里是一个字段一个字段设置的,而ModelForm是整张表设置的。所以这里的设置传入的都是字典,key就是字段名,value就是和Form里设置的值一样了。
ModelForm 中 Model 的功能
这里主要就是数据的增删改查了
添加数据
直接获取到对象,用is_valid()方法验证通过后,直接对对象用一个save()方法就能完成数据的添加。下面是可以完成数据添加的处理函数:
# views.py 文件
def user_info(request):
if request.method == 'GET':
obj = forms.UserInfo()
return render(request, 'user_info.html', {'obj': obj})
elif request.method == 'POST':
obj = forms.UserInfo(request.POST)
if obj.is_valid():
obj.save()
return render(request, 'user_info.html', {'obj': obj})
多对多的情况
例子就不写了。如果是有多对多的关联,默认也是会更新多对多关联的第三张表的数据的。
实际上save()方法内部是拆分成2步执行的,先操作当前的这张表,然后去操作多对多关联的第三张表。save()方法有个默认参数 def save(self, commit=True):
默认设置就是上面那样执行的。如果参数是False,就是不帮我们自动更新第三张表。如果要用False参数具体用法如下:
instance = obj.save(False) # 不在任何操作
instance.save() # 保存当前的表的数据
obj.save_m2m() # 保存第三张表的数据
填入默认值
要在生成html的时候,在页面内填入默认值。首先要做一步model操作,去数据库里获取一条数据对象,然后把这个对象作为ModelForm的instance参数传入。代码如下:
if request.method == 'GET':
user_obj = models.UserInfo.objects.filter(id=1).first()
obj = forms.UserInfo(instance=user_obj)
return render(request, 'user_info.html', {'obj': obj})
更新数据
上面填入默认值,在做修改的时候经常会用到。修改完成后提交到后台,就要进行数据更新了。这里不能用直接save()方法,因为这样会变成新建。数据更新用的还是save()方法,但是要再传入一个参数,告诉系统要把新的数据更新到表的哪一条,否则就是新建。具体的做法如下:
elif request.method == 'POST':
user_obj = models.UserInfo.objects.filter(id=1).first()
obj = forms.UserInfo(request.POST, instance=user_obj)
if obj.is_valid():
obj.save()
return render(request, 'user_info.html', {'obj': obj})
这里和上面填入默认值的做法差不多,只不过这里把需要更新的数据也传递进来了。
添加额外的字段
比如一个login登录的应用场景。页面上除了有用户名和密码以外,还可以加一个单选框“记住我”。这个额外的字段是不记录到数据库里的,而是会写到cookie或者session里。这种情况,可以把这个字段定义在ModelForm类里作为公有属性(静态属性),定义的时候完全和定义Form的字段是一样的:
# forms.py 文件
class UserInfo(forms.ModelForm):
is_remembered = forms.fields.CharField(
widget=my_widgets.CheckboxInput(),
label="记住我",
)
class Meta:
# 元类里的内容就略了
这样这个额外的字段在页面里会显示在最后面,有Form的全部功能,验证和生成html。但是不会有model操作。