from django.db import models
from django.utils import timezone
class PostQuerySet(models.QuerySet):
def published(self):
return self.filter(
status='published',
published_at__lte=timezone.now()
)
def by_author(self, author):
return self.filter(author=author)
class PostManager(models.Manager):
def get_queryset(self):
return PostQuerySet(self.model, using=self._db)
def published(self):
return self.get_queryset().published()
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
status = models.CharField(
max_length=20,
choices=[('draft', 'Draft'), ('published', 'Published')]
)
published_at = models.DateTimeField(null=True, blank=True)
objects = PostManager()
def __str__(self):
return self.title
Custom managers encapsulate common query patterns. I create a manager method for each frequently-used filter like published() or active(). By returning self, I can chain manager methods. For more complex queries, I create a custom QuerySet class and use Manager.from_queryset() to get both manager and queryset methods. This keeps view code clean and makes query logic reusable and testable. I avoid putting business logic in managers—they're for data access only. Model methods are better for behavior.