Django REST framework的各种技巧——2.serializer

14

serializer只做一件事情,json化model对象,所以这一部分相当简单

Django REST framework的各种技巧【目录索引】

写在最上面

所有的代码都是在下面的两个版本来做的

django==1.8.8
djangorestframework==3.2.5 

首先是官方文档

讲解

拿基本的user,group为例子

首先一个关联的model

class UserProfile(TimeStampedModel):

    user = models.OneToOneField(User, unique=True, db_index=True, related_name='profile')
    name = models.CharField(blank=True, max_length=255, db_index=True)
    phone = models.CharField(default='', blank=True, max_length=64)
    nickname = models.CharField(blank=True, null=True, max_length=255, db_index=True)
    avatar = models.URLField(blank=True, max_length=255, default='')
    is_cms_user = models.BooleanField(default=False, db_index=True)
    is_cms_active = models.BooleanField(default=False, db_index=True)

    class Meta:  # pylint: disable=missing-docstring
        db_table = "auth_userprofile"

    def __unicode__(self):
        return self.name

User对应的serializer

class GroupSerializer(serializers.ModelSerializer):

    class Meta:
        model = Group
        fields = ('id', 'name')

class UserSerializer(serializers.ModelSerializer):
    groups = GroupSerializer(many=True)
    phone = serializers.CharField(source='profile.phone', read_only=True)
    name = serializers.CharField(source='profile.name', read_only=True)
    menus = serializers.SerializerMethodField()
    is_active = serializers.BooleanField(source='profile.is_cms_active')

    def get_menus(self, user):
        return get_menus(user)

    class Meta:
        model = User
        fields = ('id', 'username', 'name', 'email', 'phone', 'groups', 'menus', 'is_active')

一个请求的response

{
    "id": 2,
    "username": "duoduo3369",
    "name": "",
    "email": "",
    "phone": "",
    "groups": [
        {
            "id": 1,
            "name": "sysadmin"
        },
        {
            "id": 17,
            "name": "大学2"
        }
    ],
    "menus": [
        {
            "menu": [
                {
                    "menu": [],
                    "codename": "information.announcement",
                    "name": "通知公告",
                    "order": 1
                },
                {
                    "menu": [],
                    "codename": "information.examinfo",
                    "name": "考试信息",
                    "order": 2
                }
            ]
       }
    ],
    "is_active": false
}
  • 外键直接可以引用其他的serializer,例如group,可以看到response中group是嵌套的

  • 外键的属性可以使用source,例如phone

  • 不在原来model上的东西使用SerializerMethodField(或者在model上但是你要对这个值做一些特殊处理)

注意点

  • serializer可以做逻辑上的操作,然而最好不要做查询(你可以用SerializerMethodField做一些数据转换例如0变为假1变为真什么的,然而最好不要做复杂的数据库查询),这种事情可以在view上做好(注意可以用select_related减少多次查询),因为这是每一个model都要serializer一次。

  • 如果说跟前端对的修改和查询使用不同的serializer,那么你就写两个,不希望修改的字段加上readonly(或者放在readonly_fields里面)

serializer的逻辑很简单,想到复杂的东西再说。Done


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

19 条评论
Vereesa · 2016-04-18

TimeStampedModel 为什么使用这个model,这个是个什么用额。

回复

D咄咄 作者 · 2016-04-22

哦 继承他的model会有带两个时间created(记录创建时间)updated(记录更新时间) 名字大体是这样 这是django-extensions这个包里面的

回复

Vereesa · 2016-04-22

serializer 文件中的get_menus写错了额。例子写的不全面,要是有链接就好啦~嘿嘿。

回复

0

没错啊

D咄咄 作者 · 2017-05-15
卓修武 · 2016-07-19

serializer不要做查询时什么意思,serializer不就是在查询数据库吗

回复

0

serializer是model to json的东西,数据库查询在model上做

D咄咄 作者 · 2017-05-15
0

@D咄咄 这个不建议这么做,是有什么根据吗?如果有的话,可以给出相关链接吗?谢谢

creazyloser · 7月5日
zonghua · 2017-05-14
def get_menus(self, user):
        return get_menus(user)

这个方法干嘛的?不会爆炸吗?

回复

0

获取用户菜单用的 为什么会爆炸

D咄咄 作者 · 2017-05-15
0

@D咄咄 这不会一直递归下到栈爆吗

zonghua · 2017-05-15
1

又不是 self.get_menus 怎么会爆炸

罗大官人 · 2017-08-29
zonghua · 2017-05-16

还有一个问题请教一下,我使用REST Framework Swagger去做API文档,为什么我扩展的APIView没有被识别出来POST的模型。我看了下源码,是按照get_serializer_class去识别模型,然而我已经定义了,为什么还是没有被实现出来。

回复

0

我的老天爷,怎么今天才看到3个月前的评论的通知。。 回答是不知道,当时没这么用过 swagger

D咄咄 作者 · 2017-09-04
0

@D咄咄 应该是网站的通知功能傻了。

zonghua · 2018-01-21
logan · 2018-01-31

嗯,继续看,你还跟踪吗

回复

冯文瑞 · 4月24日

想问下我在serializer里面如果某个字段校验失败了,怎么返回自定义的json
下面是代码

def validate(self, data):
    if not re.match(r'^1[3-9]\d{9}$', data['mobile']):
        raise serializers.ValidationError({'msg':'手机号格式错误'})

    # 判断两次密码
    if data['password'] != data['password2']:
        raise serializers.ValidationError({'msg':'两次密码不一致'})

    return data

然后返回的就成了这样,怎么在msg里加了一层列表啊?
{

"msg": [
    "手机号格式错误"
]

}

回复

0

这样嵌套试试
raise serializers.ValidationError({'data': {

    "message": "'手机号格式错误'",
    "errorCode": 404
}})
王阿觉 · 8月30日
载入中...