Взаимосвязи многие ко многим
Для определения взаимосвязи многие ко многим используйте ManyToManyField.
В этом примере Article может быть опубликован в нескольких объектах Publication, и Publication имеет несколько объектов Article.
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
def __str__(self): # __unicode__ on Python 2
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
def __str__(self): # __unicode__ on Python 2
return self.headline
class Meta:
ordering = ('headline',)
Ниже приведены примеры операций, которые можно выполнить с помощью средств API Python. Обратите внимание, что если вы используете промежуточную модель для взаимосвязи многие ко многим, некоторые методы связанного менеджера будут отключены, поэтому некоторые из этих примеров не будут работать с такими моделями.
Создайте пару 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/1.10/topics/db/examples/many_to_many/