Абстрактные модели в Django. Что это и когда их использовать?

При написании моделей для нашего проекта, зачастую приходится писать очень много дублирующихся полей, или полей, крайне схожих друг с другом, которые можно привести к одному виду. Чтобы не писать каждый раз одинаковые поля, и следовать принципу DRY, придумали абстрактные модели (Django Abstract models).

Проблема:

Возьмем простой и понятный пример. Допустим у нас есть модель Post (статьи)  и Category (категории). И у каждой модели должы быть схожие поля created_at (дата создания), is_active (опубликовано или нет), и давайте возьмем еще title.

В обычном случае, мы бы написали наш модели следующим образом:

class Post(models.Model):
    """ Model for storing Post objects """

    class Meta:
        verbose_name = 'Статья'
        verbose_name_plural = 'Статьи'

    title = models.CharField(max_length=120, verbose_name='Заголовок')
    is_active = models.BooleanField(default=True, verbose_name='Опубликовано')
    created_at = models.DateTimeField(
        auto_now=True, verbose_name='Дата создания'
    )
    cover = models.ImageField(upload_to='bla_bla_bla/', verbose_name='Обложка')
    body = models.TextField(verbose_name='Текст статьи')

    def __str__(self):
        return self.title
    
    
class Category(models.Model):
    """ Model for storing Category objects """

    class Meta:
        verbose_name = 'Категория'
        verbose_name_plural = 'Категории'

    title = models.CharField(max_length=120, verbose_name='Заголовок')
    is_active = models.BooleanField(default=True, verbose_name='Опубликовано')
    created_at = models.DateTimeField(
        auto_now=True, verbose_name='Дата создания'
    )

    parent = models.ForeignKey(
        to='self', blank=True, null=True,
        on_delete=models.CASCADE, verbose_name='Родительская категория'
    )

    def __str__(self):
        return self.title

Согласитесь, выглядит не очень? Одинаковые поля мы прописали в двух моделях, а иногда и в большем количестве моделей.

Решение:

Решение очень простое, использовать Abstract models. Abstract models - это что-то наподобие миксинов для django моделей. Абстрактная модель не создает таблицу в базе данных при запуске миграций, но зато от неё можно наследоваться и расширять наши модели.

class CommonDataAbstractModel(models.Model):
    """
        Abstract model for storing common data
    """
    
    class Meta:
        abstract = True

    title = models.CharField(max_length=120, verbose_name='Заголовок')
    is_active = models.BooleanField(default=True, verbose_name='Опубликовано')
    created_at = models.DateTimeField(
        auto_now=True, verbose_name='Дата создания'
    )
    
    
class Post(CommonDataAbstractModel):
    """ Model for storing Post objects """

    class Meta:
        verbose_name = 'Статья'
        verbose_name_plural = 'Статьи'

    cover = models.ImageField(upload_to='bla_bla_bla/', verbose_name='Обложка')
    body = models.TextField(verbose_name='Текст статьи')

    def __str__(self):
        return self.title


class Category(CommonDataAbstractModel):
    """ Model for storing Category objects """

    class Meta:
        verbose_name = 'Категория'
        verbose_name_plural = 'Категории'

    parent = models.ForeignKey(
        to='self', blank=True, null=True,
        on_delete=models.CASCADE, verbose_name='Родительская категория'
    )

    def __str__(self):
        return self.title

Согласитесь удобно? - Особенно если у нас одинаковые поля не в двух моделях, а например в десятке.

Пометки:

  1. Крайне удобно выносить в абстрактные модели поля для SEO функционала.

  2. Рекомендую хранить абстрактные модели отдельно, в каком-нибудь основном приложении например в файле mixins.py.