【问题标题】:How to use serializer result as queryset on another serializer如何在另一个序列化器上使用序列化器结果作为查询集
【发布时间】:2016-07-12 23:56:26
【问题描述】:

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

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 django-rest-framework django-serializer


    【解决方案1】:

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

    在尝试标准化问题之前,我通过序列化程序 init 中的 try catch 过滤了数据字典中传递的父属性的任何适用字段的查询集,在不同的地方修复了这个问题 - 以下是我想出的。

    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 始终可用,而父字段的对象可能不是例如在 PATCH 请求的情况下。
    • 您会注意到我在序列化程序初始化的后半部分注入了任何省略的关系值,这用于强制验证在多个字段上运行 - 很有用,例如如果用户在PATCH 请求中更改了记录的organization,则意味着分配的teams 不再适用。

    支持远亲

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

    【讨论】:

      猜你喜欢
      • 2021-05-08
      • 2020-11-27
      • 1970-01-01
      • 2020-11-24
      • 2021-07-24
      • 2013-05-16
      • 2021-12-27
      • 2017-02-08
      • 2012-01-15
      相关资源
      最近更新 更多