Django one-to-one relationships

2015-06-16

Many-to-one relationships

一对一关系,一对一关系应该使用来分离抽象模型的,不让一张表里面的字段那么多,那么乱。

models.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.db import models

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)

def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name

class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)

def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name

class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
name = models.CharField(max_length=50)

def __str__(self): # __unicode__ on Python 2
return "%s the waiter at %s" % (self.name, self.restaurant)

Restaurant 表中的 place 字段是 和 Place 表是一对一的关系,且手动设置成了主键,一家餐厅有一个地址,这样把模型都分开来,虽然查询的时候可能比较麻烦,但这样分开来能更好的抽象模型的关系,我的应用里面也能够这么做,一个表中的字段太多,有的能够分离到一对一的关系中,也更好理解些。

python manage.py shell API 用法

实例化 Place 对象

1
2
3
4
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()

实例化 Restaurant 对象,将前面实例化好的的 Place 对象绑定上去

1
2
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()

通过 Restaurant 获取其地点

1
2
>>> r.place
<Place: Demon Dogs the place>

通过 Place 获取其绑定的 Restaurant

1
2
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurane>

p2 没有绑定任何 Restaurant 对象

1
2
3
4
5
6
>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>> p2.restaurant
>>> except ObjectDoesNotExist:
>>> print("There is no restaurant here.")
There is no restaurant here.

使用 hasattr 避免捕捉异常

1
2
>>> hasattr(p2, ‘restaurant’)
False

重新绑定 Place 对象到 Restaurant

1
2
3
4
5
6
>>> r.place = p2
>>> e.save()
>>> p2.restaurant
<Restaurant: Ace Hardware the restaurant>
>>> r.place
<Place: Ace Hardware the place>

r 的地点原来是 p1,重新绑定成 p2,验证已经变成了 p2 的地址。
重新设置回来:

1
2
3
>>> p1.restaurant = r
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>

不能绑定一个没有保存到数据库中的对象到另一个对象中,否则会触发 ValueError

1
2
3
4
5
6
7
8
9
>>> p3 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> Restaurant(place=p3, serves_hot_dogs=True, serves_pizza=False)
Traceback (most recent call last):
...
ValueError: 'Cannot assign "<Place: Demon Dogs>": "Place" instance isn't saved in the database.'
>>> p.restaurant = Restaurant(place=p, serves_hot_dogs=True, serves_pizza=False)
Traceback (most recent call last):
...
ValueError: 'Cannot assign "<Restaurant: Demon Dogs the restaurant>": "Restaurant" instance isn't saved in the database.'

这上面的错误其实有两个,第一是 p3 实例化没有保存,然后就是 第二行完全没来由,没有实例化任何 Restaurant 对象就要把 p3 绑定上去,触发两个 ValueError 异常。

Django 1.8 的改变:
前面也好几处提过了,绑定未保存的对象将会静默数据丢失,不会引发任何异常。

其他用法就是增删改差了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> Restaurant.objects.all()
[<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]

>>> Place.objects.order_by('name')
[<Place: Ace Hardware the place>, <Place: Demon Dogs the place>]

>>> Restaurant.objects.get(place=p1)
>>> Restaurant.objects.get(place__pk=1)
>>> Restaurant.objects.filter(place__name__startswith="Demon")
>>> Restaurant.objects.exclude(place__address__contains="Ashland")

>>> w = r.waiter_set.create(name='Joe')
>>> w.save()
>>> w
<Waiter: Joe the waiter at Demon Dogs the restaurant>
>>> 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>]