← Назад к вопросам

Как получить количество записей с помощью метода 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 BYSELECT COUNT(*) GROUP BYБыстро
values().annotate()СтатистикаSELECT * GROUP BYБыстро

Best practices

  1. Используй count() для подсчёта: это SQL функция, очень быстра
  2. Используй exists() для проверки наличия: быстрее, чем count() > 0
  3. Используй annotate(Count()) для группировки: одна запрос вместо N
  4. Кэшируй count() на больших таблицах: SQL COUNT медленнее на миллионах
  5. Избегай len() на больших QuerySet: загружает всё в памяти

Итог

  • count() — стандартный способ для подсчёта, генерирует SELECT COUNT(*)
  • exists() — для проверки наличия, самый быстрый способ
  • annotate(Count()) — для сложной статистики и группировки
  • len() — только для уже загруженных списков в памяти