Как правильно сделать этот сложный Django queryset?

Допустим, у меня есть эти три модели:

class Author(models.Model):
   name = models.CharField(max_length=64)


class Book(models.Model):
   author = models.ForeignKey(
        Author,
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )
   name = models.CharField(max_length=64)


class Store(models.Model):
   books = models.ManyToManyField(Book)
   name = models.CharField(max_length=64)

Я не знаю автора некоторых книг. В этих случаях автор равен нулю.

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

Store.objects.all().annotate(books_count=Count('books')).order_by('-books_count')

Теперь, что если я хочу подсчитать только те книги, у которых есть автор?

Я попробовал этот набор вопросов, но он явно не корректен:

filter = Q(books__author__isnull=False)
Store.objects.annotate(books_count=Count(filter)).all().order_by('-books_count')

Кто-нибудь знает правильный способ выполнения этого запроса?

Вы можете пойти другим путем:

Book.objects.filter(author__isnull=False).values('store_set').annotate(count=Count('store_set')).order_by('-count')

Я сделаю небольшое предположение, но я думаю, что ваше использование объекта Q может быть неправильным внутри объекта Count.

Count ожидается выражение типа объект, это может быть строка, F, OuterRef, SubQuery и так далее.

Store.objects.filter(filter).annotate(
     books_count=Count("books")
).order_by('-books_count')

Думаю, это то, что вам нужно.

Я считаю, что следующий запрос - это то, что вы ищете.

books_count = (
    Book.objects.filter(
        store=OuterRef('pk'),
        author__isnull=False,
    )
    .annotate(count=Func(F("id"), function="COUNT"))
    .values("count")
)
Store.objects.annotate(count=Subquery(books_count)).order_by('-count')
Вернуться на верх