如何在另一个序列化程序中将序列化程序结果用作查询集

时间:2016-07-12 23:56:27

标签: django django-rest-framework django-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上的查询集限制为仅来自所选实体的查询集。有没有办法做到这一点?

1 个答案:

答案 0 :(得分:0)

Django Rest Framework不会让这很容易,至少在版本2.x中 - 我不确定是否有任何计划在版本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,该kwarg接受描述字段与另一个字段的关系的字符串 - 在此示例中,用法描述了分配到此存储库的团队必须属于该组织。

一些问题

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

支持远距离关系

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