LoginSignup
32
31

More than 3 years have passed since last update.

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

Posted at

投稿にあたって

  • 自分用のメモでもあります。
  • 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の機能をしっかり使ってあげることで、型のチェックがキッチリできるので、ぜひ使ってみてください。

32
31
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
31