mirror of
https://github.com/django/django.git
synced 2024-12-29 12:36:08 +00:00
a8c53041f9
Backport of 81e1a35c36
from master
226 lines
7.7 KiB
Plaintext
226 lines
7.7 KiB
Plaintext
#########################
|
|
Many-to-one relationships
|
|
#########################
|
|
|
|
To define a many-to-one relationship, use :class:`~django.db.models.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): # __unicode__ on Python 2
|
|
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)
|
|
|
|
def __str__(self): # __unicode__ on Python 2
|
|
return self.headline
|
|
|
|
class Meta:
|
|
ordering = ('headline',)
|
|
|
|
What follows are examples of operations that can be performed using the Python
|
|
API facilities.
|
|
|
|
.. highlight:: pycon
|
|
|
|
Create a few Reporters::
|
|
|
|
>>> 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()
|
|
|
|
Create an Article::
|
|
|
|
>>> 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>
|
|
|
|
Note that you must save an object before it can be assigned to a foreign key
|
|
relationship. For example, creating an ``Article`` with unsaved ``Reporter``
|
|
raises ``ValueError``::
|
|
|
|
>>> r3 = Reporter(first_name='John', last_name='Smith', email='john@example.com')
|
|
>>> Article(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: 'Cannot assign "<Reporter: John Smith>": "Reporter" instance isn't saved in the database.'
|
|
|
|
If you want to disable the unsaved instance check, you can use the
|
|
:attr:`~django.db.models.ForeignKey.allow_unsaved_instance_assignment`
|
|
attribute.
|
|
|
|
.. versionchanged:: 1.8
|
|
|
|
Previously, assigning unsaved objects did not raise an error and could
|
|
result in silent data loss.
|
|
|
|
Article objects have access to their related Reporter objects::
|
|
|
|
>>> r = a.reporter
|
|
|
|
On Python 2, these are strings of type ``str`` instead of unicode strings
|
|
because that's what was used in the creation of this reporter (and we haven't
|
|
refreshed the data from the database, which always returns unicode strings)::
|
|
|
|
>>> r.first_name, r.last_name
|
|
('John', 'Smith')
|
|
|
|
Create an Article via the Reporter object::
|
|
|
|
>>> 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
|
|
|
|
Create a new article, and add it to the article set::
|
|
|
|
>>> new_article2 = Article(headline="Paul's story", pub_date=date(2006, 1, 17))
|
|
>>> r.article_set.add(new_article2)
|
|
>>> new_article2.reporter
|
|
<Reporter: John Smith>
|
|
>>> new_article2.reporter.id
|
|
1
|
|
>>> r.article_set.all()
|
|
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
|
|
|
|
Add the same article to a different article set - check that it moves::
|
|
|
|
>>> r2.article_set.add(new_article2)
|
|
>>> new_article2.reporter.id
|
|
2
|
|
>>> new_article2.reporter
|
|
<Reporter: Paul Jones>
|
|
|
|
Adding an object of the wrong type raises TypeError::
|
|
|
|
>>> r.article_set.add(r2)
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: 'Article' instance expected
|
|
|
|
>>> r.article_set.all()
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
>>> r2.article_set.all()
|
|
[<Article: Paul's story>]
|
|
|
|
>>> r.article_set.count()
|
|
2
|
|
|
|
>>> r2.article_set.count()
|
|
1
|
|
|
|
Note that in the last example the article has moved from John to Paul.
|
|
|
|
Related managers support field lookups as well.
|
|
The API automatically follows relationships as far as you need.
|
|
Use double underscores to separate relationships.
|
|
This works as many levels deep as you want. There's no limit. For example::
|
|
|
|
>>> r.article_set.filter(headline__startswith='This')
|
|
[<Article: This is a test>]
|
|
|
|
# Find all Articles for any Reporter whose first name is "John".
|
|
>>> Article.objects.filter(reporter__first_name='John')
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
|
|
Exact match is implied here::
|
|
|
|
>>> Article.objects.filter(reporter__first_name='John')
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
|
|
Query twice over the related field. This translates to an AND condition in the
|
|
WHERE clause::
|
|
|
|
>>> Article.objects.filter(reporter__first_name='John', reporter__last_name='Smith')
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
|
|
For the related lookup you can supply a primary key value or pass the related
|
|
object explicitly::
|
|
|
|
>>> Article.objects.filter(reporter__pk=1)
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
>>> Article.objects.filter(reporter=1)
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
>>> Article.objects.filter(reporter=r)
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
|
|
>>> Article.objects.filter(reporter__in=[1,2]).distinct()
|
|
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
|
|
>>> Article.objects.filter(reporter__in=[r,r2]).distinct()
|
|
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
|
|
|
|
You can also use a queryset instead of a literal list of instances::
|
|
|
|
>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John')).distinct()
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
|
|
Querying in the opposite direction::
|
|
|
|
>>> Reporter.objects.filter(article__pk=1)
|
|
[<Reporter: John Smith>]
|
|
>>> Reporter.objects.filter(article=1)
|
|
[<Reporter: John Smith>]
|
|
>>> Reporter.objects.filter(article=a)
|
|
[<Reporter: John Smith>]
|
|
|
|
>>> Reporter.objects.filter(article__headline__startswith='This')
|
|
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
|
|
>>> Reporter.objects.filter(article__headline__startswith='This').distinct()
|
|
[<Reporter: John Smith>]
|
|
|
|
Counting in the opposite direction works in conjunction with distinct()::
|
|
|
|
>>> Reporter.objects.filter(article__headline__startswith='This').count()
|
|
3
|
|
>>> Reporter.objects.filter(article__headline__startswith='This').distinct().count()
|
|
1
|
|
|
|
Queries can go round in circles::
|
|
|
|
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John')
|
|
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
|
|
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
|
|
[<Reporter: John Smith>]
|
|
>>> Reporter.objects.filter(article__reporter=r).distinct()
|
|
[<Reporter: John Smith>]
|
|
|
|
If you delete a reporter, his articles will be deleted (assuming that the
|
|
ForeignKey was defined with :attr:`django.db.models.ForeignKey.on_delete` set to
|
|
``CASCADE``, which is the default)::
|
|
|
|
>>> Article.objects.all()
|
|
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
|
|
>>> Reporter.objects.order_by('first_name')
|
|
[<Reporter: John Smith>, <Reporter: Paul Jones>]
|
|
>>> r2.delete()
|
|
>>> Article.objects.all()
|
|
[<Article: John's second story>, <Article: This is a test>]
|
|
>>> Reporter.objects.order_by('first_name')
|
|
[<Reporter: John Smith>]
|
|
|
|
You can delete using a JOIN in the query::
|
|
|
|
>>> Reporter.objects.filter(article__headline__startswith='This').delete()
|
|
>>> Reporter.objects.all()
|
|
[]
|
|
>>> Article.objects.all()
|
|
[]
|