如何将序列化器结果用作另一个序列化器上的查询集

How to use serializer result as queryset on another serializer

是否可以像下面的代码那样在序列化程序之间创建依赖关系?

class ProSerializer(serializers.ModelSerializer):
    entity = serializers.PrimaryKeyRelatedField(many=False,queryset=Entity.objects.all())
    foo = serializers.PrimaryKeyRelatedField(many=True,queryset=Foo.objects.filter(entity=entity))
    class Meta:
        model = ..............

我想做的是将 Foo 上的查询集限制为仅来自所选实体的查询集。有办法吗?

Django Rest Framework 并没有让这变得简单,至少在版本 2.x 中是这样——而且我不确定 are/were 是否有任何计划在版本 3 中让它变得更好。

我在不同的地方破解了这个问题,在尝试标准化问题之前,在序列化程序初始化中过滤任何适用字段的查询集 属性 在数据字典中传递 - 以下是我的内容想出了。

SlugRelatedDependentField

class SlugRelatedDependentField(SlugRelatedField):
    def __init__(self, depends_on=None, **kwargs):
        assert depends_on is not None, 'The `depends_on` argument is required.'

        self.depends_on       = depends_on # archive_unit__organization or organization
        self.depends_segments = self.depends_on.split('__')
        self.depends_parent   = self.depends_segments.pop(0)
        self.depends_field    = SimpleLazyObject(lambda: self.parent.parent.fields[self.depends_parent])
        self.depends_queryset = SimpleLazyObject(lambda: self.depends_field.queryset)
        self.depends_model    = SimpleLazyObject(lambda: self.depends_queryset.model)

        super(SlugRelatedDependentField, self).__init__(**kwargs)

    def contextualize(self, instance, data):
        self.data = data
        self.instance = instance

    def get_queryset(self):
        try:
            return self.queryset.filter(**{self.depends_on: reduce(getattr, self.depends_segments, self.get_relation())})
        except self.depends_model.DoesNotExist:
            # if parent was absent or invalid, empty the queryset
            return self.queryset.none()
        except TypeError:
            # if parent was a Page instance, use the full queryset, it's only a list view
            return self.queryset.all()

    def get_relation(self):
        try:
            # if an allowed parent was passed, filter by it
            return self.depends_queryset.get(**{self.depends_field.slug_field: self.data[self.depends_parent]})
        except (KeyError, TypeError):
            # if data was empty or no parent was passed, try and grab it off of the model instance
            if isinstance(self.instance, self.parent.parent.Meta.model):
                return getattr(self.instance, self.depends_parent)
            elif self.instance is None:
                raise self.depends_model.DoesNotExist
            else:
                raise TypeError

用法

class RepositorySerializer(ModelSerializer):
    organization = SlugRelatedField(queryset=Organization.objects.all(), slug_field='slug')
    teams = SlugRelatedDependentField(allow_null=True, depends_on='organization', many=True, queryset=Team.objects.all(), required=False, slug_field='slug')

    def __init__(self, instance=None, data=empty, **kwargs):
        f = self.fields['teams']

        # assign instance and data for get_queryset
        f.child_relation.contextualize(instance, data)

        # inject relation values from instance if they were omitted so they are validated regardless
        if data is not empty and instance and name not in data:
            data[name] = [getattr(relation, f.child_relation.slug_field) for relation in getattr(instance, name).all()]

        super(RepositorySerializer, self).__init__(instance=instance, data=data, **kwargs)

总结

SlugRelatedDependentField 扩展了常规 SlugRelatedField 以接受 depends_on kwarg,它接受描述字段与另一个字段的关系的字符串——在这个例子中,用法描述了分配的团队此存储库必须属于组织。

一些陷阱

  • 如果父项不存在,我用 .none() 清空查询集,这避免了选择泄漏,否则可能会通过 OPTIIONS 请求和验证消息暴露,这通常是不希望的。
  • 我在查询父记录时使用了 data,IIRC 我这样做的原因是因为 data 始终可用,而父字段的对象可能不是,例如在 PATCH 个请求的情况下。
  • 你会注意到我在序列化程序初始化的后半部分注入了任何省略的关系值,这用于强制验证 many 字段上的 运行 - 例如有用如果用户在 PATCH 请求中更改了记录的 organization,这意味着分配的 teams 不再适用。

支持远亲

此解决方案解决的另一个问题是引用远距离关系,这可以通过将 __ 分隔字符串传递给 depends_on 来完成,例如repository__organization,我没有一个很好的示例用例,但如果你需要它就在那里。