14、Django_rest framework_ModelSerializer

一、进一步封装优化序列化器

  • 注意:更高一级的封装,代表着更少的代码,也代表着更低的可定制型
  • 要讲诉的方法,类似Django原生的ModelFormmodel的引用

二、ModelSerializer

  • ModelSerializer类能够让我们自动你创建一个具有对应模型类中,相对应字段的Serializer
  • ModelSerializer类直接继承了Serializer类,不同的是:
    1.它根据model模型的定义,自动生成默认字段。
    2.它自动生成序列化器的验证器,比如unique_together验证器。
    3.它实现了简单的.create()方法和.update()方法。
  • 声明一个ModelSerializer类,(用之前写好的序列化器示例,注释之前的类属性字段,修改成ModelSerializer类)
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        # 需要序列化的model类
        model = User

        # 序列化所有的字段
        # fields = '__all__'

        # 序列化指定字段
        # fields = ('name', 'password', 'email', 'sex')

        # 排除哪些字段不进行序列化
        exclude = ('email',)

注意:由于对应了model类的字段,会存在必填字段,所以fileds=__all__exclude慎重使用

  • 进入shell查看ModelSerializer类自动创建了哪些字段和验证器(fileds=__all__的)

    进入shell

  • 测试是否能添加成功:


    添加成功
2.1、明确指定字段

当觉得全自动的字段不满足需求时,可以通过在ModelSerializer类上显式声明字段,从而增加额外的字段或者重写默认的字段,就和在Serializer类一样的。
比如:

class UserSerializer(serializers.ModelSerializer):
    c_time = serializers.DateTimeField(label='创建时间', help_text='创建时间', read_only=True)

    class Meta:
        model = User
        fields = '__all__'
明确指定ctime
2.2、指定只读字段

当我们希望批量将某些字段指定为只读,而不是显式的逐一为每个字段添加read_only=True属性,这种情况就可以使用Metaread_only_fields选项。
该选项的值是字段名称所组成的列表或元组,并像下面这样声明:

class UserSerializer(serializers.ModelSerializer):
    c_time = serializers.DateTimeField(label='创建时间', help_text='创建时间', read_only=True)

    class Meta:
        model = User
        fields = '__all__'
        read_only_fields = ('sex', 'password')
批量指定只读

注意: 有一种特殊情况,其中一个只读字段是模型级别unique_together约束的一部分。在这种情况下,序列化器需要该字段的值才能验证约束,但也是不能由用户编辑的。
处理此问题的正确方法是在序列化器上显式指定该字段,同时提供read_only=True和default=…关键字参数。
这种情况的一个例子就是对于一个和其他标识符unique_together的当前认证的User是只读的。 在这种情况下可以像下面这样声明user字段:

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
  • PrimaryKeyRelatedField处理反向关联关系/反向序列化
    比如说interfaces是projects的从表。此时的设计是:在创建interface时,前端会传递一个project_id,我们实际在创建interface时,是不会使用project_idproject_idinterfaces.modelprojects.model是一个反向关联关系。所以我们此时需要为它添加一个显示字段。
    interfaces/serialzers.py
from rest_framework import serializers

from projects.models import Projects
from interfaces.models import Interfaces


class InterfaceModelSerializer(serializers.ModelSerializer):
    # 从表指定输出主表外键输出的值
    project = serializers.StringRelatedField(label='所属项目', read_only=True)
    # 新增model不存在的字段,前端传递的是project_id,并且和project表是一个反向关联关系,所以需要添加一个显示字段
    project_id = serializers.PrimaryKeyRelatedField(queryset=Projects.objects.all(), write_only=True,
                                                    label='所属项目id', help_text='所属项目id')

    class Meta:
        model = Interfaces
        fields = ('id', 'name', 'tester', 'create_time', 'desc', 'project', 'project_id')

        extra_kwargs = {
            'create_time': {
                'read_only': True
            }
        }

    def create(self, validated_data):
        """
        前端传的是project_id,而对应的字段应该是project,所以需要处理下这里
        :param validated_data:
        :return:
        """
        project_id = validated_data.pop('project_id')
        validated_data['project'] = project_id
        interface_obj = super().create(validated_data)
        return interface_obj
2.3、添加关键字参数

可以通过使用extra_kwargs选项快捷地在字段上指定任意附加的关键字参数。这个选项是一个将具体字段名称当作键值的字典。

  • 用法:在给字段添加ModelSerializer无法自动添加的额外条件时使用
  • 注意:extra_kwargs的值是一个字段,里面的key需要和校验参数一致,否则出错(可看源码)
    例如:
class UserSerializer(serializers.ModelSerializer):
    c_time = serializers.DateTimeField(label='创建时间', help_text='创建时间', read_only=True)

    class Meta:
        model = User
        fields = '__all__'
        extra_kwargs = {
            # model的任意字段
            "name": {
                "write_only": True,  # 字段名别写错
                "error_messages": {  #  字段名别写错
                    "max_length": "用户名最多不能超过50个字符"
                }
            }
        }
添加关键字参数
2.4、添加序列化器自定义的校验
  • 直接复制粘贴过来,但是要注意,必须和class Meta平级
class UserSerializer(serializers.ModelSerializer):
    c_time = serializers.DateTimeField(label='创建时间', help_text='创建时间', read_only=True)

    class Meta:
        model = User
        fields = '__all__'
        extra_kwargs = {
            # model的任意字段
            "name": {
                "write_only": True,  # 字段名别写错
                "error_messages": {  #  字段名别写错
                    "max_length": "用户名最多不能超过50个字符"
                }
            }
        }

    # 自定义字段级别的验证
    def validate_name(self, value):
        """
        用户名需要以“用户”开头
        :return:
        """
        if not str(value).startswith('用户'):
            # 抛出erializers.ValidationError异常
            raise serializers.ValidationError(detail='用户名需要以用户两个字开头')
        # 返回一个验证过的数据
        else:
            return attrs

    # 自定义多个字段的组合验证规则
    def validate(self, attrs):
        """
        password和email必需含有“lzl”这三个字母
        :return:
        """
        if "lzl" not in attrs['password'] or "lzl" not in attrs['email']:
            raise serializers.ValidationError(detail='password和email必需含有“lzl”这三个字母')
        else:
            return attrs
2.5、views.py的修改
  • ModelSerializer类因为实现了简单的.create()方法和.update()方法。所以之前的views.py中的代码只需要把之前的Serializer类改成调用继承了ModelSerializer的类即可
2.6、指定外键序列化输出的值(从表指定主表外键)

主表:projcet
从表:interface
在interface副表中,外键是projcet
当使用ModelSerializer进行序列化时,默认会对project外键进行处理,默认生成的是PrimaryKeyRelatedField序列化器字段,序列化输出的是该interface对应的project表的id。
如果需要修改,那么需要显示处理:

from rest_framework import serializers

from interfaces.models import Interfaces
from projects.serializer import ProjectModelSerializer

class InterfaceModelSerializer(serializers.ModelSerializer):
    # 指定外键序列化输出内容(project是interface的外键字段)
    # 1、StringRelatedField:此字段会被序列化为关联对象字符串表达形式,也就是__str__方法的内容
    project = serializers.StringRelatedField(label='所属项目')
    # 2、SlugRelatedField:指定序列化返回的字段,比如下面序列化的结果是关联project表的leader字段的值
    project = serializers.SlugRelatedField(label='所属项目',  slug_field='leader')
    # 3、  指定返回关联的project序列化器(需要指定read_only,不然前端就需要输入一个project序列化器)
    project = ProjectModelSerializer((label='所属项目',  read_only=True)

    class Meta:
        model = Interfaces
        fields = '__all__'
2.6、指定外键序列化输出的值(主表指定从表)

主表中不会默认生成从表的关联字段,需要手动指定,并且字段名是从表名_set。其它方法和和在从表指定主表的一样。

class ProjectModelSerializer(serializers.ModelSerializer):
    #注意字段名,并且由于是从表,多的一方,要指定many=True
    interfaces_set = serializers.StringRelatedField(label='项目接口', many=True)
    class Meta:
        model = Interfaces
        fields = '__all__'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345