使用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
最后再页面上显示出来

{% csrf_token %} {{ obj.as_p }}

最后打开页面查看一下,也是可以自动生成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操作。