ManyToManyField
Поле ManyToManyField предназначено для организации связи "многие ко многим" между двумя моделями. Оно создает промежуточную таблицу в базе данных, которая хранит связи между экземплярами моделей. Поле принимает класс модели, с которой устанавливается связь, и предоставляет API для работы с связанными объектами через менеджер отношений.
Синтаксис
from django.db import models
class ModelName(models.Model):
field_name = models.ManyToManyField(
OtherModel,
**options
)
Пример
Давайте создадим модели для блога, где статья может иметь множество тегов, а один тег может принадлежать множеству статей:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField('Tag', related_name='articles')
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
Пример
Добавим теги к статьям и получим все статьи для определенного тега:
from myapp.models import Article, Tag
# Create tags
python_tag = Tag.objects.create(name='python')
django_tag = Tag.objects.create(name='django')
api_tag = Tag.objects.create(name='api')
# Create article and add tags
article = Article.objects.create(
title='Django REST Framework',
content='Creating REST API with Django'
)
article.tags.add(python_tag, django_tag, api_tag)
# Get all articles for python tag
res = python_tag.articles.all()
print(res)
Результат выполнения кода:
[<Article: Django REST Framework>]
Параметры
Поле ManyToManyField принимает несколько важных параметров, влияющих на поведение связи:
-
to- обязательный параметр, указывает на модель, с которой устанавливается связь -
related_name- имя для обратной связи от связанной модели к текущей -
through- модель для промежуточной таблицы, позволяет хранить дополнительные данные -
through_fields- явное указание полей в промежуточной модели -
symmetrical- определяет симметричность связи для рекурсивных отношений
Пример
Используем параметр related_name для получения статей через тег:
from myapp.models import Tag
# Get tag by name
tag = Tag.objects.get(name='python')
# Get all articles with this tag
res = tag.articles.all()
for article in res:
print(article.title)
Результат выполнения кода:
"Django REST Framework"
"Python Basics"
Пример
Добавим несколько тегов к статье и удалим один из них:
from myapp.models import Article, Tag
# Get article and tags
article = Article.objects.get(title='Django REST Framework')
tag1 = Tag.objects.get(name='python')
tag2 = Tag.objects.get(name='django')
tag3 = Tag.objects.get(name='api')
# Add multiple tags
article.tags.add(tag1, tag2, tag3)
# Remove one tag
article.tags.remove(tag2)
# Get all tags for article
res = article.tags.all()
for tag in res:
print(tag.name)
Результат выполнения кода:
"python"
"api"
Пример
Очистим все теги у статьи и проверим результат:
from myapp.models import Article
# Get article
article = Article.objects.get(title='Django REST Framework')
# Clear all tags
article.tags.clear()
# Check if tags exist
res = article.tags.exists()
print(res)
Результат выполнения кода:
"False"
Пример
Используем промежуточную модель для хранения дополнительной информации о связи:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField(
'Tag',
through='ArticleTag',
related_name='articles'
)
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class ArticleTag(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
order = models.IntegerField(default=0)
class Meta:
ordering = ['order']
Пример
Работаем со связью через промежуточную модель:
from myapp.models import Article, Tag, ArticleTag
# Create objects
article = Article.objects.create(
title='Advanced Django',
content='Deep dive into Django'
)
tag = Tag.objects.create(name='advanced')
# Create relationship with extra data
ArticleTag.objects.create(
article=article,
tag=tag,
order=1
)
# Get all tags for article with extra data
res = ArticleTag.objects.filter(article=article)
for rel in res:
print(f"{rel.tag.name} - order: {rel.order}")
Результат выполнения кода:
"advanced - order: 1"
Смотрите также
-
поле
ForeignKey,
которое создает связь "один ко многим" -
поле
OneToOneField,
которое создает связь "один к одному" -
метод
add,
который добавляет объекты в отношение -
метод
remove,
который удаляет объекты из отношения