Связи "многие ко многим"
Для определения связи "многие ко многим" используйте ManyToManyField.
В этом примере Article может быть опубликован в нескольких объектах Publication, и у Publication есть несколько объектов Article:
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
def __str__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
def __str__(self):
return self.headline
class Meta:
ordering = ('headline',)
Ниже приведены примеры операций, которые можно выполнить с помощью средств Python API. Обратите внимание, что если вы используете промежуточную модель для связи "многие ко многим", некоторые методы связанного менеджера будут отключены, поэтому некоторые из этих примеров не будут работать с такими моделями.
Создайте несколько Publications:
>>> p1 = Publication(title='The Python Journal') >>> p1.save() >>> p2 = Publication(title='Science News') >>> p2.save() >>> p3 = Publication(title='Science Weekly') >>> p3.save()
Создайте Article:
>>> a1 = Article(headline='Django lets you build Web apps easily')
Вы не можете связать его с Publication, пока он не будет сохранён:
>>> a1.publications.add(p1) Traceback (most recent call last): ... ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used.
Сохраните его!
>>> a1.save()
Свяжите Article с Publication:
>>> a1.publications.add(p1)
Создайте ещё один Article, и установите его для отображения в Publications:
>>> a2 = Article(headline='NASA uses Python') >>> a2.save() >>> a2.publications.add(p1, p2) >>> a2.publications.add(p3)
Добавление во второй раз допустимо, это не создаст дубликат отношения:
>>> a2.publications.add(p3)
Добавление объекта неправильного типа вызывает TypeError:
>>> a2.publications.add(a1) Traceback (most recent call last): ... TypeError: 'Publication' instance expected
Создайте и добавьте Publication к Article в одном шаге, используя create():
>>> new_publication = a2.publications.create(title='Highlights for Children')
Объекты Article имеют доступ к связанным объектам Publication:
>>> a1.publications.all() <QuerySet [<Publication: The Python Journal>]> >>> a2.publications.all() <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
Объекты Publication имеют доступ к связанным объектам Article:
>>> p2.article_set.all() <QuerySet [<Article: NASA uses Python>]> >>> p1.article_set.all() <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Publication.objects.get(id=4).article_set.all() <QuerySet [<Article: NASA uses Python>]>
Связи "многие ко многим" можно запросить, используя запросы по отношениям:
>>> Article.objects.filter(publications__id=1) <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications__pk=1) <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications=1) <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications=p1) <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications__title__startswith="Science") <QuerySet [<Article: NASA uses Python>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications__title__startswith="Science").distinct() <QuerySet [<Article: NASA uses Python>]>
Функция count() учитывает distinct():
>>> Article.objects.filter(publications__title__startswith="Science").count() 2 >>> Article.objects.filter(publications__title__startswith="Science").distinct().count() 1 >>> Article.objects.filter(publications__in=[1,2]).distinct() <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]> >>> Article.objects.filter(publications__in=[p1,p2]).distinct() <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
Обратные запросы m2m поддерживаются (т.е., начиная с таблицы, в которой нет ManyToManyField):
>>> Publication.objects.filter(id=1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(pk=1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(article__headline__startswith="NASA") <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]> >>> Publication.objects.filter(article__id=1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(article__pk=1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(article=1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(article=a1) <QuerySet [<Publication: The Python Journal>]> >>> Publication.objects.filter(article__in=[1,2]).distinct() <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]> >>> Publication.objects.filter(article__in=[a1,a2]).distinct() <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
Исключение связанного элемента работает так, как ожидается (хотя SQL немного сложнее):
>>> Article.objects.exclude(publications=p2) <QuerySet [<Article: Django lets you build Web apps easily>]>
Если мы удалим Publication, его Articles не сможет получить к нему доступ:
>>> p1.delete() >>> Publication.objects.all() <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>]> >>> a1 = Article.objects.get(pk=1) >>> a1.publications.all() <QuerySet []>
Если мы удалим Article, его Publications не сможет получить к нему доступ:
>>> a2.delete() >>> Article.objects.all() <QuerySet [<Article: Django lets you build Web apps easily>]> >>> p2.article_set.all() <QuerySet []>
Добавление через другой конец m2m:
>>> a4 = Article(headline='NASA finds intelligent life on Earth') >>> a4.save() >>> p2.article_set.add(a4) >>> p2.article_set.all() <QuerySet [<Article: NASA finds intelligent life on Earth>]> >>> a4.publications.all() <QuerySet [<Publication: Science News>]>
Добавление через другой конец с использованием ключевых слов:
>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders') >>> p2.article_set.all() <QuerySet [<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]> >>> a5 = p2.article_set.all()[1] >>> a5.publications.all() <QuerySet [<Publication: Science News>]>
Удаление Publication из Article:
>>> a4.publications.remove(p2) >>> p2.article_set.all() <QuerySet [<Article: Oxygen-free diet works wonders>]> >>> a4.publications.all() <QuerySet []>
И с другой стороны:
>>> p2.article_set.remove(a5) >>> p2.article_set.all() <QuerySet []> >>> a5.publications.all() <QuerySet []>
Наборы отношений могут быть установлены:
>>> a4.publications.all() <QuerySet [<Publication: Science News>]> >>> a4.publications.set([p3]) >>> a4.publications.all() <QuerySet [<Publication: Science Weekly>]>
Наборы отношений могут быть очищены:
>>> p2.article_set.clear() >>> p2.article_set.all() <QuerySet []>
И вы можете очистить с другой стороны:
>>> p2.article_set.add(a4, a5) >>> p2.article_set.all() <QuerySet [<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]> >>> a4.publications.all() <QuerySet [<Publication: Science News>, <Publication: Science Weekly>]> >>> a4.publications.clear() >>> a4.publications.all() <QuerySet []> >>> p2.article_set.all() <QuerySet [<Article: Oxygen-free diet works wonders>]>
Восстановите Article и Publication, которые мы удалили:
>>> p1 = Publication(title='The Python Journal') >>> p1.save() >>> a2 = Article(headline='NASA uses Python') >>> a2.save() >>> a2.publications.add(p1, p2, p3)
Массово удалите некоторые Publications - ссылки на удалённые публикации должны исчезнуть:
>>> Publication.objects.filter(title__startswith='Science').delete() >>> Publication.objects.all() <QuerySet [<Publication: Highlights for Children>, <Publication: The Python Journal>]> >>> Article.objects.all() <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA finds intelligent life on Earth>, <Article: NASA uses Python>, <Article: Oxygen-free diet works wonders>]> >>> a2.publications.all() <QuerySet [<Publication: The Python Journal>]>
Массово удалите некоторые статьи - ссылки на удалённые объекты должны исчезнуть:
>>> q = Article.objects.filter(headline__startswith='Django') >>> print(q) <QuerySet [<Article: Django lets you build Web apps easily>]> >>> q.delete()
После delete() кэш QuerySet необходимо очистить, и ссылки на объекты должны исчезнуть:
>>> print(q) <QuerySet []> >>> p1.article_set.all() <QuerySet [<Article: NASA uses Python>]>
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_many/