classPlace(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) def__str__(self): return"%s the place" % self.name classRestaurant(models.Model): # OneToOneField 의 첫번째 인자로, Place 클래스를 받음 place = models.OneToOneField(Place, on_delete=models.CASCADE, primary_key=True) serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False) def__str__(self): return"%s the restaurant" % self.place.name classWaiter(models.Model): restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE) name = models.CharField(max_length=50) def__str__(self): return"%s the waiter at %s" % (self.name, self.restaurant)
위의 예시 모델에서, 하나의 장소 Place 에는 하나의 Restaurant 이 존재하고, 이는 OneToOneField 로 연결되어 있습니다. 하나의 Restaurant 에는 ForeignKey 로 연결되어 있는, Waiter 라는 모델이 존재합니다.
이는, 하나의 Restaurant 에 일하는 웨이터가 여러명 있을수 있는 관계를 모델링 한것입니다.
OneToOneField 는 선택 인자로 parent_link 를 받습니다.
Python API 사용해보기
장소 (Place) 2 곳을 생성해 줍니다.
1 2 3 4 5 6 7
In [1]: p1 = Place(name="명랑핫도그", address="성남시 분당구 정자1동")
In [2]: p1.save()
In [3]: p2 = Place(name="피자헛", address="성남시 분당구 금곡동 불정로 87")
In [4]: p2.save()
식당을 생성해 주고, 부모 객체를 해당 객체의 primary key 로 전달해줍니다.
1 2 3
In [5]: r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
In [6]: r.save()
r 은 Restaurant 의 인스턴스 이니까, 하나의 Restaurant 인스턴스는, 해당 장소에 접근할수 있습니다.
1 2
In [7]: r.place Out[7]: <Place: 명랑핫도그 the place>
반대로, 하나의 Place 는 restaurant 에 접근할수 있습니다.
1 2
In [8]: p1.restaurant Out[8]: <Restaurant: 명랑핫도그 the restaurant>
p2 는 연결된 restaurant 이 아직 없습니다.
따라서, 아래 코드를 shell 에서 실행 하면, p2 에는 restaurant 이 없다고 나옵니다
1 2 3 4 5 6 7 8 9 10
In [9]: from django.core.exceptions import ObjectDoesNotExist
In [10]: try: ...: p2.restaurant ...: except ObjectDoesNotExist: ...: print("There is no restaurant here.") ...: # Shift + Enter 를 실행하면, 실행됨.
There is no restaurant here.
hasattr 메서드를 사용하여, 예외처리를 하지 않아도 됩니다.
hasattr 는 파이썬 내장함수 입니다.
객체가 주어진 이름의 어트리뷰트를 가지고 있을때 True 를 반환하고, 그렇지 않을때에 False 를 반환합니다.
hasatt(object, name)
1 2
In [11]: hasattr(p2, 'restaurant') Out[11]: False
할당 기호 (=) 를 사용하여, place 를 설정해 줍니다. place 는 Restaurant 의 primary key 이므로, save 는 새로운 restaurant 을 생성합니다
1 2 3 4 5 6 7 8 9 10 11 12
# 위에서 r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False) # r.place = p2 로 바꿔줍니다.
In [12]: r.place = p2 In [13]: r.save()
In [14]: p2.restaurant Out[14]: <Restaurant: 피자헛 the restaurant>
In [15]: r.place Out[15]: <Place: 피자헛 the place>
Place 를 다시 역으로 바꿔줍니다.
1 2 3 4
In [16]: p1.restaurant = r
In [17]: p1.restaurant Out[17]: <Restaurant: 명랑핫도그 the restaurant>
One-To-One 관계가 할당 되기 이전에
객체가 저장 되어 있어야 ValueError 가 나질 않습니다.
1 2 3 4 5 6 7
In [18]: p3 = Place(name='Daemon Dogs', address='944 W. Fullerton')
In [19]: Restaurant.objects.create(place=p3, serves_hot_dogs=True, serves_pizza=False) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-19-18b4cdec5239> in <module> ----> 1 Restaurant.objects.create(place=p3, serves_hot_dogs=True, serves_pizza=False)
Restaurant.objects.all() 은 Restaurants 만 반환합니다. Place 는 반환하지 않습니다.
Place.objects.all() 은 Places 들을 반환합니다. Restaurant을 가지고 있던 안있던, 결과를 반환합니다.
1 2 3 4 5
In [20]: Restaurant.objects.all() Out[20]: <QuerySet [<Restaurant: 명랑핫도그 the restaurant>, <Restaurant: 피자헛 the restaurant>]>
In [21]: Place.objects.order_by('name') Out[21]: <QuerySet [<Place: 명랑핫도그 the place>, <Place: 피자헛 the place>]>
관계간 룩업들을 사용하여, 해당 모델에 쿼리문을 작성할수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
In [22]: Restaurant.objects.get(place=p1) Out[22]: <Restaurant: 명랑핫도그 the restaurant>
In [23]: Restaurant.objects.get(place__pk=1) Out[23]: <Restaurant: 명랑핫도그 the restaurant>
In [24]: Restaurant.objects.filter(place__name__startswith="명") Out[24]: <QuerySet [<Restaurant: 명랑핫도그 the restaurant>]>
In [25]: Restaurant.objects.exclude(place__address__contains="헛") Out[25]: <QuerySet [<Restaurant: 명랑핫도그 the restaurant>, <Restaurant: 피자헛 the restaurant>]>