Django Rest框架在SerializerMethodField上排序 [英] Django Rest Framework Ordering on a SerializerMethodField

查看:1202
本文介绍了Django Rest框架在SerializerMethodField上排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 #models.py 
class Topic

论坛中的个人讨论文章

title = models.CharField(max_length = 60)

def vote_count(self):

计数对象的投票

返回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 =主题


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

这是有用的:


  1. 订单ingFilter默认启用,我可以成功订购 / topics?ordering = title

  2. vote_count功能完美运行

我正在通过TopicSerializer上的MethodField进行订单,像$ code> / topics?orders = -vote_count 但似乎不支持。有没有办法可以通过该字段订购?



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

  {
id:1,
title:first post,
voteCount:1
},
{
id:2,
title:second post,
voteCount:8
},
{
id:3,
title:third post,
voteCount:4
}

我正在使用Ember来使用我的API,解析器将其转换为camelCase。我也试过订购= voteCount,但这不行(不应该)

解决方案

这是不可能使用默认的 OrderingFilter ,因为在数据库端执行排序。这是出于效率原因,因为手动排序结果可能会令人难以置信的慢,意味着从标准的 QuerySet 中删除​​。通过将所有内容保持为 QuerySet ,您可以从Django REST框架(通常期望 QuerySet )和内置分页(没有一个可能很慢)。



现在,在这些情况下,您有两个选项:找出如何检索您的值在数据库端,或尝试最小化您将要采取的性能打击。由于后一个选项是非常实现特定的,所以我现在要跳过它。



在这种情况下,您可以使用 计数功能来做数据库端。这是作为聚合API 的一部分提供的,并且像 SQL COUNT 功能。您可以通过修改视图中的 queryset 来执行等价的计数调用

  queryset = Topic.objects.annotate(vote_count = Count('topicvote_set'))

topicvote_set /fields/#django.db.models.ForeignKey.related_name\">您的 related_name 为该字段(您有一组,对吗?)。这将允许您根据投票数量排序结果,甚至进行筛选(如果您想),因为它可以在查询本身中使用。



将需要对您的序列化程序进行轻微更改,因此它可以从对象上可用的新的 vote_count 属性中获取。

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

class Meta:
model =主题

这将覆盖您现有的 vote_count 方法,您可能需要重新命名注释时使用的变量(如果您无法替换旧方法)。






此外,您可以将方法名称作为Django REST框架字段的源代码传递,它将自动调用它。所以技术上你现在的序列化器只能是

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

class Meta:
model =主题

它将像现在一样正常工作。请注意,在这种情况下需要 read_only ,因为方法与属性不同,因此无法设置该值。


I have a Forum Topic model that I want to order on a computed SerializerMethodField, such as vote_count. Here are a very simplified Model, Serializer and ViewSet to show the issue:

# 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

Here is what works:

  1. OrderingFilter is on by default and I can successfully order /topics?ordering=title
  2. The vote_count function works perfectly

I'm trying to order by the MethodField on the TopicSerializer, vote_count like /topics?ordering=-vote_count but it seems that is not supported. Is there any way I can order by that field?

My simplified JSON response looks like this:

{
    "id": 1,
    "title": "first post",
    "voteCount": 1
},
{ 
    "id": 2,
    "title": "second post",
    "voteCount": 8
},
{ 
    "id": 3,
    "title": "third post",
    "voteCount": 4
}

I'm using Ember to consume my API and the parser is turning it to camelCase. I've tried ordering=voteCount as well, but that doesn't work (and it shouldn't)

解决方案

This is not possible using the default OrderingFilter, because the ordering is implemented on the database side. This is for efficiency reasons, as manually sorting the results can be incredibly slow and means breaking from a standard QuerySet. By keeping everything as a QuerySet, you benefit from the built-in filtering provided by Django REST framework (which generally expects a QuerySet) and the built-in pagination (which can be slow without one).

Now, you have two options in these cases: figure out how to retrieve your value on the database side, or try to minimize the performance hit you are going to have to take. Since the latter option is very implementation-specific, I'm going to skip it for now.

In this case, you can use the Count function provided by Django to do the count on the database side. This is provided as part of the aggregation API and works like the SQL COUNT function. You can do the equivalent Count call by modifying your queryset on the view to be

queryset = Topic.objects.annotate(vote_count=Count('topicvote_set'))

Replacing topicvote_set with your related_name for the field (you have one set, right?). This will allow you to order the results based on the number of votes, and even do filtering (if you want to) because it is available within the query itself.

This would require making a slight change to your serializer, so it pulls from the new vote_count property available on objects.

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

    class Meta:
        model = Topic

This will override your existing vote_count method, so you may want to rename the variable used when annotating (if you can't replace the old method).


Also, you can pass a method name as the source of a Django REST framework field and it will automatically call it. So technically your current serializer could just be

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

    class Meta:
        model = Topic

And it would work exactly like it currently does. Note that read_only is required in this case because a method is not the same as a property, so the value cannot be set.

这篇关于Django Rest框架在SerializerMethodField上排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆