Я использую django 1.11:
Модель:
class Model(models.Model):
.
.
.
def count_total(self):
return self.anothermodel_set.filter(val=False).count()
Вид:
class ModelViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializerClass
permissions = AuthenticatedReadOnly
pagination_class = StandardResultsSetPagination
def list(self, request):
queryset = self.get_queryset()
# Other annotations...
# Attempt 1: returns wrong count
queryset = queryset.annotate(
a_count=Count(
Case(
When(anothermodel__val=False, then=1),
default=0,
output_field=IntegerField()
)
)
)
# Attempt 2: returns wrong count, same as attempt 1
queryset = queryset.annotate(
b_count=Count(Q(anothermodel__val=False))
)
# Ideally I want to do
queryset = queryset.order_by('count_total')
Когда сделаю заказ по count_total
, я дам
FieldError at /api/endpoint/ Cannot resolve keyword 'count_total' into field.
Потому что count_total
- это определение модели.
Сериализатор:
В моем сериализаторе я изменил определение to_represantation
для отладки:
def to_representation(self, instance):
return {'id': instance.pk, 'a_count': instance.a_count, 'b_count' : instance.b_count, 'correct_count': instance.count_total()}
В противном случае в моем сериализаторе у меня есть:
class Meta:
model = Model
fields = ('id', 'title', 'bunch-of-other-stuff', 'count_total')
instance.count_total()
возвращает правильный результат, но я не могу просто использовать его как queryset.order_by('count_total')
. Мне нужно аннотировать правильное значение, чтобы отсортировать результаты, а также избежать проблем с запросом n + 1.
Я не уверен, ваша проблема в этом или нет, но определение модели кажется неиспользованным, потому что вы не возвращаете значение, а просто выполняете расчет. Должен быть:
def count_total(self):
return self.anothermodel_set.filter(val=False).count()
Я также не понимаю, почему у вас могут быть вычисления как в модели, так и в вашем представлении. Либо используйте определение модели, либо аннотируйте свой набор запросов, но вам не нужно делать и то, и другое. Если вы пойдете по маршруту определения модели, ваше представление должно выглядеть примерно так:
class ModelViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permissions = AuthenticatedReadOnly
pagination_class = StandardResultsSetPagination
def list(self, request):
queryset = self.queryset.order_by('count_total')
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Если вы используете определение модели, вам нужно установить в сериализаторе что-то вроде атрибута read_only:
count_total = serializers.ReadOnlyField(allow_null=True)
class Meta:
model = Model
fields = ('id', 'title', 'bunch-of-other-stuff', 'count_total')
Если вы по-прежнему получаете сообщение об ошибке «Невозможно разрешить общее количество в поле», перед запуском сериализатора введите оператор печати в набор представлений, например:
print(str(queryset))
Проблема может заключаться в том, как вы фильтруете определение модели.
Да, это именно то, что я имел в виду. У вас есть это на вашем ModelSerializerClass?
Я только что отредактировал свой ответ, включив в него правильный код сериализатора. В вставленном вами коде отсутствовало поле только для чтения. Какую ошибку вы получаете?
FieldError at /api/endpoint/ Cannot resolve keyword 'count_total' into field.
Сообщите мне, помогут ли приведенные выше изменения. Если они не обязательно взглянут на набор запросов, как показано в конце моего ответа.
Позвольте нам продолжить обсуждение в чате.
Почти 3 года спустя; но это может сэкономить время для тех, кто столкнется с этим.
# Attempt 2: returns wrong count, same as attempt 1
queryset = queryset.annotate(b_count=Count(Q(anothermodel__val=False)))
Проблема связана с тем, что несколько аннотаций были объединены. Недостающая часть из приведенного выше заключалась в том, чтобы указать, что мы хотим только подсчитывать значения distinct=True
, чтобы избежать Эта проблема. Вот так:
# Success
queryset = queryset.annotate(
b_count=Count(
Q(anothermodel__val=False), distinct=True
)
)
Вы имеете в виду что-то вроде
count_total = serializers.ReadOnlyField()
?