← Назад к вопросам
Что такое ManyToManyField в Django?
2.3 Middle🔥 211 комментариев
#Django#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Что такое ManyToManyField в Django?
ManyToManyField — это поле в Django ORM, которое создаёт связь «многие-ко-многим» между двумя моделями. Это означает, что один объект одной модели может быть связан с несколькими объектами другой модели и наоборот.
Основной концепт
Реальный пример: Студенты и Курсы
from django.db import models
class Course(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course, related_name='students')
def __str__(self):
return self.name
Соотношение:
- Один студент может учиться на многих курсах
- Один курс может иметь много студентов
Как это работает в базе данных
Django автоматически создаёт промежуточную таблицу:
student (id, name)
├── id | name
├── 1 | Alice
└── 2 | Bob
course (id, name)
├── id | name
├── 1 | Python
└── 2 | Django
student_courses (id, student_id, course_id) ← Создаёт Django!
├── id | student_id | course_id
├── 1 | 1 | 1 (Alice → Python)
├── 2 | 1 | 2 (Alice → Django)
└── 3 | 2 | 1 (Bob → Python)
Основные операции
Добавление связей
# Получаем объекты
alice = Student.objects.get(name='Alice')
python_course = Course.objects.get(name='Python')
# Добавляем курс к студенту
alice.courses.add(python_course)
# Или через ID
alice.courses.add(1)
# Добавляем несколько сразу
alice.courses.add(1, 2, 3)
Удаление связей
# Удалить конкретный курс
alice.courses.remove(python_course)
# Удалить все курсы
alice.courses.clear()
Получение связанных объектов
# Получить все курсы студента
alice = Student.objects.get(name='Alice')
print(alice.courses.all())
# <QuerySet [<Course: Python>, <Course: Django>]>
# Получить всех студентов на курсе (через related_name)
python = Course.objects.get(name='Python')
print(python.students.all())
# <QuerySet [<Student: Alice>, <Student: Bob>]>
Фильтрация
# Студенты, которые учатся на конкретном курсе
students = Student.objects.filter(courses__name='Python')
# Курсы, на которых учится конкретный студент
courses = Course.objects.filter(students__name='Alice')
# Проверка: есть ли у студента конкретный курс
has_course = alice.courses.filter(id=python_course.id).exists()
Кастомная промежуточная таблица (through)
Частая задача — добавить дополнительные данные в промежуточную таблицу:
class Enrollment(models.Model):
"""Кастомная модель для связи"""
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
enrolled_date = models.DateField(auto_now_add=True) # Дата зачисления
grade = models.CharField(max_length=2, null=True) # Оценка
class Meta:
unique_together = ('student', 'course')
def __str__(self):
return f"{self.student.name} → {self.course.name}"
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course, through='Enrollment')
Работа с кастомной промежуточной таблицей
# Добавить курс со здравственной датой
enrollment = Enrollment.objects.create(
student=alice,
course=python_course,
enrolled_date='2024-01-15'
)
# Получить курсы с дополнительной информацией
enrollments = Enrollment.objects.filter(student=alice)
for e in enrollments:
print(f"{e.course.name} - зачислена {e.enrolled_date}")
Различие: ManyToManyField vs ForeignKey
# ForeignKey: один-ко-многим (от дочернего к родительскому)
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE) # Много комментариев → 1 пост
# ManyToManyField: много-ко-многим (двусторонняя связь)
class Article(models.Model):
tags = models.ManyToManyField(Tag) # Много статей → много тегов, и наоборот
Производительность и оптимизация
# Плохо: N+1 проблема
for student in Student.objects.all():
print(student.courses.all()) # Запрос для каждого студента!
# Хорошо: prefetch_related
students = Student.objects.prefetch_related('courses')
for student in students:
print(student.courses.all()) # Только 2 запроса!
Практический пример: Социальная сеть
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
followers = models.ManyToManyField(
'self',
symmetrical=False,
related_name='following'
)
# Использование
user = User.objects.get(username='alice')
user.followers.add(bob) # Alice следит за Bob
print(user.followers.all()) # Кого Alice следит
print(user.following.all()) # Кто следит за Alice (благодаря related_name)
Итоги
- ManyToManyField создаёт связь много-ко-многим
- Django автоматически создаёт промежуточную таблицу
- Используй
add(),remove(),clear()для управления связями - Для дополнительных данных используй
throughс кастомной моделью - Не забывай про
prefetch_related()для оптимизации запросов