상당히 긴 문서입니다. model을 디자인하는데 유용하게 사용되므로 작성할 때 종종 와서 찾아 보는 것이 좋습니다.
하나씩 설명하기보다, 디자인한 모델을 가져와서 훗날 재활용 및 참고하는 게 더 적절해 보입니다.
우선 기본적인 사항들을 정리했습니다.
- ImageField를 사용하려면 Pillow를 설치해야 합니다. 필자는 pipenv 가상환경을 사용하고 있으므로 pipenv install Pillow로 설치했습니다.
- blank는 required의 문제이며 null은 DB에 빈 값을 허용하는 지의 문제입니다. 따라서, null=True만 설정하고 빈 값으로 제출하려고 하면 required라며 팅겨냅니다.
- blank 설정이 안되어 있으면 required이므로 모델에서 볼드체로 표시됩니다.
- CharField의 choices를 통해 셀렉트 박스를 만들 수 있습니다.
class User(AbstractUser):
""" Custom User model """
GENDER_MALE = "male"
GENDER_FEMAIL = "female"
GENDER_OTHER = "other"
# 첫 인자는 DB에 저장되고 두번째 인자는 form에 보여집니다
# (actual value, human readable name)
GENDER_CHOICES = (
(GENDER_MALE, "Male"),
(GENDER_FEMAIL, "Female"),
(GENDER_OTHER, "Other"),
)
LANGUAGE_CHOICES = (
("en", "English"),
("kr", "Korean"),
)
CURRENCY_CHOICES = (
("usd", "USD"),
("krw", "KRW"),
)
# ImageField는 Pillow라는 패키지를 설치해야 합니다. multer 같은 개념입니다.
avatar = models.ImageField(null=True, blank=True)
# CharField는 max_length가 필수값이며 choices로 select box를 만들 수 있습니다
# max_length은 DB 레벨에서의 제한입니다. 사용자에게 보여지는 문자열의 제한이 아닙니다.
gender = models.CharField(choices=GENDER_CHOICES,
max_length=10, default=GENDER_MALE,
null=True, blank=True)
# TextField
bio = models.TextField(default="", blank=True)
# DateField는 달력입니다.
birthday = models.DateField(null=True, blank=True)
language = models.CharField(
choices=LANGUAGE_CHOICES, default="en", max_length=2, null=True, blank=True)
currency = models.CharField(
choices=CURRENCY_CHOICES, max_length=6, default="usd", null=True, blank=True)
# BooleanField는 단순한 버튼입니다
superhost = models.BooleanField(default=False)
Abstract Model을 만들어 보았습니다. abstract 속성을 True로 두기만 하면 됩니다.
Meta에 속성에 대해서는 다음 문서를 참고합시다. (3.0 버전) 생각보다 짧아서 금방 익힐 수 있습니다.
- auto_now 는 객체가 저장될 때마다 타임스탬프를 찍습니다.(updatedAt)
- auto_now_add는 객체가 첫번째 생성때만 타임스탬프를 찍습니다.(createdAt)
- 이 속성은 TimeField, DateField, TimeDateField 모두 가지고 있는 속성입니다.(https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.DateField)
from django.db import models
# Create your models here.
class TimeStampedModel(models.Model):
"""This is TimeStamped Model"""
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
# abstract가 True면 DB에는 등록되지 않습니다.
class Meta:
abstract = True
IntergerField, TimeField를 눈 여겨 볼만 합니다.
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)
host = models.ForeignKey(user_models.User, on_delete=models.CASCADE)
여기서 DateField, DateTimeField, TimeField의 차이를 알아봅시다.
왼쪽이 TimeField 오른쪽이 DateField입니다.
DateTimeField는 둘을 섞어서 날짜와 시간을 동시에 지정할 수 있는 field입니다.
Meta 클래스를 이용해서 verbose_name과 ordering을 수정할 수 있습니다.
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
# Create your models here.
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"
ordering = ['created']
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에 있을 수 있음을 의미
amenity = models.ManyToManyField(Amenity)
facility = models.ManyToManyField(Facility)
house_rules = models.ManyToManyField(HouseRule)
def __str__(self):
return self.name
ManyToManyField로 설정하면 신기하게도 패널에서 자동으로 (s)를 붙여서 보여줍니다.
class Amenity(AbstractItem):
"""Amenity Model Definition"""
pass
amenity = models.ManyToManyField(Amenity)
다음과 같이 단수로 적어줬음에도(s)가 붙는 것을 볼 수 있습니다. 그러나, amenities가 맞는 표현입니다.
따라서 다음과 같이 메타 클래스를 통해 수작업으로 설정해주도록 하겠습니다. (verbose_name_plural)
class Amenity(AbstractItem):
"""Amenity Model Definition"""
class Meta:
verbose_name_plural = "Amenities"
amenity = models.ManyToManyField(Amenity)
verbose_name을 사용할 수도 있습니다. 이 경우 자동으로 s를 붙여주게 됩니다.
사실 귀찮아서 그냥 verbose_name_plural로 통일하는 경우가 많습니다.
class HouseRule(AbstractItem):
"""A Rule Model Definition"""
class Meta:
verbose_name = "House Rule"
ordering을 통해 해당 모델 내부의 요소들을 정렬할 수도 있습니다.
(https://docs.djangoproject.com/en/3.0/ref/models/options/#ordering)
주의할 점도 보이네요. Ordering is not a free operation. Each field you add to the ordering incurs a cost to your database.
class RoomType(AbstractItem):
"""RoomType"""
class Meta:
ordering = ["created"]
내부의 요소를 기준으로 ordering을 할 수 있으므로 저는 created를 기준으로 순서를 배치해봤습니다.
'Django, Flask > 🔫 Django' 카테고리의 다른 글
ForeignKey, ManyToManyField로 모델 연결하기 (0) | 2020.05.12 |
---|---|
core를 이용하여 반복되는 Abstract model 설계 및 활용 (0) | 2020.05.12 |
User Admin panel Customizing (0) | 2020.05.11 |
Extending the existing User model (built-in User 모델을 대체하기) (0) | 2020.05.11 |
Django Start! / config/settings.py 살펴보기 (0) | 2020.05.11 |