2015-05-04 4 views
9

У меня есть модель темы форума, которую я хочу заказать на вычисленном SerializerMethodField, например vote_count. Вот очень упрощенная модель, Serializer и Viewset показать проблему:Django Rest Framework Ordering на SerializerMethodField

# 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 

Вот что работает:

  1. OrderingFilter включен по умолчанию, и я могу успешно заказать /topics?ordering=title
  2. Функция vote_count отлично работает

Я пытаюсь заказать методом MethodField на TopicSerializer, vote_count как /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 
} 

Я использую Ember потреблять мой API и анализатор превращает его в верблюжий. Я попытался упорядочения = voteCount, а также, но это не работает (и не должны)

ответ

18

Это не возможно с помощью the default OrderingFilter, поскольку упорядочение осуществляется на стороне базы данных. Это по соображениям эффективности, так как вручную сортировка результатов может быть невероятно slow и означает нарушение от стандарта QuerySet. Сохраняя все как QuerySet, вы получаете встроенную фильтрацию, предоставляемую инфраструктурой Django REST (которая обычно ожидает QuerySet) и встроенную разбивку на страницы (которая может быть медленной без одного).

Теперь у вас есть два варианта в этих случаях: выяснить, как получить свое значение на стороне базы данных, или попытаться свести к минимуму производительность, которую вы должны будете предпринять. Поскольку последний вариант очень специфичен для реализации, я сейчас пропущу его.

В этом случае вы можете использовать the Count function, предоставленный Django, чтобы сделать счет на стороне базы данных. Это предусмотрено как часть the aggregation API и работает как the SQL COUNT function. Вы можете сделать эквивалентный Count вызов путем изменения queryset представления, чтобы быть

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

Замена topicvote_set с your related_name for the field (у вас есть один комплект, не так ли?). Это позволит вам заказывать результаты на основе количества голосов и даже фильтровать (если хотите), потому что он доступен в самом запросе.

Это потребует внесения незначительных изменений в ваш сериализатор, поэтому он извлекает из нового vote_count имущества, доступного на объектах.

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

    class Meta: 
     model = Topic 

Это отменит существующий vote_count метод, так что вы можете переименовать переменную, используемую при аннотирования (если вы не можете заменить старый метод).


Кроме того, вы можете передать имя метода в качестве source рамочного поля Django REST, и он будет автоматически вызывать его.Технически текущая сериализатору может быть просто

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

    class Meta: 
     model = Topic 

И это будет работать точно как он в данный момент делает. Обратите внимание, что в этом случае требуется read_only, потому что метод не совпадает с свойством, поэтому значение не может быть установлено.

+0

отличный ответ, дал бы ему 2 голоса, если бы мог! Отлично работает и благодарит за последний отзыв о том, как избавиться от MethodField. – awwester