나의개발일지
model @property 모델에 필드를 정의하지 않아도 된다고? 본문
본 글은 작성자가 어디선가 주워듣고 이해한 내용들을 개인적인 언어로 작성한 게시물입니다.
잘못된 내용이 존재할 수 있으니, 읽게 되신다면 이점을 감안해주세요!!!
- 우리는 결국 자신이 가진 이야기로 상대방을 이해할 수 있을 뿐이다.-
django로 새로운 프로젝트를 시작하고, 새로운 app을 만들면 자동으로 만들어지는 파일 중 하나는 models.py이다.
models.py에는 프로젝트에서 필요로 하는 데이터베이스 테이블과 그에 맞는 필드를 정의하는 공간으로 많이 사용된다.
나는 정말 말 그대로 model을 정의하는데 models.py를 이용해 왔다. 그러다가 프로젝트를 진행하면서 이럴 수 이럴 수 이럴수가!!!!!
누군가 내게 말했다.
"models.py는 단순히 테이블의 정보만 저장하는 공간이 아니라고!!!!!!!!!!!!!!!!!!!!!"
"그 순간 나의 models.py는 자신의 숨겨진 비밀을 들어내기 시작하는데.......... "
"다음 편에 계속....."
이라는 소설을 볼 것 같은 느낌이 든다. 하하하
사실, models.py의 기본적인 기능은 앞서 내가 말한 데이터베이스의 테이블관 관련된 정의를 하는 공간이 맞다. 하지만 그 이상으로 다양한 기능을 재정의 하거나, 모아 둘 수 있는 공간이기도 하다. 그러한 기능 중에는 내가 알게 된 @property도 있다.
이번 글은 단순히 models.py를 모델을 정의하는 당신을 위해서 준비한 내용으로 내가 언급하는 내용 이외에도 정말 무수히 많은 기능이 있을 것이다. 만약 관심이 있다면, django의 공식 문서를 살펴보기를 바란다.
models.py
이제 본론으로 들어가서 models.py를 하나씩 알아보자!!!!
models.py에 추가로 작성할 수 있는 것은 크게 3가지로 나눌 수 있다
@property, function정의, save(), delete() 메서드 재정의 이다. 하나씩 예를 들어가면서 알아보도록 하자.
@propetry
먼저 일반적인 모델 파일을 확인해보자
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
class Meta:
db_table = "persons"
위의 코드는 Person이라는 테이블을 정의한 것으로 필드로는 first_name, last_name, birth_date가 있다. 그런데 만약에 기능을 사용하던 중에 full_name을 요구하는 API가 있다면 어떻게 해야 할까?
from users.models import Person
person = Person.objects.create(first_name="lee", last_name="test", birth_date="1900-01-01")
full_name = person.first_name + person.last_name
보통의 경우라면 나는 위와 같이 Person 객체를 가지고 성과 이름을 결합한 full_name 변수를 만들거나, 아니면 값을 return 해줄 것이다.
하지만 이런 방법이 아닌 다른 방법이 존재하는데 그것이 바로 @propetry이다.
골뱅이@가 들어갔다는 것은 데코레이터라는 의미이다. 이 데코레이터는 model 클래스에 정의되지 않은 것을 필드인데 새롭게 정의된 메서드를 마치 필드인 것처럼 보이게 하는 역할을 한다.
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
class Meta:
db_table = "persons"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
모델은 앞서 정의된 것 과 같다. 다른 점이라면 아래에 새로운 메서드를 만들었고, 메서드 앞에 @property를 장식해주었다는 것이다.
이렇게 되면 person객체를 가지고 와서 바로 필드처럼 접근이 가능하게 된다.
from users.models import Person
person = Person.objects.create(first_name="lee", last_name="test", birth_date="1900-01-01")
person.full_name # lee test
그렇다면 @property는 어디에 그리고 왜 사용되는 걸까?
사실 full_name필드를 사전에 필드로 정의하면 이런 문제가 없을 것이다. 하지만 필드로 정의한다는 것은 데이터베이스에 그만큼의 용량을 차지한다는 것을 의미한다. 그렇기 때문에 위와 같이 @property를 사용해서 메서드를 정의해주는 것이 아닐까 하는 생각이 든다.
그리고 이런 멘토님의 설명을 보았던 기억이 난다. @property는 필드에는 존재하지 않지만 모델과 직접 연관되어 사용되는 데이터이며, 프로그램의 전반적인 부분에서 사용하기 되기 위해서 구현된다.라고. 앞으로 이를 인지하면서 사용하도록 해야겠다.
function
함수를 정의한다는 것이다. 그렇지만 중요한 것은 해당 모델과 관련된 기능을 정의한다는 것이다.
앞서 본 Person 객체를 좀 더 발전시켜서 알아보자!!!
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
class Meta:
db_table = "persons"
def mz_generation_status(self):
import datetime
if self.birth_date > datetime.date(1995, 1, 1):
return "z 세대 입니다."
elif self.birth_date > datetime.date(1981, 1, 1):
return "x 세대 입니다."
else:
return "그 이전 세대 입니다."
여기서 정의된 mz_generation_status()는 Person 객체의 birth_date를 보고서 판단해서 세대를 구분해주는 기능이다.
Person모델 클래스 안에 정의된 메서드이기 때문에 Person객체를 가지고 오면 바로 사용 가능한 메서드이다. 이렇게 모델과 관련된 기능을 정의해서 넣어둘 수도 있다.
Create(), Save(), Delete()의 재정의
객체를 생성하거나, 업데이트해서 저장하거나, 삭제할 경우 보통은 이미 내장된 함수를 사용해서 구현했다. 하지만 만약에 삭제나 저장 전에 어떠한 행동이 필요하다면 어떻게 해야 할까? 그런 경우는 위의 메서드들을 재정의해서 코드를 추가해주어야 한다.
from django.db import models
class Person(models.Model):
.
.
.
.
def save(self, *args, **kwargs):
if self.pk == 1:
return "save 되지 않습니다."
else:
super().save(*args, **kwargs) # 실제 save() 를 호출
def delete(self, *args, **kwargs):
print(self.full_name)
super().delete(*args, **kwargs)
위의 코드처럼 재정의 할 수도 있다. 만약에 해당 객체의 pk가 1이라면 내용이 변경되더라도 저장이 되지는 않는 것이다.
또는 delete의 경우는 삭제되기 전에 객체의 full_name을 한번 출력하고서 삭제된다.
이렇게 필요에 따라서 재정의해서 사용하는 것이 가능하고, 모델의 클래스별로 다르게 재정의해서 상황에 맞게 사용할 수 있는 것이다.
class Person(models.Model):
.
.
.
def save(self, *args, **kwargs):
# user.save() 할때 호출되는 내장함수 Overide
# self.pk 는 생성되고 나서 생기기에,
if not self.pk:
# on Create
do_something()
else:
# on Update
do_something()
return super(Model, self).save(*args, **kwargs)
# on delete
def delete(self, *args, **kwargs):
# user.delete() 할때 호출되는 내장함수 Overide
do_something()
return super(Model, self).delete(*args, **kwargs)
실제로 create와 update의 경우 save()로 결과가 저장되는데, pk가 있는지 없는지에 따라서 새로 생성되는 것인지 아니면, 업데이트가 되는지 판단하는 기준이 된다.
다른 예제를 하나 더 살펴보면서 save()와 delete()의 재정의에 대해서 좀 더 알아보자
class PersonChoice(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
class Meta:
db_table = "personchoice"
def save(self, *args, **kwargs):
# user.save() 할때 호출되는 내장함수 Overide
# self.pk 는 생성되고 나서 생기기에,
if not self.pk:
# on Create
if self.shirt_size not in ["S", "M", "L"]:
self.shirt_size = "S"
# else:
# # on Update
# do_something()
return super(PersonChoice, self).save(*args, **kwargs)
위 코드는 @property와 더불어서 새롭게 이해한 내용이다. 이전에 필드를 정의할 때 choice로 한정된 값이 들어오게 구현하려고 했었다.
필드에서는 정의가 돼도, 유저가 그 값을 모르는 이상 정의된 값 이외의 값이 들어가는 것을 확인했었고, 이를 제한할 수 있는 방법이 없는지 고민이 되었다. 다시 말해서, 내가 원하는 것은 shirt_size에 반드시 "S", "M", "L"만 들어가기를 원하지만 유저는 그러한 사항을 모르고 "W"나 "A"를 입력한다면 그 값이 저장이 된다는 것이다.
그때는 방법을 몰라서 해결하지 못했는데, save()를 재정의 함으로써 문제를 해결할 수 있다는 실마리를 얻어서 위와 같이 작성해 보았다.
만약 유저가 "S", "M", "L" 이외의 것을 입력할 시 save() 전에 확인을 해서 디폴트 값으로 "S"로 만들어 주는 것이다. 이렇게 하면, 앞서 고민했던 유저의 잘못된 입력에 대처할 수 있게 된다. 한참을 헤매고서야 결과에 도달할 수 있었는데, 너무 뿌듯하다.
그리고 choice를 사용하는데 새롭게 알게 된 메서드가 있는데, 객체.get_필드_display()이다.
위에서는 shirt_size를 사용했으니까, 객체.get_shirt_size_display()하게 되면 객체가 선택한 choice값의 문자열 값이 반환되게 된다. 만약 "S" 였다면 "Small"이 반환되는 것이다.
이 역시 다른 곳에서 유용하게 사용할 수 있는 메서드라고 생각해서 기억해두고자 한다.
정리하자면
지금까지 나는 models.py에 단순히 테이블을 정의한 것뿐이라고 생각했는데, 그게 아니라 제대로 된 기능 자체를 사용하지 못했던 것이다.
models 자체에 대한 기능의 이해가 필요하다. 오늘 본 내용은 정말 일부분에 지나지 않기 때문에 필요할 때마다 알아가 보자!!!!
마지막으로 본 글은 개인의 사견이 많이 많이 담긴 내용이며, 잘못된 내용이 포함될 가능성이 있습니다.
만약 잘못된 내용을 있다면 언제든 댓글로 알려주세요 고치겠습니다.
감사합니다!!!!!!!
'Python.Django.DRF' 카테고리의 다른 글
| [시리즈] 어디까지 가봤니 DRF 튜토리얼??(4편) 이런 망할 개ㅌ발 (0) | 2022.11.27 |
|---|---|
| [시리즈] 어디까지 가봤니 DRF 튜토리얼??(3편) 와우~ 신기하다 (0) | 2022.11.26 |
| [시리즈] 어디까지 가봤니 DRF 튜토리얼??(2편) 이런 미?... (0) | 2022.11.26 |
| [시리즈] 어디까지 가봤니 DRF 튜토리얼??(1편) 와 시?이다..... (6) | 2022.11.26 |
| 우~~~ 보여줄게 완전히 달라진 나~~ 보여줄게 get(update)_or_create() (0) | 2022.11.25 |