앞서 언급한 것처럼 장고의 모델은 DB 테이블을 결정짓는다.
즉, 모델에는 데이터가 저장될 때 어떤 필드에 저장될 것인지, 이 데이터가 저장될 때 어떤 동작을 할 것인지 등이 포함된다.
기초
각 모델은 django.db.models.Model의 서브클래스다
모델의 각 속성(attribute)는 DB의 필드(Field)를 의미한다
모델을 생성하면 Django는 자동으로 생성된 DB엑세스 API(save(), delete() 등)를 제공한다
모델 생성 및 사용
python# app/models.py
from django.db import models
class Person(models.Model):
class Meta:
db_table = "person"
name = models.CharField(max_length=30)
email = models.EmailField()
birth = models.DateField()복사
위와 같은 형태로 클래스를 정의해 모델을 정의할 수 있다
테이블의 이름을 정해주지 않으면 sqlite DB에 app_person형태로 테이블이 자동으로 만들어지지만, 메타데이터 서브클래스 Meta를 오버라이딩해서 db_table매개변수를 다시 정의해주면 person 등으로 테이블 이름으로 바꿔줄 수 있다
클래스의 각 속성은 위에 말한 것처럼 DB의 필드를 의미하는 것이며 속성 이름 = models.필드타입(옵션=옵션) 형태로 정의할 수 있다.
모델을 사용하기 위해서는 우선 장고가 우리가 만든 앱을 인식할 수 있도록 settings.py에 등록해주어야 한다.
그렇지 않으면 우리가 아무리 모델을 변경해주고 makemigrations 명령어로 migration을 만들어 주려고 해도 아래와 같이 감지하지 못한다
settings.py에 앱을 등록하지 않았을 경우
아래와 같이 등록하면 된다
python# mysite/settings.py
INSTALLED_APPS = [
# ...
"app",
# ...
]복사
app은 당연히 본인이 생성한 앱 이름으로 바꿔주어야 한다
필드(Fields)
모델을 구성하는 가장 중요한 부분이자 유일한 부분이다.
장고에서 DB의 필드는 파이썬의 클래스 속성으로 정의된다.
필드의 이름을 선택할 때 clean이나 save, delete와 같이 models API에 미리 정의되어 있는 이름과 겹치지 않도록 해야한다.
필드 타입(Field Type)
AutoField
자동으로 값이 증가하는 IntegerField.
PK값이 자동적으로 추가되므로 일반적으로 직접 사용할 일은 잘 없다
하지만 자동으로 생성되는 id라는 이름이 정 마음에 들지 않는다면 아래와 같이 변경해줄 수 있다.
python# app/models.py
from django.db import models
class User(models.Model):
class Meta:
db_table = "user"
superduper_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)복사
위와 같이 AutoField(primary_key=True)로 superduper_id라는 필드를 만들어 주었다
migrate를 하고 결과물을 보면 다음과 같다
id를 superduper_id로 바꿨다
테이블의 PK필드 이름이 잘 바뀐 것을 볼 수 있다.테이블의 PK는 단 하나만 존재해야 하므로 primary_key속성을 True로 설정하지 않으면 오류가 난다
IntegerField
정수 필드.
MinValueValidator와 MaxValueValidator를 이용해 값을 검증한다
-2147483648 ~ 2147483647까지 모든 DB에서 안전하게 사용할 수 있다
이말을 듣고 궁금해져서 테스트 해보았다
부호있는 4byte의 최댓값 +1
부호있는 4byte 최댓값 양수에 1을 더해줬을 때 오류없이 잘된다.
여러번 테스트를 한 끝에 결과적으로 9,223,372,036,854,775,808에서 아래와 같은 오류가 발생했다.
OverflowError: Python int too large to convert to SQLite INTEGER
즉, 'python에서는 문제없는데 SQLite에서 너무 큰 값은 처리 못해요'다
원인은 현재 사용하고 있는 SQLite의 정수가 부호있는 8byte 값이기 때문이다.
따라서 2^64bit = 18,446,744,073,709,551,616의 절반까지의 양수까지 처리할 수 있다.
SQLite가 아닌 다른 DB는 다른 정수값 체계가 있을 수 있으므로 안전하게 사용하자
추가적으로 값을 검증하는 Min/MaxValueValidator를 이용해서 다음과 같이 활용할 수 있다
python# app/models.py
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
class User(models.Model):
class Meta:
db_table = "user"
superduper_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
age = models.IntegerField(validators=[MinValueValidator(18), MaxValueValidator(250)])복사
만약 성인들만 이용할 수 있는 사이트를 만들고자 할 때 만18세 미만의 청소년들은 가입을 금지시킨다고 하자
그럴 때 위와 같이 django.core.validators의 Min/MaxValueValidator클래스를 이용해 값을 제한할 수 있다
결과는 다음과 같다
Age에 15세를 입력했을 때
FloatField
파이썬의 float자료형과 대응되는 실수 필드.
이도 역시 위의 Min/MaxValueValidator클래스를 이용해 값을 제한할 수 있다
DecimalField(max_digits=None, decimal_places=None)
파이썬의 decimal자료형과 대응되는 실수 필드.
숫자의 최대 자릿수를 표현하는 max_digits와 소수부를 지정하는 decimal_places를 필수 인자로 가진다.
부동소수점인 Float에서 발생하는 무한소수 문제를 해결할 수 있지만 메모리를 많이 잡아먹는다
BooleanField
True/False로 구성된 필드.
null=True 옵션을 적용하고 default옵션을 적용하지 않으면 기본값은 Null이 된다
Unknown, Yes, No 세가지 형태가 존재할 수 있다
CharField(max_length=)
문자열 필드. 최대길이를 지정하는 max_length를 필수적으로 가져야 한다
TextField
긴 텍스트 필드. max_length가 필요없지만 만약 지정한다면 자동적으로 생성되는 form field에는 영향을 준다.
하지만 model이나 DB에는 영향을 주지 못한다.
max_length=20으로 적용했을 때 Introduce
위의 그림과 같이 max_length=20 옵션을 주면 20자 이상 텍스트를 작성할 수 없다
django shell로 Nancy의 introduce필드값을 변경
변경된 텍스트
하지만 위와 같이 DB에 직접 수정을 하면 max_length로 제한한 길이가 무색하게 잘 수정된다.
DateField / DateTimeField(auto_now=False, auto_now_add=False)
각각 파이썬의 datetime.date, datetime.datetime 객체로 표시되는 필드
TimeField(auto_now=False, auto_now_add=False)
파이썬의 datetime.time객체로 표시되는 필드.
EmailField
EmailValidator를 이용해 이메일의 유효성을 검증하는 CharField
FileField(upload_to="", storage=None, max_length=)
파일필드. PK로 지정할 수 없다
upload_to : MEDIA_ROOT의 서브디렉토리를 결정하기 위한 옵션. settings.py에 MEDIA_ROOT='/media/'를 설정하고 upload_to="photos/%Y/%m/%d"를 설정했다면 파일은 home/media/photos/2023/04/19/file.jpg 형태로 저장된다.
storage : 다양한 환경에 따라 서로 다른 storage를 선택해야 할 때 사용하는 옵션. 자세한 내용은 여기
ImageField(upload_to=None, height_field=None, width_field=None, max_length=100)
업로드된 파일이 유효한 이미지인지를 검사하는 FileField
필드 옵션(Field Options)
모든 필드 타입에 사용가능한 옵션들이다
null
기본값은 False. True라면 DB에 비어있는 값을 NULL로 저장한다.
문자열 기반 필드에서 no data(데이터가 없음)을 의미하는 값이 NULL과 빈 문자열 두가지가 혼용될 수 있으므로 CharField나 TextField에는 사용하면 안된다.
문자열 기반 필드에서 사용할 수 있는 한가지 예외는 unique=True옵션과 blank=True옵션이 함께 사용됐을 때다
이때는 빈값이 여러개 생성될 수 있으므로 unique=True옵션에 걸리지 않기 위해 null=True를 설정할 수 있다.
blank
기본값은 False. True라면 빈 문자열을 허용한다
null이 DB에 관계된(database-related) 이슈라면 blank는 유효성 검사와 관련(validation-related)된다.
blank=False라면 이 필드는 값이 필수다.
unique
unique=True라면 테이블 전체에서 값들이 유일한 값이어야 한다
ManyToManyField와 OneToOneField를 제외한 모든 필드 타입에서 사용할 수 있다
default
필드의 기본값을 설정한다.
어떤 value 혹은 callable한 객체가 들어갈 수 있다.(list, set같이 mutable한 객체는 불가)
만약 callable한 객체가 들어간다면 새로운 객체가 생성될 때마다 불릴 것이다
editable
editable=False라면 admin페이지나 ModelForm에서 보이지 않는다. 또한 모델 유효성 검사에서도 제외된다.
db_column
필드의 이름을 별도로 설정한다. 이 옵션을 따로 사용하지 않는다면 기본적인 필드 이름이 적용된다.
db_comment
Django 코드를 보지 않고 DB를 이용하는 사람들에게 각 칼럼의 comment를 달아줄 수 있다
choices
각 필드 값에 사람이 읽을 수 있는 값을 별도로 설정해줄 수 있다
[(A, B), (A, B), ...]형식으로 구성되고 A는 실제 DB에 저장되는 값, B는 사람이 읽을 수 있는 값이다
관계 필드(Relationship Fields)
각 Table의 관계를 나타내는 필드
ForeignKey(to, on_delete)
Many-to-One 관계.
현재 모델에 관련된 모델의 클래스(to)와 그 클래스의 row가 삭제됐을 때 현재 모델이 어떻게 행동할 지에 관한 on_delete옵션을 설정해주어야 한다.
모델 간의 관계를 설정할 때 상대 모델이 코드 상 뒤에 있어 NameError가 발생할 때, 문자열로 된 상대 모델의 이름으로 대체할 수 있다.(lazy relationship)
python# app/models.py
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
"Manufacturer",
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass복사
위와 같은 경우 테이블의 필드에는 manufacturer_id라는 _id가 붙은 이름으로 저장된다.
만약 이 이름이 싫다면 db_column옵션으로 다른 이름을 지정해주면 된다
인수(Arguments)
on_delete : ForeignKey로 참조하는 객체가 삭제되었을 때 SQL의 행동을 결정한다(아래 옵션들은 django.db.models에 존재한다)
CASCADE : 자신이 참조하고 있는 테이블의 데이터가 삭제되면 자동으로 자신의 데이터도 삭제
PROTECT : ProtectedError를 발생시켜 참조된 데이터의 삭제를 방지한다
RESTRICT : RestrictedError를 발생시켜 참조된 데이터의 삭제를 방지한다. PROTECT와의 차이는 일부 특수한 상황에서 데이터의 삭제를 허용한다
SET_NULL : 자신이 참조하고 있는 테이블의 데이터가 삭제되면 NULL값으로 대체한다. null=True옵션일 때만 사용가능하다
SET_DEFAULT : default값으로 대체한다. ForeignKey에 default값이 미리 설정되어 있어야 한다
SET() : 어떤 호출된 결과로 대체한다
DO_NOTHING : 아무것도 하지 않는다
related_name : 참조된 객체로부터의 관계를 위한 이름. 역참조를 위한 이름이라고 생각하면 편하다.
ManyToManyField(to)
many-to-many관계.
OneToOneField(to, on_delete, parent_link=False)
one-to-one관계.