Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Abstract Model в Django
Abstract Model — это специальный класс модели в Django, который не создаёт собственную таблицу в базе данных, но предоставляет функциональность и поля для наследуемых моделей. Это инструмент для переиспользования кода и DRY принципа.
Основная идея
# ❌ БЕЗ абстрактной модели — код дублируется
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
created_at = models.DateTimeField(auto_now_add=True) # Дублируется
updated_at = models.DateTimeField(auto_now=True) # Дублируется
is_active = models.BooleanField(default=True) # Дублируется
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True) # Дублируется
updated_at = models.DateTimeField(auto_now=True) # Дублируется
is_active = models.BooleanField(default=True) # Дублируется
class Comment(models.Model):
text = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True) # Дублируется
updated_at = models.DateTimeField(auto_now=True) # Дублируется
is_active = models.BooleanField(default=True) # Дублируется
✅ С абстрактной моделью — код переиспользуется
class TimeStampedModel(models.Model):
"""Абстрактная модель с временными метками"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # КЛЮЧЕВОЕ СЛОВО: это абстрактная модель
class SoftDeleteModel(models.Model):
"""Абстрактная модель с soft delete"""
is_active = models.BooleanField(default=True)
deleted_at = models.DateTimeField(null=True, blank=True)
class Meta:
abstract = True
def delete(self):
"""Soft delete вместо удаления"""
self.is_active = False
self.deleted_at = timezone.now()
self.save()
# Наследование
class User(TimeStampedModel, SoftDeleteModel):
"""User наследует поля от абстрактных моделей"""
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
class Meta:
db_table = 'users'
class Product(TimeStampedModel, SoftDeleteModel):
"""Product получает created_at, updated_at, is_active"""
name = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
db_table = 'products'
class Comment(TimeStampedModel, SoftDeleteModel):
"""Comment тоже наследует базовые поля"""
text = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
class Meta:
db_table = 'comments'
Что происходит в БД
-- Абстрактная модель TimeStampedModel НЕ создаёт таблицу
-- Вместо этого её поля добавляются в таблицы наследников
-- CREATE TABLE users (не создаётся TimeStampedModel_table)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(254) NOT NULL,
created_at TIMESTAMP NOT NULL, -- От TimeStampedModel
updated_at TIMESTAMP NOT NULL, -- От TimeStampedModel
is_active BOOLEAN NOT NULL, -- От SoftDeleteModel
deleted_at TIMESTAMP, -- От SoftDeleteModel
);
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT NOT NULL,
price NUMERIC(10, 2),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
is_active BOOLEAN NOT NULL,
deleted_at TIMESTAMP,
);
Отличие от Multi-table Inheritance
Abstract Model — поля копируются в каждую таблицу:
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True # БЕЗ этого было бы multi-table inheritance
class User(BaseModel):
name = models.CharField(max_length=100)
# БД:
# users (id, name, created_at) — поле created_at скопировано сюда
Multi-table Inheritance — отдельные таблицы с связями:
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
# НЕ abstract — создаёт свою таблицу
class User(BaseModel):
name = models.CharField(max_length=100)
# БД:
# basemodel (id, created_at)
# user (id, name, basemodel_ptr_id) — связь с basemodel
# Query User → автоматически делает JOIN с basemodel
Практические примеры
1. UUID Primary Key для всех моделей
import uuid
class UUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Meta:
abstract = True
class User(UUIDModel):
name = models.CharField(max_length=100)
# Получит UUID поле автоматически
class Post(UUIDModel):
title = models.CharField(max_length=200)
# Тоже UUID вместо стандартного Integer
2. Audit Log для всех моделей
from django.contrib.auth.models import User as DjangoUser
class AuditedModel(models.Model):
created_by = models.ForeignKey(DjangoUser, null=True, on_delete=models.SET_NULL, related_name='%(class)s_created')
updated_by = models.ForeignKey(DjangoUser, null=True, on_delete=models.SET_NULL, related_name='%(class)s_updated')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
def save(self, user=None, **kwargs):
if user:
self.updated_by = user
super().save(**kwargs)
class Article(AuditedModel):
title = models.CharField(max_length=200)
content = models.TextField()
# Использование
article = Article.objects.create(
title="Django Tips",
content="...",
created_by=current_user
)
3. Timestamps с timezone
from django.utils import timezone
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
# Индексы для быстрого поиска
indexes = [
models.Index(fields=['created_at']),
models.Index(fields=['updated_at']),
]
@classmethod
def get_recent(cls, days=7):
"""Получить всё создано за последние N дней"""
cutoff = timezone.now() - timezone.timedelta(days=days)
return cls.objects.filter(created_at__gte=cutoff)
4. Soft Delete паттерн
class SoftDeleteManager(models.Manager):
"""Manager который фильтрует удалённые объекты"""
def get_queryset(self):
return super().get_queryset().filter(deleted_at__isnull=True)
class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(null=True, blank=True)
objects = SoftDeleteManager()
objects_all = models.Manager() # Если нужны все, включая удалённые
class Meta:
abstract = True
def delete(self, hard=False):
if hard:
super().delete()
else:
self.deleted_at = timezone.now()
self.save()
def restore(self):
self.deleted_at = None
self.save()
class User(SoftDeleteModel):
name = models.CharField(max_length=100)
# Использование
user = User.objects.first() # Только активные
all_users = User.objects_all.all() # Включая удалённые
user.delete() # Soft delete
user.restore() # Восстановление
Best Practices
✅ ДА:
- Используйте для общих полей (created_at, updated_at)
- Используйте для общей функциональности (методы, свойства)
- Делайте осмысленные иерархии
- Документируйте зачем нужна абстрактная модель
❌ НЕТ:
- Не переусложняйте наследование
- Не смешивайте abstract и multi-table inheritance
- Не создавайте ForeignKey на абстрактную модель (обычно ошибка)
- Не наследуйте > 3 уровней (сложность растёт)
Миграции
# Когда добавляете поле в абстрактную модель:
python manage.py makemigrations
python manage.py migrate
# Django создаст миграции для ВСЕХ моделей, наследующих абстрактную
# Это гарантирует синхронизацию БД
Итоги
Abstract Model — это мощный инструмент для:
- DRY принцип — не дублируйте код
- Консистентность — все модели имеют одинаковые поля
- Масштабируемость — добавить поле один раз, применится везде
- Функциональность — общие методы и свойства
Без абстрактных моделей Django проекты быстро становятся беспорядочными и сложными для поддержки.