← Назад к вопросам
Как работает наследование в моделях?
2.2 Middle🔥 161 комментариев
#Django#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Наследование в Django моделях
Наследование в Django моделях позволяет переиспользовать код и создавать иерархии моделей. Существует три основных типа наследования.
1. Абстрактное наследование (Abstract Base Classes)
Абстрактная модель не создаёт таблицу в БД, это просто контейнер для общих полей.
from django.db import models
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
class Meta:
abstract = True # Это абстрактная модель
class Article(BaseModel):
title = models.CharField(max_length=200)
content = models.TextField()
class Meta:
db_table = 'articles'
class Comment(BaseModel):
text = models.TextField()
article = models.ForeignKey(Article, on_delete=models.CASCADE)
Результат в БД:
- Таблица articles: id, title, content, created_at, updated_at, is_active
- Таблица comments: id, text, article_id, created_at, updated_at, is_active
2. Наследование с Multi-table Inheritance
Каждая модель имеет свою таблицу, связанные через OneToOneField.
class Person(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
class Student(Person):
student_id = models.CharField(max_length=20, unique=True)
enrollment_date = models.DateField()
class Teacher(Person):
employee_id = models.CharField(max_length=20, unique=True)
department = models.CharField(max_length=100)
Результат в БД:
- Таблица person: id, name, birth_date
- Таблица student: person_ptr_id (FK), student_id, enrollment_date
- Таблица teacher: person_ptr_id (FK), employee_id, department
Использование:
# Создание
student = Student.objects.create(
name="John",
birth_date="2000-01-01",
student_id="S12345",
enrollment_date="2020-09-01"
)
# Доступ к полям
print(student.name) # John
print(student.student_id) # S12345
# Запрос
students = Student.objects.all()
teachers = Teacher.objects.all()
# Получение всех Person
persons = Person.objects.all() # Вернёт и студентов и учителей
# Проверка типа
for person in Person.objects.all():
if isinstance(person, Student):
print(f"Student: {person.student_id}")
elif isinstance(person, Teacher):
print(f"Teacher: {person.employee_id}")
3. Proxy модели
Proxy модель использует ту же таблицу, что и родитель, но позволяет переопределить поведение.
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_published = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
class PublishedArticle(Article):
"""Proxy для опубликованных статей"""
class Meta:
proxy = True
ordering = ['-created_at']
def save(self, *args, **kwargs):
self.is_published = True
super().save(*args, **kwargs)
class DraftArticle(Article):
"""Proxy для черновиков"""
class Meta:
proxy = True
objects = models.Manager()
def get_queryset(self):
return super().get_queryset().filter(is_published=False)
Использование proxy:
# Та же таблица, разные представления
published = PublishedArticle.objects.all()
drafts = DraftArticle.objects.all()
# Создание через proxy
article = PublishedArticle.objects.create(title="My Article")
print(article.is_published) # True
4. Сравнение типов наследования
| Тип | Таблица | Использование | Плюсы | Минусы |
|---|---|---|---|---|
| Abstract | Нет | Общие поля | Просто, без JOIN | Нельзя запрашивать базовый класс |
| Multi-table | Несколько | Иерархия типов | Полиморфизм, наследование | JOIN операции, медленнее |
| Proxy | Одна | Альтернативный интерфейс | Быстро, фильтрация | Нельзя добавлять поля |
5. Практические примеры
Example 1: Content Management System
class Content(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
abstract = True
class Article(Content):
body = models.TextField()
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
class Video(Content):
url = models.URLField()
duration = models.IntegerField() # в секундах
class Podcast(Content):
audio_file = models.FileField(upload_to="podcasts/")
duration = models.IntegerField()
Example 2: User roles
class User(models.Model):
email = models.EmailField(unique=True)
is_active = models.BooleanField(default=True)
class Meta:
abstract = True
class AdminUser(User):
has_full_access = models.BooleanField(default=False)
def can_delete_users(self):
return self.has_full_access
class RegularUser(User):
profile_picture = models.ImageField(upload_to="profiles/")
bio = models.TextField(blank=True)
6. Best Practices
1. Используйте Abstract для общих полей:
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
2. Избегайте глубокой иерархии: максимум 2-3 уровня
3. Proxy для фильтрации: когда нужен другой queryset
4. Multi-table для истинной иерархии: когда разные типы имеют разные поля
5. Явные related_name: избегайте конфликтов
class StudentPost(models.Model):
author = models.ForeignKey(
Student,
on_delete=models.CASCADE,
related_name="posts"
)