one_to_one.txt 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. ########################
  2. One-to-one relationships
  3. ########################
  4. .. highlight:: pycon
  5. To define a one-to-one relationship, use :ref:`ref-onetoone`.
  6. In this example, a ``Place`` optionally can be a ``Restaurant``:
  7. .. code-block:: python
  8. from django.db import models
  9. class Place(models.Model):
  10. name = models.CharField(max_length=50)
  11. address = models.CharField(max_length=80)
  12. def __str__(self): # __unicode__ on Python 2
  13. return "%s the place" % self.name
  14. class Restaurant(models.Model):
  15. place = models.OneToOneField(Place, primary_key=True)
  16. serves_hot_dogs = models.BooleanField(default=False)
  17. serves_pizza = models.BooleanField(default=False)
  18. def __str__(self): # __unicode__ on Python 2
  19. return "%s the restaurant" % self.place.name
  20. class Waiter(models.Model):
  21. restaurant = models.ForeignKey(Restaurant)
  22. name = models.CharField(max_length=50)
  23. def __str__(self): # __unicode__ on Python 2
  24. return "%s the waiter at %s" % (self.name, self.restaurant)
  25. What follows are examples of operations that can be performed using the Python
  26. API facilities.
  27. Create a couple of Places::
  28. >>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
  29. >>> p1.save()
  30. >>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
  31. >>> p2.save()
  32. Create a Restaurant. Pass the ID of the "parent" object as this object's ID::
  33. >>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
  34. >>> r.save()
  35. A Restaurant can access its place::
  36. >>> r.place
  37. <Place: Demon Dogs the place>
  38. A Place can access its restaurant, if available::
  39. >>> p1.restaurant
  40. <Restaurant: Demon Dogs the restaurant>
  41. p2 doesn't have an associated restaurant::
  42. >>> from django.core.exceptions import ObjectDoesNotExist
  43. >>> try:
  44. >>> p2.restaurant
  45. >>> except ObjectDoesNotExist:
  46. >>> print("There is no restaurant here.")
  47. There is no restaurant here.
  48. You can also use ``hasattr`` to avoid the need for exception catching::
  49. >>> hasattr(p2, 'restaurant')
  50. False
  51. Set the place using assignment notation. Because place is the primary key on
  52. Restaurant, the save will create a new restaurant::
  53. >>> r.place = p2
  54. >>> r.save()
  55. >>> p2.restaurant
  56. <Restaurant: Ace Hardware the restaurant>
  57. >>> r.place
  58. <Place: Ace Hardware the place>
  59. Set the place back again, using assignment in the reverse direction::
  60. >>> p1.restaurant = r
  61. >>> p1.restaurant
  62. <Restaurant: Demon Dogs the restaurant>
  63. Note that you must save an object before it can be assigned to a one-to-one
  64. relationship. For example, creating an ``Restaurant`` with unsaved ``Place``
  65. raises ``ValueError``::
  66. >>> p3 = Place(name='Demon Dogs', address='944 W. Fullerton')
  67. >>> Restaurant(place=p3, serves_hot_dogs=True, serves_pizza=False)
  68. Traceback (most recent call last):
  69. ...
  70. ValueError: 'Cannot assign "<Place: Demon Dogs>": "Place" instance isn't saved in the database.'
  71. >>> p.restaurant = Restaurant(place=p, serves_hot_dogs=True, serves_pizza=False)
  72. Traceback (most recent call last):
  73. ...
  74. ValueError: 'Cannot assign "<Restaurant: Demon Dogs the restaurant>": "Restaurant" instance isn't saved in the database.'
  75. .. versionchanged:: 1.8
  76. Previously, assigning unsaved objects did not raise an error and could
  77. result in silent data loss.
  78. Restaurant.objects.all() just returns the Restaurants, not the Places. Note
  79. that there are two restaurants - Ace Hardware the Restaurant was created in the
  80. call to r.place = p2::
  81. >>> Restaurant.objects.all()
  82. [<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]
  83. Place.objects.all() returns all Places, regardless of whether they have
  84. Restaurants::
  85. >>> Place.objects.order_by('name')
  86. [<Place: Ace Hardware the place>, <Place: Demon Dogs the place>]
  87. You can query the models using :ref:`lookups across relationships <lookups-that-span-relationships>`::
  88. >>> Restaurant.objects.get(place=p1)
  89. <Restaurant: Demon Dogs the restaurant>
  90. >>> Restaurant.objects.get(place__pk=1)
  91. <Restaurant: Demon Dogs the restaurant>
  92. >>> Restaurant.objects.filter(place__name__startswith="Demon")
  93. [<Restaurant: Demon Dogs the restaurant>]
  94. >>> Restaurant.objects.exclude(place__address__contains="Ashland")
  95. [<Restaurant: Demon Dogs the restaurant>]
  96. This of course works in reverse::
  97. >>> Place.objects.get(pk=1)
  98. <Place: Demon Dogs the place>
  99. >>> Place.objects.get(restaurant__place=p1)
  100. <Place: Demon Dogs the place>
  101. >>> Place.objects.get(restaurant=r)
  102. <Place: Demon Dogs the place>
  103. >>> Place.objects.get(restaurant__place__name__startswith="Demon")
  104. <Place: Demon Dogs the place>
  105. Add a Waiter to the Restaurant::
  106. >>> w = r.waiter_set.create(name='Joe')
  107. >>> w.save()
  108. >>> w
  109. <Waiter: Joe the waiter at Demon Dogs the restaurant>
  110. Query the waiters::
  111. >>> Waiter.objects.filter(restaurant__place=p1)
  112. [<Waiter: Joe the waiter at Demon Dogs the restaurant>]
  113. >>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
  114. [<Waiter: Joe the waiter at Demon Dogs the restaurant>]