Django Rest框架在SerializerMethodField上排序

aww*_*ter 14 django django-rest-framework

我有一个论坛主题模型,我想在计算的SerializerMethodField上订购,例如vote_count.以下是一个非常简化的Model,Serializer和ViewSet来显示问题:

# models.py
class Topic(models.Model):
    """
    An individual discussion post in the forum
    """
    title = models.CharField(max_length=60)

    def vote_count(self):
        """
        count the votes for the object
        """
        return TopicVote.objects.filter(topic=self).count()


# serializers.py
class TopicSerializer(serializers.ModelSerializer):
    vote_count = serializers.SerializerMethodField()

    def get_vote_count(self, obj):
        return obj.vote_count()

    class Meta:
        model = Topic


# views.py
class TopicViewSet(TopicMixin, viewsets.ModelViewSet):
    queryset = Topic.objects.all()
    serializer_class = TopicSerializer
Run Code Online (Sandbox Code Playgroud)

这是有效的:

  1. OrderingFilter默认打开,我可以成功订购 /topics?ordering=title
  2. vote_count函数完美运行

我试图通过TopicSerializer上的MethodField订购,/topics?ordering=-vote_count但是看起来似乎不支持.我有什么方法可以通过那个领域订购吗?

我简化的JSON响应如下所示:

{
    "id": 1,
    "title": "first post",
    "voteCount": 1
},
{ 
    "id": 2,
    "title": "second post",
    "voteCount": 8
},
{ 
    "id": 3,
    "title": "third post",
    "voteCount": 4
}
Run Code Online (Sandbox Code Playgroud)

我正在使用Ember来使用我的API并且解析器将它转换为camelCase.我也尝试过order = voteCount,但这不起作用(它不应该)

Kev*_*own 33

使用默认值OrderingFilter是不可能,因为排序是在数据库端实现.这是出于效率的原因,因为手动对结果进行排序可能会非常慢,并且意味着违反标准QuerySet.通过将所有内容保持为一个QuerySet,您可以从Django REST框架(通常需要a QuerySet)和内置分页(没有一个可能很慢)中提供的内置过滤中受益.

现在,在这些情况下,您有两种选择:弄清楚如何在数据库端检索您的值,或尝试最小化您将不得不采取的性能损失.由于后一种选择是特定于实现的,我现在要跳过它.

在这种情况下,您可以使用Django提供Count功能在数据库端进行计数.这是作为聚合API的一部分提供的,工作方式与SQL COUNT函数类似.您可以Count通过修改queryset视图来进行等效调用

queryset = Topic.objects.annotate(vote_count=Count('topicvote_set'))
Run Code Online (Sandbox Code Playgroud)

更换topicvote_setrelated_name的领域(你有一组吧?).这将允许您根据投票数量排序结果,甚至可以进行过滤(如果您愿意),因为它在查询本身中可用.

这需要对序列化程序稍作更改,因此它会从vote_count对象上可用的新属性中获取.

class TopicSerializer(serializers.ModelSerializer):
    vote_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Topic
Run Code Online (Sandbox Code Playgroud)

这将覆盖您现有的vote_count方法,因此您可能希望重命名注释时使用的变量(如果您不能替换旧方法).


此外,您可以将方法名称作为sourceDjango REST框架字段传递,它将自动调用它.从技术上讲,你当前的序列化器可能就是这样

class TopicSerializer(serializers.ModelSerializer):
    vote_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Topic
Run Code Online (Sandbox Code Playgroud)

而且它的工作正是像它目前做.请注意,read_only在这种情况下需要,因为方法与属性不同,因此无法设置该值.