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

Зачем нужен Manager в Django?

2.0 Middle🔥 231 комментариев
#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зачем нужен Manager в Django?

Manager — это интерфейс для выполнения операций поиска и фильтрации в БД на уровне модели. Это один из самых важных и мощных инструментов в Django ORM. Manager предоставляет методы для выполнения запросов без необходимости писать SQL вручную.

Основная идея

Вместо прямого создания SQL запросов, Django Manager позволяет писать выразительный, безопасный код на Python:

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    
    # Django автоматически создаёт Manager по умолчанию
    # objects это и есть Manager (objects.all(), objects.filter() и т.д.)
    objects = models.Manager()

# Использование Manager
articles = Article.objects.all()  # SELECT * FROM articles
published = Article.objects.filter(published=True)  # SELECT * FROM articles WHERE published = true
recent = Article.objects.filter(published=True).order_by('-created_at')  # с сортировкой

Основные методы Manager

from django.db import models
from django.utils import timezone

class Article(models.Model):
    title = models.CharField(max_length=100)
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

# 1. all() - получить все объекты
articles = Article.objects.all()  # QuerySet со всеми статьями

# 2. filter() - фильтр с условиями
published_articles = Article.objects.filter(published=True)
recent = Article.objects.filter(created_at__gte=timezone.now() - timezone.timedelta(days=7))

# 3. exclude() - исключить объекты
unpublished = Article.objects.exclude(published=True)

# 4. get() - получить один объект (или исключение)
try:
    article = Article.objects.get(id=1)
except Article.DoesNotExist:
    print("Статья не найдена")

# 5. first() / last() - первый и последний
first_article = Article.objects.first()
last_article = Article.objects.order_by('-id').first()

# 6. count() - количество объектов
total = Article.objects.count()
published_count = Article.objects.filter(published=True).count()

# 7. exists() - проверить наличие
has_published = Article.objects.filter(published=True).exists()

# 8. values() - получить словари вместо объектов
data = Article.objects.values('id', 'title')  # [{'id': 1, 'title': '...'}, ...]

# 9. values_list() - получить кортежи
ids = Article.objects.values_list('id', flat=True)  # [1, 2, 3, ...]

# 10. order_by() - сортировка
by_date = Article.objects.order_by('-created_at')  # новые первыми
by_title = Article.objects.order_by('title')  # по названию

# 11. distinct() - уникальные значения
unique_authors = Article.objects.values('author').distinct()

# 12. reverse() - обратный порядок
reversed_articles = Article.objects.all().reverse()

Цепочки (QuerySet API)

# Manager позволяет цеплять методы
result = Article.objects.filter(published=True) \
    .exclude(title__startswith='Draft') \
    .order_by('-created_at') \
    .values('id', 'title')[:10]  # pagination

# Это очень выразительно и безопасно

Создание собственного Manager (Custom Manager)

Основное применение Manager'а — создание переиспользуемых наборов фильтров:

from django.db import models
from django.utils import timezone

class PublishedManager(models.Manager):
    """Manager для получения только опубликованных статей"""
    
    def get_queryset(self):
        """Переопределяем базовый queryset"""
        return super().get_queryset().filter(published=True)
    
    def recent(self, days=7):
        """Получить недавние статьи"""
        cutoff_date = timezone.now() - timezone.timedelta(days=days)
        return self.filter(created_at__gte=cutoff_date)

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    
    # Заменяем стандартный objects на наш
    objects = PublishedManager()
    all_objects = models.Manager()  # Стандартный Manager для всех

# Использование
published = Article.objects.all()  # Только опубликованные (по умолчанию)
recent_published = Article.objects.recent(days=30)  # Опубликованные за 30 дней
all_articles = Article.all_objects.all()  # Все, включая неопубликованные
unpublished = Article.all_objects.exclude(published=True)  # Неопубликованные

Практический пример: Blog система

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(status='published')

class Article(models.Model):
    STATUS_CHOICES = [
        ('draft', 'Черновик'),
        ('published', 'Опубликована'),
        ('archived', 'Архивирована'),
    ]
    
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    views = models.IntegerField(default=0)
    
    # Managers
    objects = models.Manager()  # Все статьи
    published = PublishedManager()  # Только опубликованные
    
    class Meta:
        ordering = ['-published_at']
    
    def __str__(self):
        return self.title
    
    def publish(self):
        """Опубликовать статью"""
        self.status = 'published'
        self.published_at = timezone.now()
        self.save()

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
    author = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    objects = models.Manager()

# Использование в представлениях (Views)
def article_list(request):
    # Получить только опубликованные статьи
    articles = Article.published.all()
    return render(request, 'articles/list.html', {'articles': articles})

def article_detail(request, slug):
    # Получить одну статью
    article = Article.published.get(slug=slug)
    article.views += 1
    article.save(update_fields=['views'])
    
    # Получить комментарии
    comments = article.comments.all()
    
    return render(request, 'articles/detail.html', {
        'article': article,
        'comments': comments
    })

def draft_articles(request):
    # Получить черновики
    articles = Article.objects.filter(status='draft')
    return render(request, 'articles/drafts.html', {'articles': articles})

Продвинутые методы Manager

from django.db.models import Q, Count, Sum, Avg
from django.db import models

# 1. Q objects для сложных условий
complex_filter = Article.objects.filter(
    Q(title__icontains='python') | Q(content__icontains='python'),
    published=True
)
# SELECT * FROM articles WHERE (title LIKE '%python%' OR content LIKE '%python%') AND published = true

# 2. Агрегирование
stats = Article.objects.aggregate(
    total=Count('id'),
    avg_views=Avg('views'),
    total_views=Sum('views')
)
print(stats)  # {'total': 10, 'avg_views': 45.5, 'total_views': 455}

# 3. Group By (annotate)
from django.db.models import Count

views_by_author = Article.objects.values('author').annotate(
    article_count=Count('id'),
    total_views=Sum('views')
).order_by('-total_views')

for item in views_by_author:
    print(f"Author: {item['author']}, Articles: {item['article_count']}, Views: {item['total_views']}")

# 4. Prefetch related (оптимизация N+1 queries)
articles = Article.objects.prefetch_related('comments').all()
for article in articles:
    # comments уже загружены, нет дополнительных запросов
    print(article.comments.count())

# 5. Select related (JOIN для foreign keys)
articles = Article.objects.select_related('author').all()
for article in articles:
    # author уже загружен
    print(article.author.username)

Почему Manager так важен?

1. Безопасность от SQL-инъекций

# Плохо: SQL-инъекция возможна
query = f"SELECT * FROM articles WHERE id = {user_input}"

# Хорошо: защищено от инъекций
article = Article.objects.get(id=user_input)

2. Переиспользование логики запросов

# Вместо повторения фильтров везде
for view in [list_view, detail_view, api_view]:
    # Article.objects.filter(published=True) везде

# Один раз определяем Manager
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(published=True)

3. Читаемость и выразительность

# Читаемо
articles = Article.published.recent(days=7)

# vs SQL
# SELECT * FROM articles WHERE published=true AND created_at > (NOW() - INTERVAL 7 DAY)

4. Производительность (QuerySet ленивое вычисление)

# Запрос не выполняется здесь
queryset = Article.objects.filter(published=True)

# Запрос выполняется только здесь (при итерации/count/exists)
for article in queryset:
    print(article)

Лучшие практики

✓ Создавайте custom Managers для переиспользуемых фильтров ✓ Используйте метод get_queryset() для переопределения ✓ Используйте prefetch_related() и select_related() для оптимизации ✓ Используйте Q objects для сложных условий ✓ Название Manager'ов должно быть понятным ✗ Не переусложняйте логику в Manager'ах

Manager — это не просто инструмент для выполнения запросов, это архитектурный паттерн, который делает код безопаснее, читабельнее и производительнее.