Django указать, какую базу данных использовать для модуля - PullRequest
3 голосов
/ 31 марта 2020

В моем проекте Django у меня есть пара приложений, одно из которых - email_lists, и это приложение выполняет большую часть обработки данных, считывая данные из модели Customers. В моей производственной среде у меня есть две базы данных: default и read-replica. Я хотел бы, чтобы все запросы в конкретном модуле выполнялись к базе данных replica-set.

Я могу сделать это, если я явно скажу запросу сделать это:

def get_customers(self):
    if settings.ENV == 'production':
        customers = Customer.objects.using('read-replica').filter()
    else:
        customers = Customer.objects.filter()

, но этот модуль имеет более 100 запросов к Customer и другим моделям. У меня также есть запросы к отношениям вроде:

def get_value(self, customer):
    target_sessions = customer.sessions.filter(status='open')
    carts = Cart.objects.filter(session__in=target_sessions)

Идея состоит в том, что я хочу избежать написания:

if settings.ENV == 'production':
    instance = Model.objects.using('read-replica').filter()
else:
    instance = Model.objects.filter()

для каждого запроса. В моем проекте есть и другие места, которые нужно читать из базы данных default, поэтому это не может быть глобальным параметром. Мне просто нужен этот модуль или файл для чтения с использованием реплики.

Возможно ли это в Django, есть ли ярлыки?

Спасибо

Ответы [ 2 ]

4 голосов
/ 31 марта 2020

Вы можете прочитать на django маршрутизаторах базы данных , для этого есть несколько хороших примеров, которые можно найти в Интернете, и они должны быть простыми.

-

Другим решением было бы модифицировать Менеджер моделей.

from django.db import models


class ReplicaRoutingManager(models.Manager):
    def get_queryset(self):
        queryset = super().get_queryset(self)

        if settings.ENV == 'production':
            return queryset.using('read-replica')

        return queryset


class Customer(models.Model):

    ...
    objects = models.Manager()
    replica_objects = ReplicaRoutingManager()

с этим, вы можете просто использовать обычный Customer.objects.filter, и менеджер должен выполнить маршрутизацию.

Я все еще предлагаю перейти с решение для маршрутизатора базы данных и создание собственного класса c в классе. Но если менеджер работает на вас, это нормально.

1 голос
/ 31 марта 2020

Если вы хотите, чтобы все запросы в приложении email_lists запрашивали реплику для чтения, то маршрутизатор - это путь к go. Если вам нужно запрашивать разные базы данных в одном и том же приложении, то решение @ ibaguio - это способ go. Вот базовый c пример маршрутизатора, аналогичный тому, который я использую:

project / database_routers.py

MAP = {'some_app': 'default',
       'some_other_app': 'default',
       'email_lists': 'read-replica',}

class DatabaseRouter:

    def db_for_read(self, model, **hints):
        return MAP.get(model._meta.app_label, None)

    def db_for_write(self, model, **hints):
        return MAP.get(model._meta.app_label, None)

    def allow_relation(self, object_1, object_2, **hints):
        database_object_1 = MAP.get(object_1._meta.app_label)
        database_object_2 = MAP.get(object_2._meta.app_label)
        return database_object_1 == database_object_2

    def allow_migrate(self, db, app_label, model=None, **hints):
        return MAP.get(app_label, None)

In settings.py:

DATABASE_ROUTERS = ['project.database_router.DatabaseRouter',]

Похоже, вы хотите, чтобы он был только в производстве, поэтому я думаю, вы можете добавить его условно:

if ENV == 'production':
    DATABASE_ROUTERS = ['project.database_router.DatabaseRouter',]
...