Help us understand the problem. What is going on with this article?

【Django】同じappで複数のDBを使用する方法

投稿にあたって

  • 自分用のメモでもあります。
  • Djangoでは単一APPにたいして一つのDBを使用する記事は多く散見しましたが、同じAPPで複数DBを使用する場合の記事が少なかったので備忘録として残します。
  • またrest_frameworkを使用した記事となるため主にvalidationでの複数DB使用がメインになります。

目標

  • rest_frameworkのviewsで複数のDBを参照できる
  • rest_frameworkのviewsで複数のDBを更新できる

前提

  • settings.pyに default 及び その他DBがあることを前提とします。
  • 下記例です。
DATABASES = {
    'default': {
         'ENGINE': 'your_engine',
         'NAME': 'database1',
         'USER': 'your_user',
         'PASSWORD': 'your_passwd',
         'HOST': '127.0.0.1', # your_host
         'PORT': '5432', # your_port
    },
    'database2': {
         'ENGINE': 'your_engine',
         'NAME': 'database2',
         'USER': 'your_user',
         'PASSWORD': 'your_passwd',
         'HOST': '127.0.0.1', # your_host
         'PORT': '5432', # your_port
    },
}

実際にコードを書いていきましょう

models

モデルはデータベースの制限を受けません。
というか、データベースの設定が出来ません。悲しい。
なのでデータベースの垣根なく書きます。

from django.db import models

# 例えばこっちがdatabase1
class DatabaseOne(models.Model):
    id = models.Integer(primary_key = True)
    column_one = models.CharField(max_length = 16)

# こっちはdatabase2
class DatabaseTwo(models.Model):
    id= models.Integer(primary_key = True)
    column_two = models.CharField(max_length = 32)

# 外部キーを持ったdatabase2
class DatabaseTwoChild(models.Model):
    db2 = models.OneToOneField(DatabaseTwo, on_delete = models.CASCADE, primary_key = True)
    child_column = models.CharField(max_length = 16)

参照

これは簡単ですね。
serializer等の設定も不要なため、単純にdb_managerを間に設定するだけで出来ちゃいます。
まぁ簡単!

views

from rest_framework.response import Response
from rest_framework import viewsets
from .models import DatabaseOne, DatabaseTwo

class TestViewSet(viewsets.ModelViewSet):
    queryset = ''
    http_method_names = ["get"] # これでGETしかできないようになります

    def list(self, request):
        db1 = DatabaseOne.objects.all() #ここまでは普通のqueryset
        db2 = DatabaseTwo.objects.db_manager("database2").all() # default以外のDB
        return Response({"result": "オッケー!"})

更新・作成

こちらは少し癖があります。
まずChildを更新しようと思うと親がdefaultではないので、serializerに少し手を加えないと、defaultのデータベースを見に行こうとしてquerysetがエラーを吐きます。

serializer

from rest_framework import serializers
from .models import DatabaseOne, DatabaseTwo, DatabaseTwoChild
from rest_framework.validators import UniqueValidator # これは外部キーを使用している場合に使用します

class DatabaseTwoChildSerializer(serializers.ModelSerializer):
    class Meta:
        model = DatabaseTwoChild
        fields = "__all__"
    db2 = serializers.PrimaryKeyRelatedField(
        queryset = Database2.objects.using("database2").all(),
        validators = [UniqueValidator(queryset = DatabaseTwoChild.objects.using("database2").all())]
    )

   def create(self, data):
        save_data = DatabaseTwoChild(**data)
        save_data.save(using="database2")
        return save_data

これで下準備OKです!
ではviewしていきましょう

views

from rest_framework.response import Response
from rest_framework import viewsets
from .serializerimport import DatabaseTwoChildSerializer
from .models import DatabaseOne, DatabaseTwo

class TestViewSet(viewsets.ModelViewSet):
    queryset = ''
    http_method_names = ["post"] # これでPOSTしかできないようになります

    def create(self, request):
        serializer = DatabaseTwoSerializer(data = {
            "child_column": "hogehoge",
        })
        if serializer.is_valid():
            serializer.save()
        else:
            return Response({"result": "エラーだよ!"})
        return Response({"result": "おっけー!"})

これでdefaultでないDBも更新できます。
ユーザー情報と他の情報とかでデータベースを分けたい場合に便利です。
またserializerの機能をしっかり使ってあげることで、型のチェックがキッチリできるので、ぜひ使ってみてください。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away