【问题标题】:Django specify which database to use for moduleDjango 指定用于模块的数据库
【发布时间】:2020-07-12 08:44:26
【问题描述】:

在我的 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()

但是这个模块有超过 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 中是否可行,有什么快捷方式吗?

谢谢

【问题讨论】:

    标签: django postgresql django-models django-database


    【解决方案1】:

    你可以在 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,然后经理应该做路由。

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

    【讨论】:

    • 数据库路由器接收一个模型,所以我可以将模型的查询路由到副本,hints 属性只包含一个实例。我不知道如何将模块路由到副本或来自应用程序的所有查询都路由到副本。
    • 所以你说你有一个模型,当模块A查询时,你需要将它查询到默认值,当模块B查询时,你需要它路由到副本?
    • 我有另一个使用模型管理器/查询集的想法。给我几分钟的时间来编写一个伪代码
    • 是的,来自email_lists.views.py 文件的所有查询都应该针对副本进行,所有其他查询都默认。
    【解决方案2】:

    如果您希望 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',]
    

    【讨论】:

      猜你喜欢
      • 2014-09-16
      • 2011-03-31
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 2013-11-26
      • 2020-06-28
      • 1970-01-01
      相关资源
      最近更新 更多