Взаимосвязи "многие ко одному"
Для определения взаимосвязи "многие ко одному" используйте ForeignKey:
from django.db import models
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
def __str__(self):
return self.headline
class Meta:
ordering = ['headline']
Ниже приведены примеры операций, которые можно выполнить с помощью средств Python API.
Создайте несколько репортеров:
>>> r = Reporter(first_name='John', last_name='Smith', email='john@example.com') >>> r.save() >>> r2 = Reporter(first_name='Paul', last_name='Jones', email='paul@example.com') >>> r2.save()
Создайте статью:
>>> from datetime import date >>> a = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r) >>> a.save() >>> a.reporter.id 1 >>> a.reporter <Reporter: John Smith>
Обратите внимание, что вы должны сохранить объект, прежде чем назначить его в качестве связи по внешнему ключу. Например, создание Article с незасохранённым Reporter вызывает ValueError:
>>> r3 = Reporter(first_name='John', last_name='Smith', email='john@example.com') >>> Article.objects.create(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3) Traceback (most recent call last): ... ValueError: save() prohibited to prevent data loss due to unsaved related object 'reporter'.
Объекты Article имеют доступ к связанным объектам Reporter:
>>> r = a.reporter
Создайте статью через объект Reporter:
>>> new_article = r.article_set.create(headline="John's second story", pub_date=date(2005, 7, 29)) >>> new_article <Article: John's second story> >>> new_article.reporter <Reporter: John Smith> >>> new_article.reporter.id 1
Создайте новую статью:
>>> new_article2 = Article.objects.create(headline="Paul's story", pub_date=date(2006, 1, 17), reporter=r) >>> new_article2.reporter <Reporter: John Smith> >>> new_article2.reporter.id 1 >>> r.article_set.all() <QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
Добавьте эту же статью в другой набор статей — убедитесь, что она перемещается:
>>> r2.article_set.add(new_article2) >>> new_article2.reporter.id 2 >>> new_article2.reporter <Reporter: Paul Jones>
Добавление объекта неправильного типа вызывает исключение TypeError:
>>> r.article_set.add(r2) Traceback (most recent call last): ... TypeError: 'Article' instance expected, got <Reporter: Paul Jones> >>> r.article_set.all() <QuerySet [<Article: John's second story>, <Article: This is a test>]> >>> r2.article_set.all() <QuerySet [<Article: Paul's story>]> >>> r.article_set.count() 2 >>> r2.article_set.count() 1
Обратите внимание, что в последнем примере статья была перемещена от Джона к Полу.
Связанные менеджеры также поддерживают поиск по полям. API автоматически отслеживает связи до тех пор, пока вам это нужно. Используйте двойные подчёркивания для разделения связей. Это работает на любой глубине вложенности. Ограничений нет. Например:
>>> r.article_set.filter(headline__startswith='This') <QuerySet [<Article: This is a test>]> # Find all Articles for any Reporter whose first name is "John". >>> Article.objects.filter(reporter__first_name='John') <QuerySet [<Article: John's second story>, <Article: This is a test>]>
Здесь подразумевается точное совпадение:
>>> Article.objects.filter(reporter__first_name='John') <QuerySet [<Article: John's second story>, <Article: This is a test>]>
Выполните запрос дважды по связанному полю. Это соответствует условию AND в предложении WHERE:
>>> Article.objects.filter(reporter__first_name='John', reporter__last_name='Smith') <QuerySet [<Article: John's second story>, <Article: This is a test>]>
Для связанного поиска можно указать значение первичного ключа или явно передать связанный объект:
>>> Article.objects.filter(reporter__pk=1) <QuerySet [<Article: John's second story>, <Article: This is a test>]> >>> Article.objects.filter(reporter=1) <QuerySet [<Article: John's second story>, <Article: This is a test>]> >>> Article.objects.filter(reporter=r) <QuerySet [<Article: John's second story>, <Article: This is a test>]> >>> Article.objects.filter(reporter__in=[1,2]).distinct() <QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]> >>> Article.objects.filter(reporter__in=[r,r2]).distinct() <QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
Вы также можете использовать набор запросов вместо буквального списка экземпляров:
>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John')).distinct() <QuerySet [<Article: John's second story>, <Article: This is a test>]>
Запрос в обратном направлении:
>>> Reporter.objects.filter(article__pk=1) <QuerySet [<Reporter: John Smith>]> >>> Reporter.objects.filter(article=1) <QuerySet [<Reporter: John Smith>]> >>> Reporter.objects.filter(article=a) <QuerySet [<Reporter: John Smith>]> >>> Reporter.objects.filter(article__headline__startswith='This') <QuerySet [<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]> >>> Reporter.objects.filter(article__headline__startswith='This').distinct() <QuerySet [<Reporter: John Smith>]>
Подсчёт в обратном направлении работает в сочетании с distinct():
>>> Reporter.objects.filter(article__headline__startswith='This').count() 3 >>> Reporter.objects.filter(article__headline__startswith='This').distinct().count() 1
Запросы могут зацикливаться:
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John') <QuerySet [<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]> >>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct() <QuerySet [<Reporter: John Smith>]> >>> Reporter.objects.filter(article__reporter=r).distinct() <QuerySet [<Reporter: John Smith>]>
Если вы удалите репортера, его статьи будут удалены (если внешний ключ был определён с django.db.models.ForeignKey.on_delete установленным в CASCADE, что является значением по умолчанию):
>>> Article.objects.all()
<QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
>>> Reporter.objects.order_by('first_name')
<QuerySet [<Reporter: John Smith>, <Reporter: Paul Jones>]>
>>> r2.delete()
>>> Article.objects.all()
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> Reporter.objects.order_by('first_name')
<QuerySet [<Reporter: John Smith>]>
Вы можете выполнить удаление с помощью соединения в запросе:
>>> Reporter.objects.filter(article__headline__startswith='This').delete() >>> Reporter.objects.all() <QuerySet []> >>> Article.objects.all() <QuerySet []>
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.2/topics/db/examples/many_to_one/