1대 다 관계는 ForeignKey, 다대 다 관계는 ManyToManyField
다음과 같은 models.py를 살펴보면 host와 root_type를 각각 ForeignKey, ManyToManyField로 연결한 것을 볼 수 있습니다. 첫번째 인자로 연결할 model을 지정한 것을 볼 수 있습니다.
from django.db import models
from core import models as core_models
from django_countries.fields import CountryField
from users import models as user_models
class AbstractItem(core_models.TimeStampedModel):
""" Abtract Item """
name = models.CharField(max_length=80)
class Meta:
abstract = True
def __str__(self):
return self.name
class RoomType(AbstractItem):
"""RoomType Model Definition"""
class Meta:
verbose_name = "Room Type"
class Amenity(AbstractItem):
"""Amenity Model Definition"""
class Meta:
verbose_name_plural = "Amenities"
class Facility(AbstractItem):
"""A Facility Model Definition"""
pass
class HouseRule(AbstractItem):
"""A Rule Model Definition"""
class Meta:
verbose_name = "House Rule"
class Room(core_models.TimeStampedModel):
"""Room Model Definition"""
name = models.CharField(max_length=140)
description = models.TextField()
country = CountryField()
city = models.CharField(max_length=80)
price = models.IntegerField()
address = models.CharField(max_length=140)
guests = models.IntegerField()
beds = models.IntegerField()
bedrooms = models.IntegerField()
baths = models.IntegerField()
check_in = models.TimeField()
check_out = models.TimeField()
instant_book = models.BooleanField(default=False)
# 1대 다 관계. 하나의 User에 여러개의 Room이 있음을 의미.
# on_delete는 User가 삭제되었을 때 무엇을 해야 하는 지를 지정합니다.
# CASCADE는 User와 연결된 Room도 삭제하라는 의미입니다.
host = models.ForeignKey(user_models.User, on_delete=models.CASCADE)
room_type = models.ForeignKey(
RoomType, on_delete=models.SET_NULL, null=True)
# 다대 다 관계. 여러 Amenity가 여러 Room에 있을 수 있음을 의미
# 현재 Room에 관계를 설정했는데 Amenity 모델에 똑같은 관계 설정을 해줄 필요는 없다.
amenity = models.ManyToManyField(Amenity)
facility = models.ManyToManyField(Facility)
house_rules = models.ManyToManyField(HouseRule)
def __str__(self):
return self.name
🐱👤 왜 __str__(self): ...?
이후 어드민 패널에서 리스트를 등록하면 없어질 이름처럼 보이겠지만, 상단 부분이나 콘솔을 찍을 때 나오기 때문입니다. 귀찮아도 꼭 해줍시다.
🚀 ForeignKey
ForeignKey의 Arguments에 대해서 조금만 알아봅시다.
🐱👤 on_delete
on_delete는 연결한 model이 삭제되었을 때 어떤 행동을 해야할 지에 대해서 지정하는 것입니다. 당연히 다대 다 관계로 연결된 곳에서는 사용할 수 없습니다. 필수로 지정해야 하므로 하지 않으면 오류를 냅니다.
✔ CASCADE
공식 문서에 따르면 Cascade deletes. Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.라고 합니다. 다 지워버린다는 의미죠.
이 경우 User(1)가 삭제되면 Room(다) 도 삭제된다는 의미입니다.
반면 Room을 지운다고 해서 User가 지워지지 않습니다. 상식적으로, 1대 다의 관계인데 "다"에 속하는 Room을 지웠다고 1에 해당하는 User를 지운다는 것은 말이 되지 않습니다.
✔ PROTECT
User를 지워도 Room이 삭제되지 않지만 Room은 연결된 User가 없는 고아가 됩니다. 이 경우 ProtectedError 오류를 일으키기도 합니다.
✔ SET_NULL, SET_DEFAULT
Set the ForeignKey null; this is only possible if null is True. User를 지우면 Room은 null과 연결됩니다. null=True를 허용했다면 말이죠
다음과 같은 코드의 경우, RoomType이 삭제가 되면 null 값이 지정됩니다.
root_type = models.ForeignKey(RoomType, on_delete=models.SET_NULL, null=True)
반면 SET_DEFAULT는 설정한 default 값으로 연결됩니다. Set the ForeignKey to its default value; a default for the ForeignKey must be set.
🐱👤 ""로 모델 감싸기
또, 본질적으로 모델 연결과 연관된 이야기는 아니지만, python은 위에서 아래로 차례대로 코드를 읽어가기 때문에 종종 연결하는 모델의 순서를 중요하게 설정해야 합니다.
그러나 모델의 갯수가 많아지다보면 신경 쓰는 것에도 한계가 있습니다. 이럴 때는 모델의 ""로 묶어 String화 해줍시다.
다른 app에 있는 모델은 import할 필요 없이 "app.model" 형태로 쓰면 됩니다.
class Photo(core_models.TimeStampedModel):
"""Photo Model Definition"""
caption = models.CharField(max_length=80)
file = models.FileField()
# 아래 코드는 Room 모델 아래에 정의해야 오류가 안 납니다.
room = models.ForeignKey(Room, on_delete=models.CASCADE)
# string으로 묶어주면 알아서 모델을 찾아서 연결합니다.
room = models.ForeignKey("Room", on_delete=models.CASCADE)
def __str__(self):
return self.caption
🐱👤 🤍 쿼리 없이 연결된 모델의 연관관계를 타고 가기
해당 모델은 review-room-user로 연결되어 있으므로 다음과 같이 연쇄적인 흐름을 타고 값을 가져올 수 있습니다.
__str__ 정의 부분을 보시면 self.room.host.birthday를 가져온 것을 볼 수 있습니다. 이는 현 모델인 Review에서 room, Room에서 host 필드를 통해 User 모델의 birthday 필드를 가져온 것입니다. 믿음의 벨트(?)
아주 편리합니다~~
from django.db import models
from core import models as core_models
class Review(core_models.TimeStampedModel):
"""Review Model Definition"""
... 중략
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
room = models.ForeignKey("rooms.Room", on_delete=models.CASCADE)
def __str__(self):
return self.room.host.birthday
🚀 ManyToManyField
그냥 쓰시면 됩니다. 네.
모델간의 상관관계와는 별 상관 없지만 어드민 패널에서 확인하기 위해 admin.py를 다음과 같이 작성해주었습니다.
from django.contrib import admin
from . import models
@admin.register(models.RoomType, models.Facility, models.Amenity, models.HouseRule)
class ItemAdmin(admin.ModelAdmin):
""" Item Admin Definition """
pass
@admin.register(models.Photo)
class PhotoAdmin(admin.ModelAdmin):
""" Photo Admin Definition """
pass
# Register your models here.
@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
""" Room Admin Definition """
pass
1대 다 관계와 다대 다 관계는 패널에서 다음과 같이 보입니다.
'Django, Flask > 🔫 Django' 카테고리의 다른 글
QuerySet 활용하기, related_name (0) | 2020.05.12 |
---|---|
Admin panel Customizing (further) (0) | 2020.05.12 |
core를 이용하여 반복되는 Abstract model 설계 및 활용 (0) | 2020.05.12 |
User Admin panel Customizing (0) | 2020.05.11 |
Model field/Meta 등을 이용해 model 설계하기 (0) | 2020.05.11 |