본문 바로가기
프로그래밍/Django

[Django] 멀티 DB 라우터설정 및 연동하기

by 우주를놀라게하자 2022. 5. 11.
반응형
SMALL

계기


개인 프로젝트를 여러개 하다보니 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 공식문서에서 발췌해 온 내용이다.

 

Multiple databases | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

반응형
LIST