首页 文章

如何在Django REST框架中将过滤器应用于嵌套资源?

提问于
浏览
55

在我的应用程序中,我有以下模型:

class Zone(models.Model):
    name = models.SlugField()

class ZonePermission(models.Model):
    zone = models.ForeignKey('Zone')
    user = models.ForeignKey(User)
    is_administrator = models.BooleanField()
    is_active = models.BooleanField()

我正在使用Django REST框架来创建一个返回区域详细信息的资源,以及一个显示该区域的经过身份验证的用户权限的嵌套资源 . 输出应该是这样的:

{
    "name": "test", 
    "current_user_zone_permission": {
        "is_administrator": true, 
        "is_active": true
    }
}

我已经创建了这样的序列化器:

class ZonePermissionSerializer(serializers.ModelSerializer):
    class Meta:
        model = ZonePermission
        fields = ('is_administrator', 'is_active')

class ZoneSerializer(serializers.HyperlinkedModelSerializer):
    current_user_zone_permission = ZonePermissionSerializer(source='zonepermission_set')

    class Meta:
        model = Zone
        fields = ('name', 'current_user_zone_permission')

这样做的问题是,当我请求特定区域时,嵌套资源会为具有该区域权限的用户返回 all 的ZonePermission记录 . 有没有办法将 request.user 上的过滤器应用于嵌套资源?

顺便说一下,我不想使用 HyperlinkedIdentityField (最小化http请求) .

解决方案

这是我根据下面的答案实现的解决方案 . 我将以下代码添加到我的序列化程序类:

current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission')

def get_user_zone_permission(self, obj):
    user = self.context['request'].user
    zone_permission = ZonePermission.objects.get(zone=obj, user=user)
    serializer = ZonePermissionSerializer(zone_permission)
    return serializer.data

非常感谢您的解决方案!

5 回答

  • 5

    我发现'm faced with the same scenario. The best solution that I'是使用 SerializerMethodField 并使用该方法查询并返回所需的值 . 您可以通过 self.context['request'].user 访问该方法中的 request.user .

    不过,这似乎有点像黑客 . 我对DRF很新,所以也许有经验丰富的人可以加入 .

  • 28

    您必须使用filter而不是get,否则如果多个记录返回,您将获得Exception .

    current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission')
    
    def get_user_zone_permission(self, obj):
        user = self.context['request'].user
        zone_permission = ZonePermission.objects.filter(zone=obj, user=user)
        serializer = ZonePermissionSerializer(zone_permission,many=True)
        return serializer.data
    
  • 4

    现在你可以使用我在这里描述的方法继承ListSerializer:https://stackoverflow.com/a/28354281/3246023

    您可以继承ListSerializer并覆盖to_representation方法 . 默认情况下,to_representation方法在嵌套查询集上调用data.all() . 因此,您需要在调用方法之前生成data = data.filter(** your_filters) . 然后,您需要在嵌套序列化程序的元数据上添加子类ListSerializer作为list_serializer_class . 子类ListSerializer,覆盖to_representation,然后在嵌套的Serializer上调用super add subclassed ListSerializer作为meta list_serializer_class

  • 7

    如果你在多个地方使用QuerySet /过滤器,你可以 use a getter function on your model ,然后甚至删除序列化器/字段的'source' kwarg . DRF automatically calls functions/callables if it finds them 使用它时get_attribute功能 .

    class Zone(models.Model):
        name = models.SlugField()
    
        def current_user_zone_permission(self):
            return ZonePermission.objects.get(zone=self, user=user)
    

    我喜欢这种方法,因为它通过HTTP上的api保持API的一致性 .

    class ZoneSerializer(serializers.HyperlinkedModelSerializer):
        current_user_zone_permission = ZonePermissionSerializer()
    
        class Meta:
            model = Zone
            fields = ('name', 'current_user_zone_permission')
    

    希望这有助于一些人!

    注意:名称不匹配 need ,如果需要/你想仍然可以使用源kwarg .

    编辑:我刚刚意识到模型上的函数无法访问用户或请求 . 因此,自定义模型字段/ ListSerializer可能更适合此任务 .

  • 2

    我会用两种方式之一来做 .

    1)通过视图中的预取来执行此操作:

    serializer = ZoneSerializer(Zone.objects.prefetch_related(
            Prefetch('zone_permission_set', 
                queryset=ZonePermission.objects.filter(user=request.user), 
                to_attr='current_user_zone_permission'))
            .get(id=pk))
    

    2)或者通过.to_代表来做:

    class ZoneSerializer(serializers.HyperlinkedModelSerializer):
    
        class Meta:
            model = Zone
            fields = ('name',)
    
        def to_representation(self, obj):
            data = super(ZoneSerializer, self).to_representation(obj)
            data['current_user_zone_permission'] = ZonePermissionSerializer(ZonePermission.objects.filter(zone=obj, user=self.context['request'].user)).data
            return data
    

相关问题