1. ==========================
    
  2. Many-to-many relationships
    
  3. ==========================
    
  4. 
    
  5. .. highlight:: pycon
    
  6. 
    
  7. To define a many-to-many relationship, use
    
  8. :class:`~django.db.models.ManyToManyField`.
    
  9. 
    
  10. In this example, an ``Article`` can be published in multiple ``Publication``
    
  11. objects, and a ``Publication`` has multiple ``Article`` objects:
    
  12. 
    
  13. .. code-block:: python
    
  14. 
    
  15.     from django.db import models
    
  16. 
    
  17.     class Publication(models.Model):
    
  18.         title = models.CharField(max_length=30)
    
  19. 
    
  20.         class Meta:
    
  21.             ordering = ['title']
    
  22. 
    
  23.         def __str__(self):
    
  24.             return self.title
    
  25. 
    
  26.     class Article(models.Model):
    
  27.         headline = models.CharField(max_length=100)
    
  28.         publications = models.ManyToManyField(Publication)
    
  29. 
    
  30.         class Meta:
    
  31.             ordering = ['headline']
    
  32. 
    
  33.         def __str__(self):
    
  34.             return self.headline
    
  35. 
    
  36. What follows are examples of operations that can be performed using the Python
    
  37. API facilities.
    
  38. 
    
  39. Create a few ``Publications``::
    
  40. 
    
  41.     >>> p1 = Publication(title='The Python Journal')
    
  42.     >>> p1.save()
    
  43.     >>> p2 = Publication(title='Science News')
    
  44.     >>> p2.save()
    
  45.     >>> p3 = Publication(title='Science Weekly')
    
  46.     >>> p3.save()
    
  47. 
    
  48. Create an ``Article``::
    
  49. 
    
  50.     >>> a1 = Article(headline='Django lets you build web apps easily')
    
  51. 
    
  52. You can't associate it with a ``Publication`` until it's been saved::
    
  53. 
    
  54.     >>> a1.publications.add(p1)
    
  55.     Traceback (most recent call last):
    
  56.     ...
    
  57.     ValueError: "<Article: Django lets you build web apps easily>" needs to have a value for field "id" before this many-to-many relationship can be used.
    
  58. 
    
  59. Save it!
    
  60. ::
    
  61. 
    
  62.     >>> a1.save()
    
  63. 
    
  64. Associate the ``Article`` with a ``Publication``::
    
  65. 
    
  66.     >>> a1.publications.add(p1)
    
  67. 
    
  68. Create another ``Article``, and set it to appear in the ``Publications``::
    
  69. 
    
  70.     >>> a2 = Article(headline='NASA uses Python')
    
  71.     >>> a2.save()
    
  72.     >>> a2.publications.add(p1, p2)
    
  73.     >>> a2.publications.add(p3)
    
  74. 
    
  75. Adding a second time is OK, it will not duplicate the relation::
    
  76. 
    
  77.     >>> a2.publications.add(p3)
    
  78. 
    
  79. Adding an object of the wrong type raises :exc:`TypeError`::
    
  80. 
    
  81.     >>> a2.publications.add(a1)
    
  82.     Traceback (most recent call last):
    
  83.     ...
    
  84.     TypeError: 'Publication' instance expected
    
  85. 
    
  86. Create and add a ``Publication`` to an ``Article`` in one step using
    
  87. :meth:`~django.db.models.fields.related.RelatedManager.create`::
    
  88. 
    
  89.     >>> new_publication = a2.publications.create(title='Highlights for Children')
    
  90. 
    
  91. ``Article`` objects have access to their related ``Publication`` objects::
    
  92. 
    
  93.     >>> a1.publications.all()
    
  94.     <QuerySet [<Publication: The Python Journal>]>
    
  95.     >>> a2.publications.all()
    
  96.     <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
    
  97. 
    
  98. ``Publication`` objects have access to their related ``Article`` objects::
    
  99. 
    
  100.     >>> p2.article_set.all()
    
  101.     <QuerySet [<Article: NASA uses Python>]>
    
  102.     >>> p1.article_set.all()
    
  103.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  104.     >>> Publication.objects.get(id=4).article_set.all()
    
  105.     <QuerySet [<Article: NASA uses Python>]>
    
  106. 
    
  107. Many-to-many relationships can be queried using :ref:`lookups across
    
  108. relationships <lookups-that-span-relationships>`::
    
  109. 
    
  110.     >>> Article.objects.filter(publications__id=1)
    
  111.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  112.     >>> Article.objects.filter(publications__pk=1)
    
  113.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  114.     >>> Article.objects.filter(publications=1)
    
  115.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  116.     >>> Article.objects.filter(publications=p1)
    
  117.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  118. 
    
  119.     >>> Article.objects.filter(publications__title__startswith="Science")
    
  120.     <QuerySet [<Article: NASA uses Python>, <Article: NASA uses Python>]>
    
  121. 
    
  122.     >>> Article.objects.filter(publications__title__startswith="Science").distinct()
    
  123.     <QuerySet [<Article: NASA uses Python>]>
    
  124. 
    
  125. The :meth:`~django.db.models.query.QuerySet.count` function respects
    
  126. :meth:`~django.db.models.query.QuerySet.distinct` as well::
    
  127. 
    
  128.     >>> Article.objects.filter(publications__title__startswith="Science").count()
    
  129.     2
    
  130. 
    
  131.     >>> Article.objects.filter(publications__title__startswith="Science").distinct().count()
    
  132.     1
    
  133. 
    
  134.     >>> Article.objects.filter(publications__in=[1,2]).distinct()
    
  135.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  136.     >>> Article.objects.filter(publications__in=[p1,p2]).distinct()
    
  137.     <QuerySet [<Article: Django lets you build web apps easily>, <Article: NASA uses Python>]>
    
  138. 
    
  139. Reverse m2m queries are supported (i.e., starting at the table that doesn't have
    
  140. a :class:`~django.db.models.ManyToManyField`)::
    
  141. 
    
  142.     >>> Publication.objects.filter(id=1)
    
  143.     <QuerySet [<Publication: The Python Journal>]>
    
  144.     >>> Publication.objects.filter(pk=1)
    
  145.     <QuerySet [<Publication: The Python Journal>]>
    
  146. 
    
  147.     >>> Publication.objects.filter(article__headline__startswith="NASA")
    
  148.     <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
    
  149. 
    
  150.     >>> Publication.objects.filter(article__id=1)
    
  151.     <QuerySet [<Publication: The Python Journal>]>
    
  152.     >>> Publication.objects.filter(article__pk=1)
    
  153.     <QuerySet [<Publication: The Python Journal>]>
    
  154.     >>> Publication.objects.filter(article=1)
    
  155.     <QuerySet [<Publication: The Python Journal>]>
    
  156.     >>> Publication.objects.filter(article=a1)
    
  157.     <QuerySet [<Publication: The Python Journal>]>
    
  158. 
    
  159.     >>> Publication.objects.filter(article__in=[1,2]).distinct()
    
  160.     <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
    
  161.     >>> Publication.objects.filter(article__in=[a1,a2]).distinct()
    
  162.     <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
    
  163. 
    
  164. Excluding a related item works as you would expect, too (although the SQL
    
  165. involved is a little complex)::
    
  166. 
    
  167.     >>> Article.objects.exclude(publications=p2)
    
  168.     <QuerySet [<Article: Django lets you build web apps easily>]>
    
  169. 
    
  170. If we delete a ``Publication``, its ``Articles`` won't be able to access it::
    
  171. 
    
  172.     >>> p1.delete()
    
  173.     >>> Publication.objects.all()
    
  174.     <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>]>
    
  175.     >>> a1 = Article.objects.get(pk=1)
    
  176.     >>> a1.publications.all()
    
  177.     <QuerySet []>
    
  178. 
    
  179. If we delete an ``Article``, its ``Publications`` won't be able to access it::
    
  180. 
    
  181.     >>> a2.delete()
    
  182.     >>> Article.objects.all()
    
  183.     <QuerySet [<Article: Django lets you build web apps easily>]>
    
  184.     >>> p2.article_set.all()
    
  185.     <QuerySet []>
    
  186. 
    
  187. Adding via the 'other' end of an m2m::
    
  188. 
    
  189.     >>> a4 = Article(headline='NASA finds intelligent life on Earth')
    
  190.     >>> a4.save()
    
  191.     >>> p2.article_set.add(a4)
    
  192.     >>> p2.article_set.all()
    
  193.     <QuerySet [<Article: NASA finds intelligent life on Earth>]>
    
  194.     >>> a4.publications.all()
    
  195.     <QuerySet [<Publication: Science News>]>
    
  196. 
    
  197. Adding via the other end using keywords::
    
  198. 
    
  199.     >>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders')
    
  200.     >>> p2.article_set.all()
    
  201.     <QuerySet [<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]>
    
  202.     >>> a5 = p2.article_set.all()[1]
    
  203.     >>> a5.publications.all()
    
  204.     <QuerySet [<Publication: Science News>]>
    
  205. 
    
  206. Removing ``Publication`` from an ``Article``::
    
  207. 
    
  208.     >>> a4.publications.remove(p2)
    
  209.     >>> p2.article_set.all()
    
  210.     <QuerySet [<Article: Oxygen-free diet works wonders>]>
    
  211.     >>> a4.publications.all()
    
  212.     <QuerySet []>
    
  213. 
    
  214. And from the other end::
    
  215. 
    
  216.     >>> p2.article_set.remove(a5)
    
  217.     >>> p2.article_set.all()
    
  218.     <QuerySet []>
    
  219.     >>> a5.publications.all()
    
  220.     <QuerySet []>
    
  221. 
    
  222. Relation sets can be set::
    
  223. 
    
  224.     >>> a4.publications.all()
    
  225.     <QuerySet [<Publication: Science News>]>
    
  226.     >>> a4.publications.set([p3])
    
  227.     >>> a4.publications.all()
    
  228.     <QuerySet [<Publication: Science Weekly>]>
    
  229. 
    
  230. Relation sets can be cleared::
    
  231. 
    
  232.     >>> p2.article_set.clear()
    
  233.     >>> p2.article_set.all()
    
  234.     <QuerySet []>
    
  235. 
    
  236. And you can clear from the other end::
    
  237. 
    
  238.     >>> p2.article_set.add(a4, a5)
    
  239.     >>> p2.article_set.all()
    
  240.     <QuerySet [<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]>
    
  241.     >>> a4.publications.all()
    
  242.     <QuerySet [<Publication: Science News>, <Publication: Science Weekly>]>
    
  243.     >>> a4.publications.clear()
    
  244.     >>> a4.publications.all()
    
  245.     <QuerySet []>
    
  246.     >>> p2.article_set.all()
    
  247.     <QuerySet [<Article: Oxygen-free diet works wonders>]>
    
  248. 
    
  249. Recreate the ``Article`` and ``Publication`` we have deleted::
    
  250. 
    
  251.     >>> p1 = Publication(title='The Python Journal')
    
  252.     >>> p1.save()
    
  253.     >>> a2 = Article(headline='NASA uses Python')
    
  254.     >>> a2.save()
    
  255.     >>> a2.publications.add(p1, p2, p3)
    
  256. 
    
  257. Bulk delete some ``Publications`` - references to deleted publications should
    
  258. go::
    
  259. 
    
  260.     >>> Publication.objects.filter(title__startswith='Science').delete()
    
  261.     >>> Publication.objects.all()
    
  262.     <QuerySet [<Publication: Highlights for Children>, <Publication: The Python Journal>]>
    
  263.     >>> Article.objects.all()
    
  264.     <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>]>
    
  265.     >>> a2.publications.all()
    
  266.     <QuerySet [<Publication: The Python Journal>]>
    
  267. 
    
  268. Bulk delete some articles - references to deleted objects should go::
    
  269. 
    
  270.     >>> q = Article.objects.filter(headline__startswith='Django')
    
  271.     >>> print(q)
    
  272.     <QuerySet [<Article: Django lets you build web apps easily>]>
    
  273.     >>> q.delete()
    
  274. 
    
  275. After the :meth:`~django.db.models.query.QuerySet.delete`, the
    
  276. :class:`~django.db.models.query.QuerySet` cache needs to be cleared, and the
    
  277. referenced objects should be gone::
    
  278. 
    
  279.     >>> print(q)
    
  280.     <QuerySet []>
    
  281.     >>> p1.article_set.all()
    
  282.     <QuerySet [<Article: NASA uses Python>]>