1. Django Tutorial(Airbnb) - ForeignKey & ManyToManyField

🌈 ForeignKey & ManyToManyField

🔥 ForeignKey

🔥 ManyToManyField

🔥 Sting 형태로 테이블 연결

🔥 set을 통한 접근 & related_name


1. ForeignKey

  • ForeignKey는 한 테이블의 필드를 기준으로 다른 모델의 테이블을 연결시키는 기능으로 ForeignKey가 포함된 테이블을 '자식 테이블'이고, ForeignKey를 통해 참조할 테이블을 '부모 테이블'이라 해요.
  • ForeignKey는 '자식 테이블'의 필드 값이 '부모 테이블'의 Primary Key를 참조함으로써 일대다 관계로(many-to-one) 테이블을 연결하는데, '부모 테이블'의 한 object가 자신을 참조하는 '자식 테이블'의 여러 object와 연동되어 DB를 효과적으로 관리할 수 있어요.

1) ForeignKey 란?

  • 아래 코드를 보면, 여러개의 객실 정보를 저장하는 Room 테이블의 'host' 필드가 User 테이블의 Primary Key를 참조 함으로써 Room 테이블의 object들이 어떤 사용자와 연결 지을지 결정합니다.
  • ForeignKey 사용 방법
    • 🔎 필드명 = ForeignKey(참조할 모델, on_delete="")
  • 아래 코드에서 host 필드는 User 테이블을 ForeignKey로 참조하고 있고, room_type 필드는 같은 Model에 있는 RoomType 테이블을 참조하고 있어요.
from django.db import models
from django_countries.fields import CountryField
from core import models as core_models
from users import models as user_models
# Create your models here.
class AbstractItem(core_models.TimeStampedModel):
    """AbstractItem Object Definition"""
    name = models.CharField(max_length=80)
    class Meta:
        abstract = True
    def __str__(self):
        return self.name
class RoomType(AbstractItem):
    """Room Type Model Definition"""
    pass
class Room(core_models.TimeStampedModel):
    """Room Model Definifion"""
    name = models.CharField(max_length=140)
    description = models.TextField()
    city = models.CharField(max_length=80)
    country = CountryField()
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField()
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_out = models.TimeField()
    check_in = models.TimeField()
    instant_book = models.BooleanField(default=False)
    host = models.ForeignKey(user_models.User, on_delete=models.CASCADE) # 👈 ForeignKey
    room_type = models.ForeignKey(RoomType, on_delete=models.CASCADE, null=True) # 👈 ForeignKey
    def __str__(self):
        return self.name

2) on_delete 옵션

  • ForeignKey 통해서 생성된 객실이 어떤 한 사용자를 가르키고 있는데, 만일 사용자가 탈퇴를 한다면 해당 객실의 데이터를 어떻게할지 결정하는 것이 on_delete 옵션의 기능이에요.
  • CASCADE : 참조 대상인 부모(사용자)를 삭제하면, 참조하고 있는 자식(객실)을 자동으로 삭제
    • 🔎 on_delete=models.CASCADE
  • PROTECT : 참조 중인 자식(객실)을 모두 삭제하기 전에는 참조 대상인 부모(사용자)를 삭제 할 수 없도록 보호
    • 🔎 on_delete=models.PROTECT
  • SET_NULL : 참조 대상인 부모(사용자)를 삭제하면, 참조 중인 자식(객실)은 참조값 Null을 가지고 존재
    • 🔎 on_delete=models.SET_NULL
  • Default : 참조 대상인 부모(사용자)를 삭제하면, 참조 중인 자식(객실)은 설정한 Default값과 연결
  • 🔎 on_delete=models.SET_DEFAULT

2. ManyToManyField

1) ManyToManyField 란?

  • ManyToManyField는 다대다 관계(many-to-many)로 테이블을 연결하고자 할 때 사용해요.
  • 예를 들어, 1개의 게시글에 여러 개의 tag가 달릴 수 있고, 또한 어떤 한 tag는 여러 개의 게시글에서 사용될 때 사용해요.
  • ManyToManyField 관계를 필드를 생성하게 되면 관계 연결을 위한 중간 테이블(Intermediate Table)을 Django에서 자동으로 생성합니다.

2) room/models.py

  • ManyToManyFiel 사용 방법
    • 🔎 필드명 = ManyToManyField(참조할 모델, blank=True)
from django.db import models
from django_countries.fields import CountryField
from core import models as core_models
from users import models as user_models
# Create your models here.
class AbstractItem(core_models.TimeStampedModel):
    """AbstractItem Object Definition"""
    name = models.CharField(max_length=80)
    class Meta:
        abstract = True
    def __str__(self):
        return self.name
class RoomType(AbstractItem):
    """Room Type Model Definition"""
    pass
class Amenity(AbstractItem):
    """Amenity Model Definition"""
    pass
class Facility(AbstractItem):
    """Facility Model Definition"""
    pass
class HouseRule(AbstractItem):
    """HouseRule Model Definition"""
    pass
class Room(core_models.TimeStampedModel):
    """Room Model Definifion"""
    name = models.CharField(max_length=140)
    description = models.TextField()
    city = models.CharField(max_length=80)
    country = CountryField()
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField()
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_out = models.TimeField()
    check_in = models.TimeField()
    instant_book = models.BooleanField(default=False)
    host = models.ForeignKey(user_models.User, on_delete=models.CASCADE)
    room_type = models.ForeignKey(RoomType, on_delete=models.CASCADE, null=True)
    amenities = models.ManyToManyField(Amenity, blank=True) # 👈 ManyToManyField
    facilities = models.ManyToManyField(Facility, blank=True) # 👈 ManyToManyField
    house_rule = models.ManyToManyField(HouseRule, blank=True) # 👈 ManyToManyField
    def __str__(self):
        return self.name

3) room/admin.py

  • Admin Panel에 나타나게 하기 위해서는 아래처럼 admin 등록을 진행시켜주어야 하죠,, 이를 통합하여 작성할 수 도 있습니다.
from django.contrib import admin
from . import models
# Register your models here.
@admin.register(models.RoomType)
class ItemAdmin(admin.ModelAdmin):
    pass
@admin.register(models.Amenity)
class AmenityAdmin(admin.ModelAdmin):
    pass
@admin.register(models.Facility)
class FacilityAdmin(admin.ModelAdmin):
    pass
@admin.register(models.HouseRule)
class HouseRuleAdmin(admin.ModelAdmin):
    pass
@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
    pass
  • 단, 테이블이 비슷한 특징을 갖고 있고 추후 개별적인 Custom이 필요없을 때는 Admin을 통합하여 등록할 수 있어요:)
from django.contrib import admin
from . import models
# Register your models here.
@admin.register(models.RoomType, models.Amenity, models.Facility, models.HouseRule)
class ItemAdmin(admin.ModelAdmin):
    pass
@admin.register(models.Room) # 👈 별도의 Custom을 적용시킬 테이블을 통합시키지 않아요:)
class RoomAdmin(admin.ModelAdmin):
    pass


3. Sting 형태로 테이블 연결

  • Python은 코드를 위에서 아래로 방향으로 읽어나가기 때문에 참조할 Class가 아래 있으면 읽어오지 못한다는 문제가 있어요.
from django.db import models
from core import models as core_models
from users import models as user_models
# Create your models here.
class Photo(core_models.TimeStampedModel):
    """Photo Model Definition"""
    caption = models.CharField(max_length=80)
    file = models.ImageField()
    room = models.ForeignKey(Room, on_delete=models.CASCADE) # 👈 바로 아래 Room을 인식못함   
class Room(core_models.TimeStampedModel):
    """Room Model Definifion"""
    name = models.CharField(max_length=140)
    description = models.TextField()
    city = models.CharField(max_length=80)
    country = CountryField()
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField()
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_out = models.TimeField()
    check_in = models.TimeField()
    instant_book = models.BooleanField(default=False)
    host = models.ForeignKey(user_models.User, on_delete=models.CASCADE)
    room_type = models.ForeignKey(RoomType, on_delete=models.CASCADE, null=True)
    amenities = models.ManyToManyField(Amenity, blank=True)
    facilities = models.ManyToManyField(Facility, blank=True)
    house_rule = models.ManyToManyField(HouseRule, blank=True)
    def __str__(self):
        return self.name
  • 이런 경우에, string으로 변환하여 Model을 연결시키면 import를 하지 않아도 Django에서 해당 Class를 스스로 찾고, 참조할 Class가 아래 있더라도 Django에서 알아서 처리합니다:)
from django.db import models
from core import models as core_models
# from users import models as user_models # 👈 필요 없음
# Create your models here.
class Photo(core_models.TimeStampedModel):
    """Photo Model Definition"""
    caption = models.CharField(max_length=80)
    file = models.ImageField()
    room = models.ForeignKey("Room", on_delete=models.CASCADE) # 👈 string으로 연결
class Room(core_models.TimeStampedModel):
    """Room Model Definifion"""
    name = models.CharField(max_length=140)
    description = models.TextField()
    city = models.CharField(max_length=80)
    country = CountryField()
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField()
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_out = models.TimeField()
    check_in = models.TimeField()
    instant_book = models.BooleanField(default=False)
    # 👇 string으로 연결
    host = models.ForeignKey("users.User", on_delete=models.CASCADE)
    room_type = models.ForeignKey("RoomType", on_delete=models.CASCADE, null=True)
    amenities = models.ManyToManyField("Amenity", blank=True)
    facilities = models.ManyToManyField("Facility", blank=True)
    house_rule = models.ManyToManyField("HouseRule", blank=True)
    def __str__(self):
        return self.name

3. set을 통한 접근 & related_name

1) set을 통한 접근

  • ForeignKey로 어떤 테이블 가르킬 경우, 자식 테이블의 필드값으로 부모테이블로 접근이 가능해요!
  • 즉, ForeignKey로 부모 테이블를 참조중이기 때문에 해당 object의 모든 필드에 접근이 가능합니다.
  • _set은 반대로 그 object에서 자식 테이블의 필드에 접근하는 기능이에요.
  • Room 모델에 있는 host 필드(rooms/models.py)가 ForeignKey를 통해 User 테이블을 가르키면, User 모델에 각 object에서 "[모델명]_set"으로 자신을 가르키는 모든 element에 접근이 가능합니다.
  • 즉, _set는 자신을 참조하고 있는 object로 접근하는 기능이에요:)

2) related_name

  • 필드 속성으로 related_name에 값을 지정하면, _set으로 접근하는 대신 지정한 값으로 접근이 가능해요.
    • 🔎 host = models.ForeignKey("users.User", related_name="rooms", on_delete=models.CASCADE)
class Room(core_models.TimeStampedModel):
    """Room Model Definifion"""
    name = models.CharField(max_length=140)
    description = models.TextField()
    city = models.CharField(max_length=80)
    country = CountryField()
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField()
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_out = models.TimeField()
    check_in = models.TimeField()
    instant_book = models.BooleanField(default=False)
    host = models.ForeignKey(
        "users.User", related_name="rooms", on_delete=models.CASCADE
    )
    room_type = models.ForeignKey(
        "RoomType", related_name="rooms", on_delete=models.CASCADE, null=True
    )
    amenities = models.ManyToManyField("Amenity", related_name="rooms", blank=True)
    facilities = models.ManyToManyField("Facility", related_name="rooms", blank=True)
    house_rule = models.ManyToManyField("HouseRule", related_name="rooms", blank=True)
    def __str__(self):
        return self.name
  • ManyToManyField 또한 _set을 이용하여 자신을 가르키고있는 object로 접근할 수 있어요:)

좋은 웹페이지 즐겨찾기