← Назад к вопросам
Как получить количество записей с помощью метода QuerySet?
1.0 Junior🔥 81 комментариев
#Soft Skills
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Получение количества записей в Django ORM
Для подсчёта записей в Django используется метод count() QuerySet. Это один из самых частых операций в веб-приложениях.
1. Базовый метод count()
Самый простой и рекомендуемый способ:
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
created_at = models.DateTimeField(auto_now_add=True)
# Подсчитать ВСЕ пользователей
total_users = User.objects.count()
print(total_users) # 1523
# Подсчитать с фильтром
active_users = User.objects.filter(is_active=True).count()
print(active_users) # 892
# Подсчитать за определённый период
from datetime import timedelta
from django.utils import timezone
recent_users = User.objects.filter(
created_at__gte=timezone.now() - timedelta(days=7)
).count()
print(recent_users) # 45
SQL, который выполняется:
SELECT COUNT(*) FROM user; -- или COUNT(*) WHERE is_active = 1
2. count() vs len() — когда использовать
count() — для больших наборов данных
# ХОРОШО: count() выполняет SQL COUNT(*)
# Быстро даже на миллионах записей
count = User.objects.all().count() # 1 запрос: SELECT COUNT(*)
# ПЛОХО: len() загружает ВСЕ записи в память
# Медленно на больших наборах!
count = len(User.objects.all()) # N запросов, загружает всё в памяти
len() — когда QuerySet уже загружен
# Если QuerySet уже в памяти, len() быстрее
users = User.objects.filter(is_active=True)[:10]
# len() не выполняет новый запрос (список уже есть)
print(len(users)) # Быстро
# count() выполнит новый COUNT запрос
print(users.count()) # Медленнее (дополнительный SQL запрос)
Правило: используй count() для подсчёта, len() для размера уже загруженного списка.
3. Подсчёт с аннотациями (Count)
Для более сложных сценариев используй Count() с annotate():
from django.db.models import Count
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
# Подсчитать количество книг для каждого автора
authors = Author.objects.annotate(
book_count=Count("book")
).order_by("-book_count")
for author in authors:
print(f"{author.name}: {author.book_count} books")
# SQL: SELECT author.*, COUNT(book) AS book_count FROM author GROUP BY author.id
# Подсчитать только авторов с более чем 5 книг
productive_authors = Author.objects.annotate(
book_count=Count("book")
).filter(book_count__gt=5)
4. Count с distinct (подсчёт уникальных)
from django.db.models import Count, Q
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
# Подсчитать, сколько уникальных авторов прокомментировали каждый пост
posts = Post.objects.annotate(
unique_commenters=Count("comment__author", distinct=True)
)
for post in posts:
print(f"Post: {post.title}, unique commenters: {post.unique_commenters}")
5. Условный подсчёт (Case/When)
from django.db.models import Count, Case, When, IntegerField, Q
# Подсчитать рейтинги отдельно
reviews = Product.objects.annotate(
five_star_count=Count(
Case(When(rating=5, then=1)),
output_field=IntegerField()
),
four_star_count=Count(
Case(When(rating=4, then=1)),
output_field=IntegerField()
),
one_star_count=Count(
Case(When(rating=1, then=1)),
output_field=IntegerField()
)
)
for product in reviews:
print(
f"{product.name}: "
f"5⭐={product.five_star_count}, "
f"4⭐={product.four_star_count}, "
f"1⭐={product.one_star_count}"
)
6. exists() — проверка наличия без подсчёта
Если нужно просто проверить, есть ли записи:
# ПЛОХО: count() подсчитывает ВСЕ записи
if User.objects.filter(email="test@example.com").count() > 0:
print("User exists")
# ХОРОШО: exists() прекращает поиск при первой найденной записи
if User.objects.filter(email="test@example.com").exists():
print("User exists") # Намного быстрее!
SQL:
-- count() (медленнее)
SELECT COUNT(*) FROM user WHERE email = "test@example.com";
-- exists() (быстрее)
SELECT 1 FROM user WHERE email = "test@example.com" LIMIT 1;
7. Производительность: примеры запросов
# Сценарий 1: Подсчитать активных пользователей
# БЫСТРО (COUNT SQL)
active_count = User.objects.filter(is_active=True).count()
# МЕДЛЕННО (загружает все в памяти)
active_users = list(User.objects.filter(is_active=True))
active_count = len(active_users)
# Сценарий 2: Проверить, есть ли администраторы
# БЫСТРО
has_admin = User.objects.filter(is_superuser=True).exists()
# МЕДЛЕННО
has_admin = User.objects.filter(is_superuser=True).count() > 0
# Сценарий 3: Статистика по авторам
# БЫСТРО (одна GROUP BY запрос)
stats = Author.objects.annotate(
total_books=Count("book"),
total_reviews=Count("book__review")
)
# МЕДЛЕННО (N+1 проблема)
for author in Author.objects.all():
total_books = author.book_set.count() # Отдельный запрос!
total_reviews = author.book_set.aggregate(
Count("review")
) # Ещё запрос!
8. Подсчёт с VALUES и GROUP BY
from django.db.models import Count
# Подсчитать комментарии по статусу
status_counts = Comment.objects.values("status").annotate(
count=Count("id")
)
for item in status_counts:
print(f"{item['status']}: {item['count']} comments")
# pending: 15
# approved: 42
# rejected: 3
# SQL: SELECT status, COUNT(id) FROM comment GROUP BY status;
9. Подсчёт с multiple joins
from django.db.models import Count, F
# Сложный пример: подсчитать, сколько активных комментариев
# имеет каждый пост активных авторов
posts = Post.objects.filter(
author__is_active=True
).annotate(
active_comments=Count(
Case(
When(comment__author__is_active=True, then=1),
output_field=IntegerField()
),
distinct=True
)
).order_by("-active_comments")
10. Практический пример: пагинация с count
from django.paginator import Paginator
def get_paginated_users(page_number: int, per_page: int = 10):
queryset = User.objects.filter(is_active=True).order_by("-created_at")
# count() выполняется один раз
total = queryset.count()
# Paginator использует это значение
paginator = Paginator(queryset, per_page)
page_obj = paginator.get_page(page_number)
return {
"users": page_obj.object_list,
"total": total,
"page": page_number,
"pages": paginator.num_pages
}
11. Оптимизация больших count() запросов
# Для таблиц с миллионами записей COUNT может быть медленным
# Решение 1: Кэширование
from django.views.decorators.cache import cache_page
from django.core.cache import cache
def get_user_count():
cache_key = "user_count"
count = cache.get(cache_key)
if count is None:
count = User.objects.count() # Медленно, но редко
cache.set(cache_key, count, 3600) # Кэш на час
return count
# Решение 2: Денормализация в отдельной таблице
class Statistics(models.Model):
metric = models.CharField(max_length=50)
value = models.IntegerField()
updated_at = models.DateTimeField(auto_now=True)
# Обновлять эту таблицу в background task, не на каждый запрос
# Решение 3: Приблизительный count через pg_stat_user_tables (PostgreSQL)
from django.db import connection
def estimate_user_count():
with connection.cursor() as cursor:
cursor.execute(
"SELECT n_live_tup FROM pg_stat_user_tables WHERE relname = %s",
["user"]
)
return cursor.fetchone()[0] # Приблизительное значение
Сравнительная таблица
| Метод | Использование | SQL запрос | Скорость |
|---|---|---|---|
| count() | Подсчёт записей | SELECT COUNT(*) | Быстро |
| len() | Размер уже загруженного списка | Нет (памяти) | Зависит |
| exists() | Проверка наличия | SELECT 1 LIMIT 1 | Очень быстро |
| annotate(Count) | Подсчёт с GROUP BY | SELECT COUNT(*) GROUP BY | Быстро |
| values().annotate() | Статистика | SELECT * GROUP BY | Быстро |
Best practices
- Используй count() для подсчёта: это SQL функция, очень быстра
- Используй exists() для проверки наличия: быстрее, чем count() > 0
- Используй annotate(Count()) для группировки: одна запрос вместо N
- Кэшируй count() на больших таблицах: SQL COUNT медленнее на миллионах
- Избегай len() на больших QuerySet: загружает всё в памяти
Итог
- count() — стандартный способ для подсчёта, генерирует
SELECT COUNT(*) - exists() — для проверки наличия, самый быстрый способ
- annotate(Count()) — для сложной статистики и группировки
- len() — только для уже загруженных списков в памяти