Django Queryset с аннотацией - PullRequest
       19

Django Queryset с аннотацией

0 голосов
/ 28 января 2020

Я пишу один метод в Django Модель менеджера. Я хочу написать метод, который определяет количество проданных копий (книг) на автора.

У меня есть две модели и метод, написанный на Менеджер . Моя проблема в том, что метод также должен быть цепным из любого набора запросов Author, например, что-то вроде

Author.objects.filter(...).exlucde(...).total_copies_sold()

также должно работать.

Пример:

author = Author.objects.create(...)
Book.objects.create(..., author=author, copies_sold=10)
Book.objects.create(..., author=author, copies_sold=20)

author_total_books = Author.objects.total_copies_sold().first()
>>> author_total_books.copies
30

Ниже моего код. Это работает как в примере выше, но затем я пытаюсь что-то вроде:

author_books = Author.objects.filter(id=2).total_copies_sold()

Я получил

У объекта 'QuerySet' нет атрибута 'annotate'

class AuthorManager(models.Manager):

    def total_copies_sold(self):
        return self.get_queryset().annotate(copies=Sum('book__copies_sold')



class Author(models.Model):
    first_name = models.CharField(max_length=120)
    last_name = models.CharField(max_length=120)

    objects = AuthorManager()


class Book(models.Model):
    title = models.CharField(max_length=120)
    copies_sold = models.PositiveIntegerField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')

[Отредактировано]

Спасибо, Шиллингт, за ответ. Я добавил:

class AuthorQueryset(models.QuerySet):
    def total_copies_sold(self):
        return self.annotate(copies=Sum('books__copies_sold'))

Я попробовал что-то вроде:

author_books = Author.objects.filter(id=2).total_copies_sold()

>>> author_books.copies

Я получил

Объект AuthorQueryset не имеет атрибута "копии"

Ответы [ 2 ]

1 голос
/ 29 января 2020

То, что вы ищете:

from django.db import models
from django.db.models import Sum
from django.db.models.functions import Coalesce


class AuthorManager(models.Manager):
    def get_queryset(self):
        return AuthorQuerySet(self.model, using=self._db)

    def annotate_with_copies_sold(self):
        return self.get_queryset().annotate_with_copies_sold()


class AuthorQuerySet(models.QuerySet):
    def annotate_with_copies_sold(self):
        return self.annotate(copies_sold=Sum('books__copies_sold'))


class Author(models.Model):
    objects = AuthorManager()
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)


class Book(models.Model):
    title = models.CharField(max_length=30)
    copies_sold = models.PositiveIntegerField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')

Теперь можно объединять запросы, например:

author_total_books = Author.objects.total_copies_sold().first()

Однако вы не сможете использовать его в объекте QuerySet, например :

author_books = Author.objects.filter(id=2).total_copies_sold()

Это потому, что вы аннотируете объект Author, а не QuerySet. Чтобы получить этот результат, вы должны выполнить:

Author.objects.annotate_with_copies_sold().get(id=2)
author.copies_sold 
15
1 голос
/ 28 января 2020

Вам нужно использовать Manager.from_queryset, чтобы настроить своего менеджера. Вот документы .

class AuthorQueryset(models.QuerySet):
    def total_copies_sold(self):
        ...

class Author(models.Model):
    objects = models.Manager.from_queryset(AuthorQueryset)()
...