Django 指定模块使用哪个数据库

Django specify which database to use for module

在我的 Django 项目中,我有几个应用程序,其中之一是 email_lists,这个应用程序执行大量数据处理,从模型 Customers 读取数据。在我的生产环境中,我有两个数据库:defaultread-replica。我希望特定模块中的所有查询都针对 replica-set 数据库进行。

如果我明确告诉查询这样做,我就能做到:

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

但是这个模块对 Customer 和其他模型有 100 多个查询。我也对以下关系有疑问:

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 中可行吗,有没有捷径?

谢谢

你可以在 django database routers 上阅读这方面的内容,一些很好的例子也可以在网上找到,它们应该很简单。

--

另一个解决方案是修改模型管理器。

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,经理应该做路由。

我仍然建议使用数据库路由器解决方案,并在 class 中创建自定义逻辑。但是,如果经理为您工作,那很好。

如果您希望 email_lists 应用程序中的所有查询都查询只读副本,那么路由器是可行的方法。如果您需要在同一个应用程序中查询不同的数据库,那么@ibaguio 的解决方案是可行的方法。这是一个类似于我正在使用的基本路由器示例:

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)

在settings.py中:

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

看起来你只需要在生产中使用它,所以我认为你可以有条件地添加它:

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