프로그래밍/Python

[Django] 장고 기초 - (3) Models

Churnobyl 2023. 4. 20. 11:47
728x90
반응형

 

Chapter 04.  모델(Model)에 관하여

 

앞서 언급한 것처럼 장고의 모델은 DB 테이블을 결정짓는다.

즉, 모델에는 데이터가 저장될 때 어떤 필드에 저장될 것인지, 이 데이터가 저장될 때 어떤 동작을 할 것인지 등이 포함된다.

 


기초

  • 각 모델은 django.db.models.Model의 서브클래스다
  • 모델의 각 속성(attribute)는 DB의 필드(Field)를 의미한다
  • 모델을 생성하면 Django는 자동으로 생성된 DB엑세스 API(save(), delete() 등)를 제공한다

 

모델 생성 및 사용

# 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에 앱을 등록하지 않았을 경우

 

아래와 같이 등록하면 된다

 

# mysite/settings.py

INSTALLED_APPS = [
    # ...
    "app",
    # ...
]

app은 당연히 본인이 생성한 앱 이름으로 바꿔주어야 한다

 


필드(Fields)

모델을 구성하는 가장 중요한 부분이자 유일한 부분이다.

장고에서 DB의 필드는 파이썬의 클래스 속성으로 정의된다.

필드의 이름을 선택할 때 clean이나 save, delete와 같이 models API에 미리 정의되어 있는 이름과 겹치지 않도록 해야한다.

 


필드 타입(Field Type)

 

  • AutoField

자동으로 값이 증가하는 IntegerField.

PK값이 자동적으로 추가되므로 일반적으로 직접 사용할 일은 잘 없다

 

하지만 자동으로 생성되는 id라는 이름이 정 마음에 들지 않는다면 아래와 같이 변경해줄 수 있다.

 

# 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

정수 필드.

MinValueValidatorMaxValueValidator를 이용해 값을 검증한다

-21474836482147483647까지 모든 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를 이용해서 다음과 같이 활용할 수 있다

 

# 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=<int>)

문자열 필드. 최대길이를 지정하는 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=<int>)

파일필드. 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)

 

# 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에 존재한다)
    1. CASCADE : 자신이 참조하고 있는 테이블의 데이터가 삭제되면 자동으로 자신의 데이터도 삭제
    2. PROTECT : ProtectedError를 발생시켜 참조된 데이터의 삭제를 방지한다
    3. RESTRICT : RestrictedError를 발생시켜 참조된 데이터의 삭제를 방지한다. PROTECT와의 차이는 일부 특수한 상황에서 데이터의 삭제를 허용한다
    4. SET_NULL : 자신이 참조하고 있는 테이블의 데이터가 삭제되면 NULL값으로 대체한다. null=True옵션일 때만 사용가능하다
    5. SET_DEFAULT : default값으로 대체한다. ForeignKey에 default값이 미리 설정되어 있어야 한다
    6. SET() : 어떤 호출된 결과로 대체한다
    7. DO_NOTHING : 아무것도 하지 않는다
  • related_name : 참조된 객체로부터의 관계를 위한 이름. 역참조를 위한 이름이라고 생각하면 편하다.

 

  • ManyToManyField(to)

many-to-many관계. 

 

 

 

  • OneToOneField(to, on_delete, parent_link=False)

one-to-one관계.

 

반응형