61

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

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嵌套资源应用过滤器?

顺便说一句,我不想​​为此使用 a 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

非常感谢您的解决方案!

4

5 回答 5

30

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

尽管如此,这似乎有点像黑客。我对DRF相当陌生,所以也许有更多经验的人可以加入。

于 2013-05-30T15:31:22.033 回答
10

您必须使用过滤器而不是获取,否则如果返回多条记录您将得到异常。

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
于 2013-09-25T12:09:53.940 回答
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. 添加子类 ListSerializer 作为嵌套 Serializer 上的元 list_serializer_class
于 2015-02-05T21:33:00.950 回答
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)

我喜欢这种方法,因为它使您的 API 在底层与 HTTP 上的 api 保持一致。

class ZoneSerializer(serializers.HyperlinkedModelSerializer):
    current_user_zone_permission = ZonePermissionSerializer()

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

希望这可以帮助一些人!

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

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

于 2015-04-17T08:52:28.130 回答
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
于 2015-08-31T10:46:00.787 回答