扩展 django rest 框架以允许在嵌套序列化程序中继承上下文

Posted

技术标签:

【中文标题】扩展 django rest 框架以允许在嵌套序列化程序中继承上下文【英文标题】:Extend django rest framework to allow inheriting context in nested serializers 【发布时间】:2016-05-31 09:23:58 【问题描述】:

我正在使用 Django 1.6(很快升级到 1.8)、Python 2.7 和 DRF 3.2.5(很快升级到最新版本)。

我有一组深度嵌套的序列化程序(大约 10 级深度,总共有 20-30 个模型被序列化)。 我正在尝试向上下文添加一个布尔标志,这将确定序列化的输出层次结构是详细的(包括所有模型的字段)还是基本的(仅部分字段)。

我写了以下代码(部分sn-p):

from rest_framework import serializers
from app.models import Institute, Department, Member

class MemberSerializer(serializers.ModelSerializer):
    def get_fields(self):
        fields = super(MemberSerializer, self).get_fields()
        if self.context['basic_view']:
            for field in ['height', 'weight']:
                del fields[field]
        return fields

    class Meta:
        model = Member
        fields = ('id', 'birth_date', 'height', 'weight')


class DepartmentSerializer(serializers.ModelSerializer):
    members = MemberSerializer(many=True, read_only=True)

    def get_fields(self):
        fields = super(DepartmentSerializer, self).get_fields()
        if self.context['basic_view']:
            for field in ['type', 'manager']:
                del fields[field]
        return fields

    class Meta:
        model = Department
        fields = ('id', 'name', 'type', 'manager', 'members')


class InstituteSerializer(serializers.ModelSerializer):
    departments = DepartmentSerializer(many=True, read_only=True)

    def get_fields(self):
        fields = super(InstituteSerializer, self).get_fields()
        if self.context['basic_view']:
            for field in ['name', 'type']:
                del fields[field]
        return fields

    class Meta:
        model = Institute
        fields = ('id', 'name', 'type', 'departments')

def get_entities(is_basic_view):
    institutes_list = Institute.objects.all()

    serializer = InstituteSerializer(institutes_list, many=True, read_only=True, context='basic_view': is_basic_view)

    return serializer.data

但后来发现从“get_entities”传递到“InstituteSerializer”的“上下文”没有传递给嵌套的序列化程序。 这意味着在上面的示例中 - InstituteSerializer 在“上下文”中有“basic_view”,但 MemberSerializer 和 DepartmentSerializer 没有。

我在context in nested serializers django rest framework 中找到了一个可行的解决方案:对每个嵌套字段(例如“部门”)使用 SerializerMethodField,并在“get_”方法中手动传递上下文。 我对该解决方案的问题是它需要在我的代码中嵌入此代码 20-30 次,最终使源代码行数增加一倍。

我的请求 - 如果有人拥有(或可以帮助实现)serializers.ModelSerializer 的扩展,它将在构造时获得额外的参数,例如'继承上下文'。 然后,我唯一需要在我的类中更改的事情,例如在“InstituteSerializer”中,就是添加该参数:

class InstituteSerializer(serializers.ModelSerializer):
    departments = DepartmentSerializer(many=True, read_only=True, inherit_context=True)

    def get_fields(self):
        fields = super(InstituteSerializer, self).get_fields()
        if self.context['basic_view']:
            for field in ['name', 'type']:
                del fields[field]
        return fields

    class Meta:
        model = Institute
        fields = ('id', 'name', 'type', 'departments')

【问题讨论】:

也许我不太理解,但你为什么不简单地传递一下上下文呢?例如。从 InstituteSerializer 调用 departments = DepartmentSerializer(many=True, read_only=True, context=self.context) 并使用它之前收到的上下文? @C14L 这是不可能的,因为此时'self'是一个未解决的引用 【参考方案1】:

显然我错过了什么...... “上下文”已经继承到嵌套的序列化程序...

但是,它对我不起作用的原因是,作为我嵌套的一部分,一些子序列化程序是通过 serializers.SerializerMethodField() 定义的。 在这种情况下,(仅!)上下文是自动继承的。

解决方案是在与每个 SerializerMethodField 相关的“get_...”方法中简单地传递“上下文”:

class ParentSerializer(serializers.ModelSerializer):
    child = serializers.SerializerMethodField()

    def get_child(self, obj):
        child = ....
        serializer = ChildSerializer(instance=child, context=self.context)
        return serializer.data

P.S - 不久前创建了一个类似于我的 DRF github 问题:https://github.com/tomchristie/django-rest-framework/issues/2555

【讨论】:

以上是关于扩展 django rest 框架以允许在嵌套序列化程序中继承上下文的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest 框架嵌套序列化器

Django rest框架嵌套序列化程序创建方法

django rest 框架模型序列化器 - 读取嵌套,写入平面

嵌套序列化程序 django rest 框架中的上下文

django rest 框架 POST 请求因嵌套序列化而失败

如何在 django rest 框架中的嵌套序列化器相关对象上使用 prefetch_related?