계기
개인 프로젝트를 여러개 하다보니 DB 포트가 겹치기도 하고, 가끔 필요한 데이터를 이미 만들어둔 경우에 재사용을 하고 싶어서 시작하게 되었다.
기술스팩
일단 기본적으로 DB는 도커를 사용하여 MySQL을 사용하였다.
연동
1. Django 프로젝트의 setting.py를 열고 'DATABASES={}'를 설정해준다.
기본적으로 아래와 같이 설정되어 있을것이다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 DATABASES의 키 값이 default로 설정되어 있는데, 현재 프로젝트에선 각각의 DB를 분리하기 위해서 아래과 같이 변경하였다.
DATABASES: {
"default": {},
"article_db": {
"ENGINE": "django.db.backends.mysql",
"NAME": "article",
"USER": "root",
"PASSWORD": "1234",
"HOST": "127.0.0.1",
"PORT": "3366"
},
"user_db": {
"ENGINE": "django.db.backends.mysql",
"NAME": "user",
"USER": "root",
"PASSWORD": "1234",
"HOST": "127.0.0.1",
"PORT": "3367"
}
}
user 정보를 담는 DB의 키값은 user_db로 article정보를 담는 DB의 키 값은 article_db로 정해주었다.
(여기서 키 값이라함은 잠시후 명령어에서 나오겠지만 migrate를 할 시 지정해줄 키 값이라고 생각하고 있으면 된다.)
DB설정이 완료된 후 Router설정을 해주어 우리가 원하는 데이터가 원하는 DB로 들어갈 수 있게 처리한다.
아래는 내 현재 프로젝트 구성이다.
현재 나는 common이란 패키지를 하나 만들어주고, 그 하위에 db_router를 설정해주는 코드를 작성했다.
(settings.py가 있는 프로젝트안에 넣기도 하고...자신이 편한 위치에 패키지를 생성하고 만들면 될것같다)
이제 Router기능을 하는 코드를 생성해보자.
db_Router.py
class UserRouter:
"""
튜토리얼 코드상에는 auth, contenttyypes만 존재하는데 user_db에
admin 패널도 사용하고자 DB에 들어갈 테이블에 추가해주었다.
default에 들어가는 기본적인 테이블 정보들은 settings.py에 INSTALLED_APP에 설정되어
있는 값들이였으므로, 내가 추가하고자 하는 app들을 원하는 DB에 맞게 넣어준다
INSTALLED_APPS = [
'django.contrib.admin', #여기서 admin이 필요하다는것을 알아서 추가해주었다.
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions', #여기서 sessions이 필요하다는것을 알아서 추가해주었다.
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api',
'articles',
]
참고로 admin 없이 migrate 해버리면, admin패널로 들어갈 수 없다
"""
# app Name
route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'}
# db키 값(settings.py에서 설정한 키 값이라고 보면된다.)
db_name = 'user_db'
def db_for_read(self, model, **hints):
"""
Attempts to read logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def db_for_write(self, model, **hints):
"""
Attempts to write logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the obj1 or obj2 apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'primary_db' database.
"""
if app_label in self.route_app_labels:
return self.db_name
return None
db_Router코드도 작성되었고 이제 settings.py에 우리가 생성한 라우터를 설정해주면 끝나게 된다.
아래과 같이 DATABASE_ROUTERS=[] 를 설정해주고, 본인이 만든 라우터 위치를 잡아주고, 클래스를 잡아주면 된다.
나의 경우에 common 패키지 하위에 db_Router.py에 UserRouter클래스를 생성하였기 때문에 아래와 같은 경로가 된다.
DATABASE_ROUTERS = [
'common.db_Router.UserRouter',
]
위와 같이 설정이 완료되었다면, 이제 migrate를 하게되는데, 기존의 방식인 manage.py migrate로는 할 수 없다. Why? default로 설정되어 있는 설정을 우리가 지웠기 때문에 migrate의 명령어에 database를 명시해주는 형식으로 변경이 된다.
명령어는 다음과 같다.
(--database만 추가해주면되고, user_db는 우리가 처음에 설정해주었던 User데이터가 들어가는 데이터베이스 정보들의 키 값이다)
python manage.py migrate --database=user_db
이렇게 명시적으로 database를 설정 해줘야기 때문에 createsuperuser의 경우에도 아래와 같이 명시를 해줘서 저장해준다.
python manage.py createsuperuser
유저정보를 담은 DB설정은 완료가 되었고, 다음 article이란 app의 정보를 담는 방법은 어떻게 해야할까?
방법은 간단하다! 일단 db_Router.py에 클래스를 하나 추가해주고, settings.py에 추가해주면 된다.
db_Router.py
(내용은 위의 코드와 같으나, 한글 주석은 제거하였다.)
class ArticleRouter:
# app Name
route_app_labels = {'articles'}
db_name = 'article_db'
def db_for_read(self, model, **hints):
"""
Attempts to read logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def db_for_write(self, model, **hints):
"""
Attempts to write logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the obj1 or obj2 apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'primary_db' database.
"""
if app_label in self.route_app_labels:
return self.db_name
return None
class UserRouter:
# app Name
route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'}
db_name = 'user_db'
def db_for_read(self, model, **hints):
"""
Attempts to read logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def db_for_write(self, model, **hints):
"""
Attempts to write logs and others models go to primary_db.
"""
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the obj1 or obj2 apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'primary_db' database.
"""
if app_label in self.route_app_labels:
return self.db_name
return None
settings.py
DATABASE_ROUTERS 설정 추가
DATABASE_ROUTERS = [
'common.db_Router.ArticleRouter',
'common.db_Router.UserRouter',
]
다시 한번 article을 DB에 migrate를 해야하므로, 아래와 같이 명령어를 입력한다.
python manage.py --database=article_db
각 DB별로 저장하는 데이터의 값을 분리하게 된다.
DB 구조
참고 문서
https://docs.djangoproject.com/en/4.0/topics/db/multi-db/
Django 공식문서에서 발췌해 온 내용이다.
'프로그래밍 > Django' 카테고리의 다른 글
[Django]filter_fields로 URL 파라미터 사용하기 (0) | 2022.05.13 |
---|---|
[Django][에러노트]raise ValueError, "No frame marked with %s." % fname (0) | 2022.05.07 |
[Django][Python]QuerySet N+1 prblem 해결하기 (0) | 2022.04.06 |
[Django][Python]QuerySet 캐싱을 통해 문제 해결하기 (0) | 2022.04.06 |
[Django][Python]QuerySet을 통해 알아보는 ORM의 특징 (0) | 2022.04.06 |