Django 指定模块使用哪个数据库
Django specify which database to use for module
在我的 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()
但是这个模块对 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',]
在我的 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()
但是这个模块对 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',]