我做了类似的,最接近点1,但不是使用中间件来设置默认连接Django数据库路由器。这允许应用程序逻辑使用多个数据库(如果每个请求都需要)。它取决于应用程序逻辑为每个查询选择一个合适的数据库,这是这种方法的最大缺点。
使用此设置,所有数据库都列在settings.DATABASES中,包括可能在客户之间共享的数据库。每个特定于客户的模型都放置在具有特定应用标签的Django应用中。
例如。以下类定义了存在于所有客户数据库中的模型。
class MyModel(Model):
....
class Meta:
app_label = 'customer_records'
managed = False
数据库路由器放置在settings.DATABASE_ROUTERS链中,以通过app_label路由数据库请求,类似这样的东西(不是一个完整的示例):
class AppLabelRouter(object):
def get_customer_db(self, model):
# Route models belonging to 'myapp' to the 'shared_db' database, irrespective
# of customer.
if model._meta.app_label == 'myapp':
return 'shared_db'
if model._meta.app_label == 'customer_records':
customer_db = thread_local_data.current_customer_db()
if customer_db is not None:
return customer_db
raise Exception("No customer database selected")
return None
def db_for_read(self, model, **hints):
return self.get_customer_db(model, **hints)
def db_for_write(self, model, **hints):
return self.get_customer_db(model, **hints)
这个路由器的特殊部分是thread_local_data.current_customer_db()调用。在路由器被执行之前,调用者/应用程序必须在thread_local_data中设置当前客户db。 Python上下文管理器可以用于此目的推送/弹出当前客户数据库。
使用所有这些配置,应用程序代码然后看起来像这样,其中UseCustomerDatabase是一个上下文管理器,将当前客户数据库名推入/弹出到thread_local_data,以便thread_local_data.current_customer_db()将返回正确的数据库名称,当路由器最终击中:
class MyView(DetailView):
def get_object(self):
db_name = determine_customer_db_to_use(self.request)
with UseCustomerDatabase(db_name):
return MyModel.object.get(pk=1)
这是一个相当复杂的设置已经。它的工作,但我会尝试总结我看到的优点和缺点:
优点
>数据库选择灵活。它允许在单个查询中使用多个数据库,客户特定和共享数据库都可以在请求中使用。
>数据库选择是显式的(不确定这是一个优点或缺点)。如果尝试运行查询命中客户数据库但应用程序尚未选择一个查询,则会出现异常,指示编程错误。
>使用数据库路由器允许不同的数据库存在于不同的主机上,而不是依赖于USE数据库;语句,猜测所有数据库都可以通过单个连接访问。
缺点
>它的设置很复杂,有相当多的层涉及到它的功能。
>线程本地数据的需要和使用是模糊的。
>视图使用数据库选择代码。这可以使用基于类的视图来抽象以基于请求参数自动选择数据库,其方式与中间件将选择默认数据库相同。
>选择数据库的上下文管理器必须以查询被评估时上下文管理器仍然处于活动状态的方式包围查询集。
建议
如果你想要灵活的数据库访问,我建议使用Django的数据库路由器。使用中间件或视图Mixin,它会根据请求参数自动设置用于连接的默认数据库。您可能必须诉诸于线程本地数据来存储要使用的默认数据库,以便在路由器被命中时,它知道要路由到哪个数据库。这允许Django使用其对数据库(如果需要可驻留在不同主机上)的现有持久连接,并根据请求中设置的路由选择要使用的数据库。
此方法还具有以下优点:如果需要,可以通过使用QuerySet using()功能来选择除缺省值之外的数据库来重写用于查询的数据库。