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

时间:2013-05-29 18:51:02

标签: django django-rest-framework

在我的应用中,我有以下型号:

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')

问题在于,当我请求特定区域时,嵌套资源会返回具有该区域权限的用户的所有的ZonePermission记录。有没有办法在request.user上对嵌套资源应用过滤器?

BTW我不想为此使用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 个答案:

答案 0 :(得分:28)

我面临同样的情况。我发现的最佳解决方案是使用SerializerMethodField并使用该方法查询并返回所需的值。您可以通过request.user访问该方法中的self.context['request'].user

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

答案 1 :(得分:7)

你必须使用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

答案 2 :(得分:5)

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

  

您可以继承ListSerializer并覆盖to_representation方法。

     

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

     
      
  1. 子类ListSerializer,覆盖to_representation然后调用super
  2.   
  3. 在嵌套的Serializer上添加子类ListSerializer作为元list_serializer_class
  4.   

答案 3 :(得分:4)

如果你在多个地方使用QuerySet /过滤器,你可以在你的模型上使用getter功能,然后甚至放弃“来源”'用于Serializer / Field的kwarg。 DRF在使用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')

希望这有助于一些人!

注意:名称不需要需要才能匹配,如果您需要/想要,您仍然可以使用源kwarg。

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

答案 4 :(得分: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_representation:

来做
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