[Django] ORM Cookbook, 모델을 정의하는 방법
해당 포스팅은 Django ORM CookBook 을 공부하며 새로 알게된 사실이나 더 나아가 추가적으로 같이 알면 좋을 내용을 정리하고 있습니다.
1. 자기 참조 외래 키
자기 자신을 참조하는 자기 참조 외래 키를 정의할 수 있습니다.
class Employee(models.Model):
manager = models.ForeignKey('self', on_delete=models.CASCADE)
class Employee(models.Model):
manager = models.ForeignKey("Employee", on_delete=models.CASCADE)
2. 기존 DB를 django Model로 옮기기
Django에서 models.py를 통해 DB를 생성할 수 도 있지만 경우에 따라 기존의 DB를 사용해야 할 수 있습니다.
이 때 데이터베이스를 분석하여 이에 맞게 모델을 생성할 수 있습니다.
python manage.py inspectdb
3. 데이터베이스 View
데이터베이스에는 View라는 개념이 존재합니다.
DB 내에서 조회할 수 있도록 질의문으로 정의된 객체이며 데이터를 물리적으로 저장하는 것은 아니지만 테이블과 같이 조회할 수 있습니다.
이를 통해 필요한 정보만 쉽게 조회 가능한 인터페이스를 만들 수 있습니다.
SQL 질의문으로 View를 정의한 후 이를 django 내에서 조회해보겠습니다.
create view temp_user as
select id, nickname
from auth_user;
django 내에서는 Meta 클래스를 조작하여 View를 조작할 수 있습니다.
class TempUser(models.Model):
nickname = models.CharField(max_length=100)
class Meta:
managed = False
db_table = "temp_user"
7. 범용모델 정의
Django-한-모델이-여러-개의-모델과-관계를-맺어야하는-순간-contenttypes-framework
8. 테이블 이름 정의
django는 기본적으로 appname_tablename
으로 데이터베이스의 테이블 이름을 정의합니다.
따라서 여태 예제들에서 SQL 질의문을 보면 위와 같은 형식인걸 확인할 수 있습니다.
테이블의 이름을 직접 정의하기 위해서는 Meta 클래스를 조작합니다.
class TempUser(models.Model):
nickname = models.CharField(max_length=100)
# 생략
class Meta:
db_table = "temp_user"
9. 열 이름 정의
DB table의 열 이름은 Django Model 클래스의 필드 이름이 디폴트로 사용됩니다.
이 때 db_column
속성을 사용하여 열 이름을 정의할 수 있습니다.
class TempUser(models.Model):
nickname = models.CharField(max_length=100, db_column='column1')
# 생략
class Meta:
db_table = "temp_user"
10. Null VS Blank
Django-Model-Field의-Null-vs-Blank
11. UUID를 Primary Key로 사용
Django에서 기본적으로 Model을 만들 경우 양의 정수를 값으로 가지는 ID 필드가 정의 됩니다.
이를 UUID로 대체하고 싶으면 UUIDField
를 사용합니다.
import uuid
from django.db import models
class TempUser(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
nickname = models.CharField(max_length=100)
TempUser.objects.first().id
# 3cd2b4b0c36f43488a93b3bb72029f46
12. SlugField 사용
path Variable로 객체를 구별할 때 id 값을 사용할 수 도 있지만 string type의 문자열로 구별해야할 경우도 있습니다.
/post/12 # id로 구분
/post/my first posting # title로 구분
하지만 URL에는 공백이 들어갈 수 없으므로 이는 %20
로 치환됩니다.
/post/my%20first%20posting
이는 보기 좋지 않으므로 SlugField
를 사용하면 보기 좋게 표현할 수 있습니다.
이는 모든 문자를 소문자화 하고 공백은 -
로 표현해줍니다.
/post/my-first-posting
model 에서 이를 적용하기 위해서는 다음과 같습니다.
from django.utils.text import slugify
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
post = Post.objects.create(title='my first posting')
post.slug
# my-first-posting
13. 여러 개의 데이터베이스 사용
Django에서 하나의 프로젝트에 여러 개의 DB를 사용할 수 있습니다.
현재 Django project에는 user_data
, customer_data
라는 두 개의 app이 존재합니다.
# settings.py
DATABASE_ROUTERS = ['path.to.DemoRouter']
DATABASE_APPS_MAPPING = {'user_data': 'users_db',
'customer_data':'customers_db'}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'users_db': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'postgres_user',
'PASSWORD': 'password'
},
'customers_db': {
'NAME': 'customer_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_cust',
'PASSWORD': 'root'
}
}
-
DATABASE_ROUTERS
database router
의 경로를 기재합니다.database router
에 대해서는 아래에서 다시 알아보겠습니다. -
DATABASE_APPS_MAPPING
딕셔너리 형태로, app과 DB를 매핑시킵니다.
위와 같은 경우,
user_data
앱은users_db
에 해당하는 postgresql에 저장되고customer_data
앱은customers_db
에 해당하는 mysql에 저장됩니다. -
DATABASES
딕셔너리 형태로 사용할 데이터베이스들을 정의합니다.
이제 router를 알아봅시다.
router를 통해 DB를 특정하지 않은 경우 위 DATABASES
에서 정의한 default
에 해당하는 DB에 중계됩니다.
class DemoRouter:
"""
user_data 앱의 모델에서 수행되는 모든 데이터베이스 연산을 제어하는 중계기
"""
def db_for_read(self, model, **hints):
"""
user_data 앱의 모델을 조회하는 경우 users_db로 중계한다.
"""
if model._meta.app_label == 'user_data':
return 'users_db'
return None
def db_for_write(self, model, **hints):
"""
user_data 앱의 모델을 기록하는 경우 users_db로 중계한다.
"""
if model._meta.app_label == 'user_data':
return 'users_db'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
user_data 앱의 모델과 관련된 관계 접근을 허용한다.
"""
if obj1._meta.app_label == 'user_data' or \
obj2._meta.app_label == 'user_data':
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
user_data 앱의 모델에 대응하는 표가 users_db 데이터베이스에만 생성되도록 한다.
"""
if app_label == 'user_data':
return db == 'users_db'
return None
여러 개의 데이터베이스를 사용할 때 특정 데이터베이스에서 대해 마이그레이션 할 수 있습니다.
python manage.py migrate --database=users_db