Fixed #17605: Restored deleted query documentation that used to live in doctests. Thanks zsiciarz for work on the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17737 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
1235cd376e
commit
dad1f5c21e
|
@ -0,0 +1,10 @@
|
||||||
|
========================================
|
||||||
|
Examples of model relationship API usage
|
||||||
|
========================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
many_to_many
|
||||||
|
many_to_one
|
||||||
|
one_to_one
|
|
@ -0,0 +1,289 @@
|
||||||
|
##########################
|
||||||
|
Many-to-many relationships
|
||||||
|
##########################
|
||||||
|
|
||||||
|
.. highlight:: pycon
|
||||||
|
|
||||||
|
To define a many-to-many relationship, use :ref:`ref-manytomany`.
|
||||||
|
|
||||||
|
In this example, an ``Article`` can be published in multiple ``Publication``
|
||||||
|
objects, and a ``Publication`` has multiple ``Article`` objects:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Publication(models.Model):
|
||||||
|
title = models.CharField(max_length=30)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('title',)
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
headline = models.CharField(max_length=100)
|
||||||
|
publications = models.ManyToManyField(Publication)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.headline
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('headline',)
|
||||||
|
|
||||||
|
What follows are examples of operations that can be performed using the Python
|
||||||
|
API facilities.
|
||||||
|
|
||||||
|
Create a couple of Publications::
|
||||||
|
|
||||||
|
>>> p1 = Publication(title='The Python Journal')
|
||||||
|
>>> p1.save()
|
||||||
|
>>> p2 = Publication(title='Science News')
|
||||||
|
>>> p2.save()
|
||||||
|
>>> p3 = Publication(title='Science Weekly')
|
||||||
|
>>> p3.save()
|
||||||
|
|
||||||
|
Create an Article::
|
||||||
|
|
||||||
|
>>> a1 = Article(headline='Django lets you build Web apps easily')
|
||||||
|
|
||||||
|
You can't associate it with a Publication until it's been saved::
|
||||||
|
|
||||||
|
>>> 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.
|
||||||
|
|
||||||
|
Save it!
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> a1.save()
|
||||||
|
|
||||||
|
Associate the Article with a Publication::
|
||||||
|
|
||||||
|
>>> a1.publications.add(p1)
|
||||||
|
|
||||||
|
Create another Article, and set it to appear in both Publications::
|
||||||
|
|
||||||
|
>>> a2 = Article(headline='NASA uses Python')
|
||||||
|
>>> a2.save()
|
||||||
|
>>> a2.publications.add(p1, p2)
|
||||||
|
>>> a2.publications.add(p3)
|
||||||
|
|
||||||
|
Adding a second time is OK::
|
||||||
|
|
||||||
|
>>> a2.publications.add(p3)
|
||||||
|
|
||||||
|
Adding an object of the wrong type raises TypeError::
|
||||||
|
|
||||||
|
>>> a2.publications.add(a1)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: 'Publication' instance expected
|
||||||
|
|
||||||
|
Add a Publication directly via publications.add by using keyword arguments::
|
||||||
|
|
||||||
|
>>> new_publication = a2.publications.create(title='Highlights for Children')
|
||||||
|
|
||||||
|
Article objects have access to their related Publication objects::
|
||||||
|
|
||||||
|
>>> a1.publications.all()
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
>>> a2.publications.all()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
|
||||||
|
|
||||||
|
Publication objects have access to their related Article objects::
|
||||||
|
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: NASA uses Python>]
|
||||||
|
>>> p1.article_set.all()
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
>>> Publication.objects.get(id=4).article_set.all()
|
||||||
|
[<Article: NASA uses Python>]
|
||||||
|
|
||||||
|
Many-to-many relationships can be queried using :ref:`lookups across relationships <lookups-that-span-relationships>`::
|
||||||
|
|
||||||
|
>>> Article.objects.filter(publications__id__exact=1)
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
>>> Article.objects.filter(publications__pk=1)
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
>>> Article.objects.filter(publications=1)
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
>>> Article.objects.filter(publications=p1)
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
|
||||||
|
>>> Article.objects.filter(publications__title__startswith="Science")
|
||||||
|
[<Article: NASA uses Python>, <Article: NASA uses Python>]
|
||||||
|
|
||||||
|
>>> Article.objects.filter(publications__title__startswith="Science").distinct()
|
||||||
|
[<Article: NASA uses Python>]
|
||||||
|
|
||||||
|
The count() function respects distinct() as well::
|
||||||
|
|
||||||
|
>>> 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()
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
>>> Article.objects.filter(publications__in=[p1,p2]).distinct()
|
||||||
|
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
|
||||||
|
|
||||||
|
Reverse m2m queries are supported (i.e., starting at the table that doesn't have
|
||||||
|
a ManyToManyField)::
|
||||||
|
|
||||||
|
>>> Publication.objects.filter(id__exact=1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
>>> Publication.objects.filter(pk=1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
|
||||||
|
>>> Publication.objects.filter(article__headline__startswith="NASA")
|
||||||
|
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
|
||||||
|
|
||||||
|
>>> Publication.objects.filter(article__id__exact=1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
>>> Publication.objects.filter(article__pk=1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
>>> Publication.objects.filter(article=1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
>>> Publication.objects.filter(article=a1)
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
|
||||||
|
>>> Publication.objects.filter(article__in=[1,2]).distinct()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
|
||||||
|
>>> Publication.objects.filter(article__in=[a1,a2]).distinct()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
|
||||||
|
|
||||||
|
Excluding a related item works as you would expect, too (although the SQL
|
||||||
|
involved is a little complex)::
|
||||||
|
|
||||||
|
>>> Article.objects.exclude(publications=p2)
|
||||||
|
[<Article: Django lets you build Web apps easily>]
|
||||||
|
|
||||||
|
If we delete a Publication, its Articles won't be able to access it::
|
||||||
|
|
||||||
|
>>> p1.delete()
|
||||||
|
>>> Publication.objects.all()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>]
|
||||||
|
>>> a1 = Article.objects.get(pk=1)
|
||||||
|
>>> a1.publications.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
If we delete an Article, its Publications won't be able to access it::
|
||||||
|
|
||||||
|
>>> a2.delete()
|
||||||
|
>>> Article.objects.all()
|
||||||
|
[<Article: Django lets you build Web apps easily>]
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
Adding via the 'other' end of an m2m::
|
||||||
|
|
||||||
|
>>> a4 = Article(headline='NASA finds intelligent life on Earth')
|
||||||
|
>>> a4.save()
|
||||||
|
>>> p2.article_set.add(a4)
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: NASA finds intelligent life on Earth>]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science News>]
|
||||||
|
|
||||||
|
Adding via the other end using keywords::
|
||||||
|
|
||||||
|
>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders')
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
|
||||||
|
>>> a5 = p2.article_set.all()[1]
|
||||||
|
>>> a5.publications.all()
|
||||||
|
[<Publication: Science News>]
|
||||||
|
|
||||||
|
Removing publication from an article::
|
||||||
|
|
||||||
|
>>> a4.publications.remove(p2)
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: Oxygen-free diet works wonders>]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
And from the other end::
|
||||||
|
|
||||||
|
>>> p2.article_set.remove(a5)
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[]
|
||||||
|
>>> a5.publications.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
Relation sets can be assigned. Assignment clears any existing set members::
|
||||||
|
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science News>]
|
||||||
|
>>> a4.publications = [p3]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science Weekly>]
|
||||||
|
|
||||||
|
Relation sets can be cleared::
|
||||||
|
|
||||||
|
>>> p2.article_set.clear()
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
And you can clear from the other end::
|
||||||
|
|
||||||
|
>>> p2.article_set.add(a4, a5)
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science News>, <Publication: Science Weekly>]
|
||||||
|
>>> a4.publications.clear()
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[]
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: Oxygen-free diet works wonders>]
|
||||||
|
|
||||||
|
Recreate the article and Publication we have deleted::
|
||||||
|
|
||||||
|
>>> p1 = Publication(title='The Python Journal')
|
||||||
|
>>> p1.save()
|
||||||
|
>>> a2 = Article(headline='NASA uses Python')
|
||||||
|
>>> a2.save()
|
||||||
|
>>> a2.publications.add(p1, p2, p3)
|
||||||
|
|
||||||
|
Bulk delete some Publications - references to deleted publications should go::
|
||||||
|
|
||||||
|
>>> Publication.objects.filter(title__startswith='Science').delete()
|
||||||
|
>>> Publication.objects.all()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: The Python Journal>]
|
||||||
|
>>> Article.objects.all()
|
||||||
|
[<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()
|
||||||
|
[<Publication: The Python Journal>]
|
||||||
|
|
||||||
|
Bulk delete some articles - references to deleted objects should go::
|
||||||
|
|
||||||
|
>>> q = Article.objects.filter(headline__startswith='Django')
|
||||||
|
>>> print q
|
||||||
|
[<Article: Django lets you build Web apps easily>]
|
||||||
|
>>> q.delete()
|
||||||
|
|
||||||
|
After the delete, the QuerySet cache needs to be cleared, and the referenced
|
||||||
|
objects should be gone::
|
||||||
|
|
||||||
|
>>> print q
|
||||||
|
[]
|
||||||
|
>>> p1.article_set.all()
|
||||||
|
[<Article: NASA uses Python>]
|
||||||
|
|
||||||
|
An alternate to calling clear() is to assign the empty set::
|
||||||
|
|
||||||
|
>>> p1.article_set = []
|
||||||
|
>>> p1.article_set.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
>>> a2.publications = [p1, new_publication]
|
||||||
|
>>> a2.publications.all()
|
||||||
|
[<Publication: Highlights for Children>, <Publication: The Python Journal>]
|
||||||
|
>>> a2.publications = []
|
||||||
|
>>> a2.publications.all()
|
||||||
|
[]
|
|
@ -0,0 +1,208 @@
|
||||||
|
#########################
|
||||||
|
Many-to-one relationships
|
||||||
|
#########################
|
||||||
|
|
||||||
|
.. highlight:: pycon
|
||||||
|
|
||||||
|
To define a many-to-one relationship, use :class:`~django.db.models.ForeignKey`.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
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 __unicode__(self):
|
||||||
|
return u"%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 __unicode__(self):
|
||||||
|
return self.headline
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('headline',)
|
||||||
|
|
||||||
|
What follows are examples of operations that can be performed using the Python
|
||||||
|
API facilities.
|
||||||
|
|
||||||
|
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 datetime
|
||||||
|
>>> a = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter=r)
|
||||||
|
>>> a.save()
|
||||||
|
|
||||||
|
>>> a.reporter.id
|
||||||
|
1
|
||||||
|
|
||||||
|
>>> a.reporter
|
||||||
|
<Reporter: John Smith>
|
||||||
|
|
||||||
|
Article objects have access to their related Reporter objects::
|
||||||
|
|
||||||
|
>>> r = a.reporter
|
||||||
|
|
||||||
|
These are strings 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=datetime(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=datetime(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__exact='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__exact='John', reporter__last_name__exact='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__exact=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()
|
||||||
|
[]
|
|
@ -0,0 +1,132 @@
|
||||||
|
########################
|
||||||
|
One-to-one relationships
|
||||||
|
########################
|
||||||
|
|
||||||
|
.. highlight:: pycon
|
||||||
|
|
||||||
|
To define a one-to-one relationship, use :ref:`ref-onetoone`.
|
||||||
|
|
||||||
|
In this example, a ``Place`` optionally can be a ``Restaurant``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from django.db import models, transaction, IntegrityError
|
||||||
|
|
||||||
|
class Place(models.Model):
|
||||||
|
name = models.CharField(max_length=50)
|
||||||
|
address = models.CharField(max_length=80)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s the place" % self.name
|
||||||
|
|
||||||
|
class Restaurant(models.Model):
|
||||||
|
place = models.OneToOneField(Place, primary_key=True)
|
||||||
|
serves_hot_dogs = models.BooleanField()
|
||||||
|
serves_pizza = models.BooleanField()
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s the restaurant" % self.place.name
|
||||||
|
|
||||||
|
class Waiter(models.Model):
|
||||||
|
restaurant = models.ForeignKey(Restaurant)
|
||||||
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s the waiter at %s" % (self.name, self.restaurant)
|
||||||
|
|
||||||
|
What follows are examples of operations that can be performed using the Python
|
||||||
|
API facilities.
|
||||||
|
|
||||||
|
Create a couple of Places::
|
||||||
|
|
||||||
|
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
|
||||||
|
>>> p1.save()
|
||||||
|
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
|
||||||
|
>>> p2.save()
|
||||||
|
|
||||||
|
Create a Restaurant. Pass the ID of the "parent" object as this object's ID::
|
||||||
|
|
||||||
|
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
|
||||||
|
>>> r.save()
|
||||||
|
|
||||||
|
A Restaurant can access its place::
|
||||||
|
|
||||||
|
>>> r.place
|
||||||
|
<Place: Demon Dogs the place>
|
||||||
|
|
||||||
|
A Place can access its restaurant, if available::
|
||||||
|
|
||||||
|
>>> p1.restaurant
|
||||||
|
<Restaurant: Demon Dogs the restaurant>
|
||||||
|
|
||||||
|
p2 doesn't have an associated restaurant::
|
||||||
|
|
||||||
|
>>> p2.restaurant
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
DoesNotExist: Restaurant matching query does not exist.
|
||||||
|
|
||||||
|
Set the place using assignment notation. Because place is the primary key on
|
||||||
|
Restaurant, the save will create a new restaurant::
|
||||||
|
|
||||||
|
>>> r.place = p2
|
||||||
|
>>> r.save()
|
||||||
|
>>> p2.restaurant
|
||||||
|
<Restaurant: Ace Hardware the restaurant>
|
||||||
|
>>> r.place
|
||||||
|
<Place: Ace Hardware the place>
|
||||||
|
|
||||||
|
Set the place back again, using assignment in the reverse direction::
|
||||||
|
|
||||||
|
>>> p1.restaurant = r
|
||||||
|
>>> p1.restaurant
|
||||||
|
<Restaurant: Demon Dogs the restaurant>
|
||||||
|
|
||||||
|
Restaurant.objects.all() just returns the Restaurants, not the Places. Note
|
||||||
|
that there are two restaurants - Ace Hardware the Restaurant was created in the
|
||||||
|
call to r.place = p2::
|
||||||
|
|
||||||
|
>>> Restaurant.objects.all()
|
||||||
|
[<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]
|
||||||
|
|
||||||
|
Place.objects.all() returns all Places, regardless of whether they have
|
||||||
|
Restaurants::
|
||||||
|
|
||||||
|
>>> Place.objects.order_by('name')
|
||||||
|
[<Place: Ace Hardware the place>, <Place: Demon Dogs the place>]
|
||||||
|
|
||||||
|
You can query the models using :ref:`lookups across relationships <lookups-that-span-relationships>`::
|
||||||
|
|
||||||
|
>>> Restaurant.objects.get(place=p1)
|
||||||
|
<Restaurant: Demon Dogs the restaurant>
|
||||||
|
>>> Restaurant.objects.get(place__pk=1)
|
||||||
|
<Restaurant: Demon Dogs the restaurant>
|
||||||
|
>>> Restaurant.objects.filter(place__name__startswith="Demon")
|
||||||
|
[<Restaurant: Demon Dogs the restaurant>]
|
||||||
|
>>> Restaurant.objects.exclude(place__address__contains="Ashland")
|
||||||
|
[<Restaurant: Demon Dogs the restaurant>]
|
||||||
|
|
||||||
|
This of course works in reverse::
|
||||||
|
|
||||||
|
>>> Place.objects.get(pk=1)
|
||||||
|
<Place: Demon Dogs the place>
|
||||||
|
>>> Place.objects.get(restaurant__place__exact=p1)
|
||||||
|
<Place: Demon Dogs the place>
|
||||||
|
>>> Place.objects.get(restaurant=r)
|
||||||
|
<Place: Demon Dogs the place>
|
||||||
|
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
|
||||||
|
<Place: Demon Dogs the place>
|
||||||
|
|
||||||
|
Add a Waiter to the Restaurant::
|
||||||
|
|
||||||
|
>>> w = r.waiter_set.create(name='Joe')
|
||||||
|
>>> w.save()
|
||||||
|
>>> w
|
||||||
|
<Waiter: Joe the waiter at Demon Dogs the restaurant>
|
||||||
|
|
||||||
|
Query the waiters::
|
||||||
|
|
||||||
|
>>> Waiter.objects.filter(restaurant__place=p1)
|
||||||
|
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
|
||||||
|
>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
|
||||||
|
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
|
|
@ -19,3 +19,4 @@ model maps to a single database table.
|
||||||
multi-db
|
multi-db
|
||||||
tablespaces
|
tablespaces
|
||||||
optimization
|
optimization
|
||||||
|
examples/index
|
||||||
|
|
|
@ -18,13 +18,6 @@ The basics:
|
||||||
* With all of this, Django gives you an automatically-generated
|
* With all of this, Django gives you an automatically-generated
|
||||||
database-access API; see :doc:`/topics/db/queries`.
|
database-access API; see :doc:`/topics/db/queries`.
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
A companion to this document is the `official repository of model
|
|
||||||
examples`_. (In the Django source distribution, these examples are in the
|
|
||||||
``tests/modeltests`` directory.)
|
|
||||||
|
|
||||||
.. _official repository of model examples: https://code.djangoproject.com/browser/django/trunk/tests/modeltests
|
|
||||||
|
|
||||||
Quick example
|
Quick example
|
||||||
=============
|
=============
|
||||||
|
@ -326,9 +319,6 @@ whatever you want. For example::
|
||||||
For details on accessing backwards-related objects, see the
|
For details on accessing backwards-related objects, see the
|
||||||
:ref:`Following relationships backward example <backwards-related-objects>`.
|
:ref:`Following relationships backward example <backwards-related-objects>`.
|
||||||
|
|
||||||
For sample code, see the `Many-to-one relationship model tests`_.
|
|
||||||
|
|
||||||
.. _Many-to-one relationship model tests: https://code.djangoproject.com/browser/django/trunk/tests/modeltests/many_to_one
|
|
||||||
|
|
||||||
Many-to-many relationships
|
Many-to-many relationships
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -376,10 +366,6 @@ form would let users select the toppings.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
See the `Many-to-many relationship model example`_ for a full example.
|
|
||||||
|
|
||||||
.. _Many-to-many relationship model example: https://code.djangoproject.com/browser/django/trunk/tests/modeltests/many_to_many/models.py
|
|
||||||
|
|
||||||
:class:`~django.db.models.ManyToManyField` fields also accept a number of extra
|
:class:`~django.db.models.ManyToManyField` fields also accept a number of extra
|
||||||
arguments which are explained in :ref:`the model field reference
|
arguments which are explained in :ref:`the model field reference
|
||||||
<manytomany-arguments>`. These options help define how the relationship should
|
<manytomany-arguments>`. These options help define how the relationship should
|
||||||
|
@ -569,10 +555,6 @@ can be made; see :ref:`the model field reference <ref-onetoone>` for details.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
See the `One-to-one relationship model example`_ for a full example.
|
|
||||||
|
|
||||||
.. _One-to-one relationship model example: https://code.djangoproject.com/browser/django/trunk/tests/modeltests/one_to_one/models.py
|
|
||||||
|
|
||||||
:class:`~django.db.models.OneToOneField` fields also accept one optional argument
|
:class:`~django.db.models.OneToOneField` fields also accept one optional argument
|
||||||
described in the :ref:`model field reference <ref-onetoone>`.
|
described in the :ref:`model field reference <ref-onetoone>`.
|
||||||
|
|
||||||
|
|
|
@ -483,6 +483,8 @@ probably use:
|
||||||
Again, this only scratches the surface. A complete reference can be found in the
|
Again, this only scratches the surface. A complete reference can be found in the
|
||||||
:ref:`field lookup reference <field-lookups>`.
|
:ref:`field lookup reference <field-lookups>`.
|
||||||
|
|
||||||
|
.. _lookups-that-span-relationships:
|
||||||
|
|
||||||
Lookups that span relationships
|
Lookups that span relationships
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue