본문 바로가기
Django/Django개념

Django multiple DB 문서정리

by hyun-am 2022. 12. 19.

django multiple databases

Multiple databases

django는 여러개의 데이터베이스와 상호작용할 수 있습니다. 이것을 사용하기 위해서는 몇 가지 추가 단계를 수행해야 합니다.

Defining your databases

맨 처음으로 해야할 일은 database를 한개 이상 사용한다고 서버에 알려줘야합니다.

이것은 database setting을 통해 작성할 수 있습니다. 이 설정은 Django 전체에서 특정 데이터베이스를 참조하는 방법인 데이터베이스 alias을 해당 connection에 대한 setting dictionary에 매핑합니다. 내부 dictionary의 설정은 DATABASE 설명서에 자세히 설명되어 있습니다.

데이터베이스는 선택한 alias를 가질 수 있습니다. 그러나 alias 기본값에는 특별한 의미가 있습니다. Django는 다른 데이터베이스가 선택되지 않은 경우 default alias를 가진 데이터베이스를 사용합니다.

다음은 두 개의 데이터베이스(postgresql, mysql)을 사용하는 예시입니다.

DATABASES = {
    'default': {
        'NAME': 'app_data',
        'ENGINE': 'django.db.backends.postgresql',
        'USER': 'postgres_user',
        'PASSWORD': 's3krit'
    },
    'users': {
        'NAME': 'user_data',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'priv4te'
    }
}

프로젝트 컨셉에서 default 데이터베이스의 개념이 이해되지 않는 경우 사용하려는 데이터베이스를 지정할때 주의해야합니다.

django는 기본 데이터베이스 항목을 정의해야 하지만 parameters dictionary를 비워 둘 수 있습니다.

이렇게 하려면 쿼리가 기본 데이터베이스로 라우팅되지 않도록 사용중인 contrib 및 써드파티 모델을 포함하여 모든 앱 모델에 대해 DATABASE_ROUTERS를 설정해야 합니다. 다음은 기본 항목이 의도적으로 비어있는 두 개의 기본이 아닌 데이터베이스를 정의하는 세팅입니다.

DATABASES = {
    'default': {},
    'users': {
        'NAME': 'user_data',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'superS3cret'
    },
    'customers': {
        'NAME': 'customer_data',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_cust',
        'PASSWORD': 'veryPriv@ate'
    }
}

데이터베이스 설정에서 정의하지 않은 데이터베이스에 액세스하려고 하면 Django는 django.utils.connection.ConnectionDoesNotExists 예외를 발생시킵니다.

Synchronizing your databases

migration management 명령어는 한 번에 하나의 데이터베이스에서 작동합니다. 기본적으로 데이터베이스는 default 데이터베이스에서 작동하지만 --database 옵션을 제공하여 다른 데이터베이스를 동기화하도록 지시할 수 있습니다. 따라서 위의 첫 번째 예에서 모든 모델을 모든 데이터베이스에 동기화하려면 다음을 호출해야 합니다.

$ ./manage.py migrate
$ ./manage.py migrate --database=users

모든 응용프로그램을 특정 데이터베이스에 동기화하지 않으려면 특정 모델의 가용성을 제한하는 정책을 구현하는 데이터베이스 라우터를 정의할 수 있습니다.

위의 두 번째 예에서와 같이 기본 데이터베이스를 비워둔 경우, 마이그레이션을 실행할 때마다 데이터베이스 이름을 제공해야합니다. 데이터베이스 이름을 생략하면 오류가 발생합니다. 두번째 마이그레이션 예시

$ ./manage.py migrate --database=users
$ ./manage.py migrate --database=customers

Using other management commands

데이터베이스와 상호 작용하는 대부분의 다른 django-admin 명령은 마이그레이션과 동일한 방식으로 작동합니다. --database를 사용하여 사용되는 데이터베이스를 제어하면서 한 번에 하나의 데이터베이스에서만 작동합니다.

이 규칙의 예외는 makemigrations 명령입니다. 새 마이그레이션을 생성하기 전에 데이터베이스의 마이그레이션 기록을 검증하여 기존 마이그레이션 파일의 문제(편집으로 인해 발생할 수 있음)을 파악합니다. 기본적으로 기본 데이터베이스만 확인하지만 라우터가 설치된 경우라우터의 allow_migrate()메서드를 참조합니다.

Automatic database routing

여러 데이터베이스를 사용하는 가장 쉬운 방법은 데이터베이스 라우팅 체계를 설정하는 것입니다. 기본 라우팅 체계는 개체가 원래 데이터베이스이 ‘fixed’된 상태로 유지되도록 합니다(즉, foo 데이터베이스에서 검색된 개체는 동일한 데이터베이스에 저장됨). default 라우팅 체계는 데이터베이스가 지정되지 않은 경우 모든 쿼리가 default 데이터베이스로 fall back되도록 합니다.

기본 라우팅 체계를 활성화하기 위해 아무 것도 할 필요가 없습니다. 모든 Django 프로젝트에서 ‘기본적으로’ 제공됩니다. 그러나 보다 흥미로운 데이터베이스 할당 동작을 구현하려는 경우 자체 데이터베이스 라우터를 정의하고 설치할 수 있습니다.

Database routers

데이터베이스 라우터는 최대 4개의 메소드를 제공합니다.

db_for_read(model, **hints)

  • 읽기전용 데이터베이스를 suggest합니다.
  • 데이터베이스 작업이 데이터베이스 선택에 도움이 될 수 있는 추가 정보를 제공할 수 있는 경우 hints dictionary에 제공됩니다. 유효한 힌트에 대한 자세한 내용은 아래에 나와있습니다.
  • no suggestion일 경우 None을 반환합니다.

db_for_write(model, **hints)

  • 쓰기전용 데이터베이스를 suggest합니다.
  • 데이터베이스 작업이 데이터베이스 선택에 도움이 될 수 있는 추가 정보를 제공할 수 있는 경우 hints dictionary에 제공됩니다. 유효한 힌트에 대한 자세한 내용은 아래에 나와있습니다.
  • no suggestion일 경우 None을 반환합니다.

allow_migrate(db, app_label, model_name=None, **hints)

  • alias가 db인 데이터베이스에서 마이그레이션 작업을 실행할 수 있는지 확인합니다. 작업을 실행해야 하면 True를 반환하고, 실행하지 않아야 하면 False를 반환하고, 라우터에 suggest가 없을경우 None을 반환합니다.
  • app_label 위치 arguments는 마이그레이션되는 애플리케이션의 label입니다.
  • model_name은 대부분의 마이그레이션 작업에서 마이그레이션되는 모델의 model._meta.model_name(모델 __name__ 의 소문자 버전) 값으로 설정됩니다. 힌트를 사용하여 제공하지 않은 한 RunPython 및 RunSQL 작업에 대한 값은 None입니다.
  • 힌트는 라우터에 추가 정보를 전달하기 위해 특정 작업에서 사용됩니다.
  • model_name이 설정되면 hint는 일반적으로 ‘model’키 아래에 모델 클래스를 포함합니다. 기록 모델일 수 있으므로 사용자 지정 특성, 메서드 또는 관리자가 없을 수 있습니다. _meta에만 의존해야합니다.
  • 이 방법은 지정된 데이터베이스에서 모델의 가용성을 확인하는 데에도 사용할 수 있습니다.
  • makemigration은 항상 모델 변경에 대한 마이그레이션을 생성하지만 allow_migrate()가 False를 반환하면 db에서 마이그레이션을 실행할 때 model_name에 대한 모든 마이그레이션 작업을 자동으로 건너뜁니다. 이미 마이그레이션이 있는 모델에 대한 allow_migration()동작을 변경하면 손상된 외래 키, 추가 테이블 또는 누락된 테이블이 발생할 수 있습니다. makemigrationssms 마이그레이션 기록을 확인할 때 마이그레이션이 허용된 앱이 없는 데이터베이스를 건너뜁니다.

라우터는 이러한 모든 방법을 제공할 필요는 없습니다. 그 중 하나 이상을 생략할 수 있습니다. 메소드 중 하나가 생략되면 Django는 관련 검사를 수행할 때 해당 라우터를 건너뜁니다.

Hints

  • 데이터베이스 라우터가 수신한 힌트를 사용하여 지정된 요청을 수신할 데이터베이스를 결정하는 데 사용할 수 있습니다.
  • 현재 제공되는 유일한 힌트는 진행 중인 읽기 또는 쓰기 작업과 관련된 개체 인스턴스입니다. 저장 중인 인스턴스 이거나 many-to-many 관계에 추가중인 인스턴스일 수도 있습니다.

Using routers

  • 데이터베이스 라우터는 DATABASE_ROUTERS 설정을 사용하여 설치됩니다. 이 설정은 기본 라우터(django.db.router)에서 사용해야 하는 라우터를 각각 지정하는 클래스 이름 목록을 정의합니다.
  • 기본 라우터는 Django의 데이터베이스 작업에서 데이터베이스 사용량을 할당하는 데 사용됩니다. 쿼리가 사용할 데이터베이스를 알아야 할 때마다 기본 라우터를 호출하여 모델과 힌트(사용 가능한 경우)를 제공합니다. 기본 라우터는 데이터베이스 제안을 반환할 때까지 각 라우터 클래스를 차례로 시도합니다. 제안을 반환하는 라우터가 없으면 기본 라우터가 힌트 인스턴스의 현재 instance._state.db를 시도합니다. 힌트 인스턴스가 제공되지 않았거나 instance._state.db가 None인 경우 기본 라우터는 기본 데이터베이스를 할당합니다.

예시

이 예제는 라우터 인프라가 데이터베이스 사용을 변경하는 데 사용될 수 있는 방법을 보여주기 위한 것입니다. 그것은 라우터가 어떻게 사용되는지를 보여주기 위해 몇몇 복잡한 문제들을 의도적으로 무시합니다.

이 예제는 myapp의 모델 중 하나가 다른 데이터베이스 외부의 모델과의 관계를 포함하는 경우 작동하지 않습니다. 데이터베이스 간 관계는 Django가 현재 처리할 수 없는 참조 무결성 문제를 일으킵니다.

설명된 Primary/Replica(일부 데이터베이스에서는 master/slave라고 함) 구성에도 결함이 있습니다. 복제 지연(즉, 쓰기가 복제본에 전파되는 데 걸리는 시간 때문에 발생하는 쿼리 불일치)을 처리하기 위한 솔루션을 제공하지 않습니다. 또한 트랜잭션과 데이터베이스 활용 전략의 상호 작용을 고려하지 않습니다.

그렇다면 실제로 이것은 무엇을 의미할까요? 다른 샘플 configuration을 고려하겠습니다. 이것은 여러 개의 데이터베이스를 가질 것입니다. 하나는 인증 응용 프로그램을 위한 것이고, 다른 모든 앱은 두 개의 읽기 복제본이 있는 primary/replica 설정을 사용합니다. 다음은 데이터베이스를 지정하는 설정입니다.

DATABASES = {
    'default': {},
    'auth_db': {
        'NAME': 'auth_db_name',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'swordfish',
    },
    'primary': {
        'NAME': 'primary_name',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'spam',
    },
    'replica1': {
        'NAME': 'replica1_name',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'eggs',
    },
    'replica2': {
        'NAME': 'replica2_name',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'bacon',
    },
}

이제 라우팅을 처리해야 합니다. 먼저 auth 및 contenttypes 앱에 대한 쿼리를 auth_db로 보내는 것을 알고 있는 라우터가 필요합니다(인증 모델은 ContentType에 연결되므로 동일한 데이터베이스에 저장해야 합니다)

class AuthRouter:
    """
    A router to control all database operations on models in the
    auth and contenttypes applications.
    """
    route_app_labels = {'auth', 'contenttypes'}

    def db_for_read(self, model, **hints):
        """
        Attempts to read auth and contenttypes models go to auth_db.
        """
        if model._meta.app_label in self.route_app_labels:
            return 'auth_db'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth and contenttypes models go to auth_db.
        """
        if model._meta.app_label in self.route_app_labels:
            return 'auth_db'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the auth or contenttypes 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
        'auth_db' database.
        """
        if app_label in self.route_app_labels:
            return db == 'auth_db'
        return None

또한 다른 모든 앱을 primary/replica 구성으로 전송하고 읽을 복제본을 랜덤으로 선택할 경우 다음과 같이 만들면 되겠습니다.

import random

class PrimaryReplicaRouter:
    def db_for_read(self, model, **hints):
        """
        Reads go to a randomly-chosen replica.
        """
        return random.choice(['replica1', 'replica2'])

    def db_for_write(self, model, **hints):
        """
        Writes always go to primary.
        """
        return 'primary'

    def allow_relation(self, obj1, obj2, **hints):
        """
        Relations between objects are allowed if both objects are
        in the primary/replica pool.
        """
        db_set = {'primary', 'replica1', 'replica2'}
        if obj1._state.db in db_set and obj2._state.db in db_set:
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        return True

마지막으로 설정파일에서 다음을 추가합니다(라우터가 정의된 모듈에 대한 실제 파이썬 경로로 path.to를 대체합니다)

DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']

라우터가 처리되는 순서는 중요합니다. 라우터는 DATABASE_ROUTERS 설정에 나열된 순서대로 쿼리됩니다. 이 예에서 AuthRouter는 PrimaryReplicaRouter보다 먼저 처리되며, 그 결과 인증된 모델에 관한 결정은 다른 결정이 내려지기 전에 처리됩니다. 이 예에서 AuthRouter는 PrimaryReplicaRouter보다 먼저 처리되며, 그 결과 인증된 모델에 관한 결정은 다른 결정이 내려지기 전에 처리됩니다. DATABASE_ROUTERS 설정이 다른 순서로 두 라우터를 나열한 경우 PrimaryReplicaRouter.allow_migrate()가 먼저 처리됩니다.

이 설정이 설치되고 모든 데이터베이스가 데이터베이스 동기화에 마이그레이션 되면 몇 가지 Django 코드를 실행할 수 있습니다.

>>> # This retrieval will be performed on the 'auth_db' database
>>> fred = User.objects.get(username='fred')
>>> fred.first_name = 'Frederick'

>>> # This save will also be directed to 'auth_db'
>>> fred.save()

>>> # These retrieval will be randomly allocated to a replica database
>>> dna = Person.objects.get(name='Douglas Adams')

>>> # A new object has no database allocation when created
>>> mh = Book(title='Mostly Harmless')

>>> # This assignment will consult the router, and set mh onto
>>> # the same database as the author object
>>> mh.author = dna

>>> # This save will force the 'mh' instance onto the primary database...
>>> mh.save()

>>> # ... but if we re-retrieve the object, it will come back on a replica
>>> mh = Book.objects.get(title='Mostly Harmless')

이 예시는 인증 앱의 모델과의 상호 작용을 처리하는 라우터와 다른 모든 앱과의 상호 작용을 처리하는 다른 라우터를 정의했습니다. 기본 데이터베이스를 비워두고 달리 지정되지 않은 모든 앱을 처리하기 위해 포괄적인 데이터베이스 라우터를 정의하지 않으려면 마이그레이션하기 전에 라우터가 INSTALLED_APPS의 모든 앱 이름을 처리해야 합니다. 하나의 데이터베이스에 함께 있어야 하는 contrib 앱에 대한 정보는 contrib 앱의 동작을 참조하면 되겠습니다.

수동으로 데이터베이스 선택하기

Django는 또한 코드에서 데이터베이스 사용을 완벽하게 제어할 수 있는 API를 제공합니다. 수동으로 지정된 데이터베이스 할당은 라우터가 할당한 데이터베이스보다 우선합니다.

쿼리셋에 대한 데이터베이스 수동으로 선택하기

QuerySet ‘체인’에서 언제든지 QuerySet에 대한 데이터베이스를 선택할 수 있습니다. 지정한 데이터베이스를 사용하는 다른 쿼리 셋을 가져오려면 쿼리셋에서 ()를 사용하여 호출하면 되겠습니다.

using()은 쿼리를 실행하려는 데이터베이스의 멸칭인 단일 아규먼트를 사용합니다. 예시는 다음과 같습니다.

>>> # This will run on the 'default' database.
>>> Author.objects.all()

>>> # So will this.
>>> Author.objects.using('default')

>>> # This will run on the 'other' database.
>>> Author.objects.using('other')

저장할 데이터베이스 선택하기

Model.save()에 using 키워드를 사용하여 데이터를 저장할 데이터베이스를 지정합니다. 예를 들어, legacy_users 데이터베이스에 개체를 저장하려면 다음을 사용합니다.

예를 들어, legacy_users 데이터베이스에 개체를 저장하려면 다음을 사용합니다.

>>> my_object.save(using='legacy_users')

using을 지정하지 않으면 save() 메서드는 라우터가 할당한 기본 데이터베이스에 저장합니다.

한 데이터베이스에서 다른 데이터베이스로 개체 이동하기

인스턴스를 하나의 데이터베이스에 저장한 경우 인스턴스를 새 데이터베이스로 마이그레이션 하는 방법으로 save(using=…)를 사용하고 싶을 수 있습니다. 그러나 적잘한 조치를 취하지 않으면 예상치 못한 결과가 나올 수 있습니다.

다음은 그런 예시입니다.

>>> p = Person(name='Fred')
>>> p.save(using='first')  # (statement 1)
>>> p.save(using='second') # (statement 2)

statement 1 에서 새로운 person 개체가 첫 번째 데이터베이스에 저장됩니다. 이때 p에는 기본 키가 없으므로 Django는 SQL INSERT 문을 발생시킵니다. 이렇게 하면 기본키가 생성되고 Django는 해당 기본 키를 p에 할당합니다.

statement 2 에서 저장이 발생하면 p에는 이미 기본 키 값이 있으며 Django는 새 데이터베이스에서 해당 기본키를 사용하려고 시도합니다. 기본 키 값이 두 번째 데이터베이스에서 사용되지 않는 경우 아무런 문제가 없습니다. 개체가 새 데이터베이스로 복사됩니다.

그러나 p의 기본키가 이미 두 번째 데이터베이스에서 사용 중인 경우 p가 저장될 때 두번째 데이터베이스의 기존 개체가 재정의됩니다.

두 가지 방법으로 이것을 피할 수 있습니다. 먼저 인스턴스의 기본 키를 지울 수 있습니다. 개체에 기본키가 없으면 Django는 새 개체로 취급하여 두 번째 데이터베이스의 데이터 손실을 방지합니다.

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.pk = None # Clear the primary key.
>>> p.save(using='second') # Write a completely new object.

두 번째 방법은 Django가 SQL INSERT를 수행하도록 하기 위해 save()에 force_insert 옵션을 사용하는 것입니다.

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.save(using='second', force_insert=True)

이렇게 하면 Fred라는 사람이 두 데이터베이스에서 동일한 기본 키를 갖게 됩니다. 두 번째 데이터베이스에 저장하려고 할 때 해당 기본 키가 이미 사용 중인 경우 오류가 발생합니다.

데이터베이스 지정해서 지우기

기본적으로 기존 개체를 삭제하는 호출은 처음에 개체를 검색하는 데 사용된 동일한 데이터베이스에서 실행됩니다.

>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database

모델을 삭제할 데이터베이스를 지정하려면 using() 키워드 아규먼트를 Model.delete() 메서드에 전달합니다. 이 아규먼트는 save()에 키워드 아규먼트를 사용하는 것과 동일하게 작동합니다.

예를 들어, 사용자를 legacy_users 데이터베이스에서 new_users 데이터베이스로 마이그레이션하는 경우 다음 명령을 사용할 수 있습니다.

>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')

여러 데이터베이스에서 Admin 사용하기

관리자에게 기본이 아닌 데이터베이스에 대한 액세스 권한을 부여하려면 관리자에서 db_manager() 메서드를 사용합니다.

예를 들어 데이터베이스에 접근하는 custom manager method인 User.objects.create_user()가 있다고 가정합니다. create_user()는 QuerySet method가 아니라 Manage method이기 때문에 User.objects.using(’new_users’).create_user()를 할 수 없습니다. [create_user() 메서드는 관리자인 User.objects에서만 사용할 수 있으며 관리자에서 파생된 QuerySet 객체에서는 사용할 수 없습니다.) 해결 방법은 다음과 같이 db_manager()를 사용하는 것입니다.

User.objects.db_manager('new_users').create_user(...)

db_manager()는 지정한 데이터베이스에 바인딩된 관리자의 복사본을 반환합니다.

여러 데이터베이스에서 get_queryset() 사용하기

관리자에서 get_queryset()을 재정의하는 경우 부모에서 메서드를 호출하거나(super() 사용) 관리자에서 _db속성(데이터베이스 이름이 포함된 문자열을 적절하게 사용해야합니다.)

예를 들어 get_queryset 메서드에서 사용자 지정 QuerySet 클래스를 반환하려는 경우 다음과 같이 할 수 있습니다.

class MyManager(models.Manager):
    def get_queryset(self):
        qs = CustomQuerySet(self.model)
        if self._db is not None:
            qs = qs.using(self._db)
        return qs

Django의 Admin 인터페이스에서 여러 데이터베이스 Expose하기

Django의 관리자는 여러 데이터베이스를 명시적으로 지원하지 않습니다. 라우터 체인에서 지정하지 않은 데이터베이스의 모델에 대한 관리 인터페이스를 제공하려면 관리자가 콘텐츠에 특정 데이터베이스를 사용하도록 지시하는 사용자 지정 ModelAdmin 클래스를 작성해야합니다.

ModelAdmin 개체에는 다중 데이터베이스 지원을 위해 사용자 지정이 필요한 다음 메서드가 있습니다.

class MultiDBModelAdmin(admin.ModelAdmin):
    # A handy constant for the name of the alternate database.
    using = 'other'

    def save_model(self, request, obj, form, change):
        # Tell Django to save objects to the 'other' database.
        obj.save(using=self.using)

    def delete_model(self, request, obj):
        # Tell Django to delete objects from the 'other' database
        obj.delete(using=self.using)

    def get_queryset(self, request):
        # Tell Django to look for objects on the 'other' database.
        return super().get_queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super().formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)

여기에 제공된 구현은 주어진 유형의 모든 개체가 특정 데이터베이스에 저장되는 다중 데이터베이스 전략을 구현합니다.(예 : 모든 개체는 다른 데이터베이스에 있습니다.) 여러 데이터베이스의 사용이 더 복잡한 경우 ModelAdmin이 해당 전략을 반영해야 합니다.

InlineModelAdmin 개체는 비슷한 방식으로 처리할 수 있습니다. 세 가지 맞춤형 방법이 필요합니다.

class MultiDBTabularInline(admin.TabularInline):
    using = 'other'

    def get_queryset(self, request):
        # Tell Django to look for inline objects on the 'other' database.
        return super().get_queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super().formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)

모델 관리 정의를 작성한 후에는 모든 관리 인스턴스에 등록할 수 있습니다.

from django.contrib import admin

# Specialize the multi-db admin objects for use with specific models.
class BookInline(MultiDBTabularInline):
    model = Book

class PublisherAdmin(MultiDBModelAdmin):
    inlines = [BookInline]

admin.site.register(Author, MultiDBModelAdmin)
admin.site.register(Publisher, PublisherAdmin)

othersite = admin.AdminSite('othersite')
othersite.register(Publisher, MultiDBModelAdmin)

이 예에서는 두 개의 관리 사이트를 설정합니다. 첫 번째 사이트에서 Author 및 Publisher 개체가 노출됩니다. 개시자 개체에는 해당 게시자가 게시한 책을 보여주는 표 형식 인라인이 있습니다. 두 번째 사이트는 인라인 없이 게시자만 노출됩니다.

여러 데이터베이스에서 raw cursor 사용

두개 이상의 데이터베이스를 사용하는 경우 django.db.connections를 사용하여 특정 데이터베이스에 대한 connection 및 cursor을 얻을 수 있습니다.

django.db.connections는 alias를 사용하여 특정 연결을 검색할 수 있는 dict와 같은 객체입니다.

from django.db import connections
with connections['my_db_alias'].cursor() as cursor:
    ...

다중 데이터베이스의 제한 사항

데이터베이스 간 관계(Cross-database relations)

Django는 현재 외래 키 또는 여러 데이터베이스에 걸친 many-to-many 관계에 대한 지원을 제공하지 않습니다. 라우터를 사용하여 모델을 다른 데이터베이스로 분할한 경우 해당 모델에서 정의한 외래 키 및 다대다 관계는 단일 데이터베이스 내부에 있어야 합니다.

이는 참조 무결성 때문입니다. 두 객체 간의 관계를 유지하기 위해 Django는 관련 객체의 primary key가 유효한지 알아합니다. primary key가 별도의 데이터베이스에 저장되어 있으면 primary key의 유효성을 쉽게 평가할 수 없습니다.

Postgres, Oracle 또는 MySql을 InnoDB와 함께 사용하는 경우 이는 데이터베이스 integrity level에서 적용됩니다. 데이터베이스 level key 제약 조건은 유효성을 검사할 수 없는 관계 생성을 방지합니다.

Contrib Apps의 동작

여러 contrib 앱에는 모델이 포함되어 있으며 일부 앱은 다른 앱에 의존합니다. 데이터베이스 간 관계는 불가능하므로 이러한 모델을 데이터베이스 간에 분할할 수 있는 방법에 몇 가지 제한이 있습니다.

  • 각각의 contenttypes.ContentType, sessionis.Session 그리고 sites.Site는 적합한 라우터가 있는 모든 데이터베이스에 저장할 수 있습니다.
  • 인증 모델(User,Group 및 Permission)은 함께 연결되고 ContentType에 연결되므로 ContentType과 동일한 데이터베이스에 저장해야 합니다.
  • admin은 인증에 의존하므로 해당 모델은 인증과 동일한 데이터베이스에 있어야 합니다.
  • 플랫 페이지 및 redirects는 사이트에 따라 다르므로 해당 모델은 사디트와 동일한 데이터베이스에 있어야 합니다.
  • 또한 일부 객체는 마이그레이션이 데이터베이스에 보관할 테이블을 생성한 직후에 자동으로 생성됩니다.
  • 기본 사이트
  • 각 모델에 대한 ContentType(해당 데이터베이스에 저장되지 않은 모델 포함)
  • 각 모델에 대한 권한(해당 데이터베이스에 저장되지 않은 권한 포함)

여러 데이터베이스가 있는 일반적인 설정의 경우 이러한 개체를 둘 이상의 데이터베이스에 두는 것은 유용하지 않습니다. 일반적인 설정에는 기본/복제 및 외부 데이터베이스에 대한 연결이 포함됩니다. 따라서 이 세 가지 모델을 하나의 데이터베이스에만 동기화 할 수 있는 데이터베이스 라우터를 작성하는 것이 좋습니다. 여러 데이터베이스의 테이블이 필요하지 않은 contrib 및 서드파티 앱에 대해 동일한 접근 방식을 사용합니다.

 

참고 링크

https://docs.djangoproject.com/en/4.1/topics/db/multi-db/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

댓글