Отже, ми продовжуємо працювати над нашим спільним проєктом — веб-додатком для управління бібліотекою. На цьому етапі у нас є кілька моделей: 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-об'єкти для вирішення реальних задач, що стосуються нашого проєкту. Усі приклади, які ми розглянули, можуть бути легко адаптовані для вирішення твоїх власних задач у веброзробці.
Якщо ти відчуваєш себе трохи перевантаженим, це нормально. Повернися до цієї лекції за потреби та поступово експериментуй із різними підходами. Пам'ятай, як казав один мудрий розробник: "Найкращий спосіб дізнатися щось нове — це закомітити це в продакшн... Але краще все ж таки в локальній копії!" 😊
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ