JavaRush /Курси /Модуль 3: Django /Практичне заняття з агрегацій та анотацій

Практичне заняття з агрегацій та анотацій

Модуль 3: Django
Рівень 10 , Лекція 9
Відкрита

Отже, ми продовжуємо працювати над нашим спільним проєктом — веб-додатком для управління бібліотекою. На цьому етапі у нас є кілька моделей: Book, Author та Publisher. Тепер ми будемо використовувати агрегації, анотації, Q- та F-об'єкти для виконання різних аналітичних запитів.

Ось наша поточна модель бази даних:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    birth_date = models.DateField(null=True, blank=True)

    def __str__(self):
        return self.name

class Publisher(models.Model):
    name = models.CharField(max_length=100)
    country = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name='books')
    price = models.DecimalField(max_digits=10, decimal_places=2)
    publication_date = models.DateField()

    def __str__(self):
        return self.title

Задача 1: загальна вартість книг

Давай почнемо з простого: нам потрібно дізнатися, яка загальна вартість всіх книг у нашій бібліотеці. Звісно ж, ми будемо використовувати функцію aggregate().

from django.db.models import Sum

total_price = Book.objects.aggregate(total=Sum('price'))
print(total_price)

Що тут відбувається?

  • aggregate(Sum('price')): повертає словник, де ключем буде ім'я, яке ми вказали для результату (total), а значенням — загальна сума поля price у всіх записах у таблиці Book.

Вивід буде виглядати так:

{'total': Decimal('10753.50')}

--

Задача 2: середня ціна книг для кожного видавця

Тепер нам потрібно обчислити середню вартість книг, випущених кожним видавцем. Тут нам допоможе функція annotate().

from django.db.models import Avg

publishers = Publisher.objects.annotate(avg_price=Avg('books__price'))
for publisher in publishers:
    print(f"{publisher.name}: {publisher.avg_price}")

Розбір:

  • annotate(avg_price=Avg('books__price')): додає обчислюване поле avg_price до кожного об'єкта Publisher. Це поле містить середню ціну всіх книг, пов'язаних із цим видавцем.

Приклад виводу:

O'Reilly Media: 25.50
Penguin Books: 18.75
Random House: 20.00

Задача 3: кількість книг, випущених після 2020 року

Тепер ми використовуємо фільтрацію та анотацію, щоб підрахувати кількість книг, випущених кожним видавцем після 2020 року.

from django.db.models import Count

publishers = Publisher.objects.annotate(
    books_after_2020=Count('books', filter=models.Q(books__publication_date__year__gt=2020))
)

for publisher in publishers:
    print(f"{publisher.name}: {publisher.books_after_2020} книг після 2020 року")

Що нового?

  • Новий параметр filter у функції анотації дозволяє застосувати фільтрацію тільки для анотованих даних.
  • Q(books__publication_date__year__gt=2020): фільтрує книги з роком публікації більше 2020.

Задача 4: знайти автора з максимальною кількістю книг

Як щодо того, щоб дізнатися, хто з авторів написав найбільше книг? Для цього підійде функція annotate з Count.

author = Author.objects.annotate(book_count=Count('books')).order_by('-book_count').first()
print(f"Найпродуктивніший автор: {author.name}, {author.book_count} книг.")
  • Count('books'): рахує кількість пов'язаних книг для кожного автора.
  • .order_by('-book_count'): сортує авторів за кількістю книг у порядку спадання.
  • .first(): повертає першого автора зі списку, що відсортований.

Задача 5: порівняння цін на книги одного видавця

Тепер складніше: як порівняти ціну певної книги із середньою ціною інших книг того ж видавця? Для цього згодяться F-об'єкти.

from django.db.models import F

books = Book.objects.annotate(
    publisher_avg_price=Avg('publisher__books__price')
).filter(price__lt=F('publisher_avg_price'))

for book in books:
    print(f"{book.title} дешевша за середню ціну у видавця {book.publisher.name}.")

Новий елемент:

  • F('publisher_avg_price'): дозволяє працювати зі значенням обчислюваного поля прямо в запиті.

Задача 6: складний запит з Q-об'єктами

Давай знайдемо всіх авторів, у яких є хоча б одна книга дешевша за 10 доларів або вартість книги перевищує 100 доларів у видавця зі США.

q_authors = Author.objects.filter(
    Q(books__price__lt=10) | Q(books__price__gt=100, books__publisher__country='USA')
).distinct()

for author in q_authors:
    print(f"Автор: {author.name}")

Розбір:

  • Q(): дозволяє об'єднувати умови за допомогою логічного АБО.
  • .distinct(): видаляє дублікати записів, якщо автор підходить під декілька умов одразу.

Задача 7: кількість книг по року

Для додаткового челенджу давай підрахуємо кількість книг, опублікованих у кожен рік.

from django.db.models.functions import TruncYear

yearly_books = Book.objects.annotate(year=TruncYear('publication_date')).values('year').annotate(count=Count('id')).order_by('year')

for entry in yearly_books:
    print(f"Рік: {entry['year']}, Кількість книг: {entry['count']}")

Пояснення:

  • TruncYear('publication_date'): дозволяє витягнути рік із дати публікації.
  • .values('year'): групує результати по року.
  • .annotate(count=Count('id')): рахує кількість книг для кожного року.

Підсумок

Ось і все! Ми використали агрегації, анотації, Q- та F-об'єкти для вирішення реальних задач, що стосуються нашого проєкту. Усі приклади, які ми розглянули, можуть бути легко адаптовані для вирішення твоїх власних задач у веброзробці.

Якщо ти відчуваєш себе трохи перевантаженим, це нормально. Повернися до цієї лекції за потреби та поступово експериментуй із різними підходами. Пам'ятай, як казав один мудрий розробник: "Найкращий спосіб дізнатися щось нове — це закомітити це в продакшн... Але краще все ж таки в локальній копії!" 😊

3
Опитування
Фільтрація даних з анотаціями, рівень 10, лекція 9
Недоступний
Фільтрація даних з анотаціями
Фільтрація даних з анотаціями
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ