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

Что такое абстрактная модель в Django?

2.8 Senior🔥 121 комментариев
#Тестирование

Комментарии (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 — это мощный инструмент для:

  1. DRY принцип — не дублируйте код
  2. Консистентность — все модели имеют одинаковые поля
  3. Масштабируемость — добавить поле один раз, применится везде
  4. Функциональность — общие методы и свойства

Без абстрактных моделей Django проекты быстро становятся беспорядочными и сложными для поддержки.

Что такое абстрактная модель в Django? | PrepBro