Django多个和动态数据库

49

我一直在评估django,并想知道以下是否可行。我已经查看了常规的多个数据库文档,所以请不要向我指出那些内容,因为我没有发现这种用例。如果我的说法有误,我会撤回它 :)

我想要一个主数据库,大多数应用程序模型将驻留在其中,但是其中一个应用程序需要动态创建数据库,这些将是特定于客户的数据库。

数据库路径(我打算使用sqlite)将存储在主数据库中,因此游标需要更改,但模型将保持不变。

我欢迎任何关于如何实现这一目标的想法?


为什么你一定要使用多个数据库?有没有理由不能调整你的应用程序模型架构,以保存每个客户的信息?为每个客户使用多个数据库是非常不规则的。 - Lyle Pratt
我目前正在尝试确定适当的架构。我不一定要使用多个数据库,但这是提供可移植、在线和离线服务的选项之一。为此,我想找出是否可能实现这一点,从概念上来说相当简单,但我知道在一个通用框架中支持这一点可能会很棘手,所以我想看看是否有人做过类似的事情。 - nickjb
3
可能是[Django:动态数据库文件](https://dev59.com/9mYq5IYBdhLWcg3wui0z)的重复问题(注意:链接的问题似乎提供了一个适当的解决方案,不需要在添加/更改/删除新数据库时重新启动服务器的解决方法)。 - mgibsonbr
@LylePratt 如果你有多租户,每个客户需要一个新的数据库。 - kta
3个回答

39
我将从 "不应在运行时编辑设置" 开始讲起。
虽然如此,我也有同样的问题,即我想为每个用户创建一个唯一的数据库。这样做的原因是我提供了用户保存/访问不存储在我的服务器上的数据库的功能,这需要有多个数据库,因此需要为每个用户创建一个数据库。
这个答案并不是实现所需目标的推荐方式。我希望能听到django大师如何最好地解决这个问题。但是,这是我一直在使用的解决方案,而且到目前为止效果很好。我正在使用sqlite,但它可以轻松修改为任何数据库。
总之,这是整个过程:
1. 在设置中添加新的数据库(在运行时) 2. 创建一个文件来存储这些设置以便在服务器重新启动时重新加载(在运行时) 3. 运行一个脚本,以加载保存的设置文件(每当服务器重新启动时)
那么,如何实现呢?
首先,当创建新用户时,我会在设置中创建一个新的数据库。这段代码位于我创建新用户的视图中。
from YOUR_PROJECT_NAME import settings
database_id = user.username #just something unique
newDatabase = {}
newDatabase["id"] = database_id
newDatabase['ENGINE'] = 'django.db.backends.sqlite3'
newDatabase['NAME'] = '/path/to/db_%s.sql' % database_id
newDatabase['USER'] = ''
newDatabase['PASSWORD'] = ''
newDatabase['HOST'] = ''
newDatabase['PORT'] = ''
settings.DATABASES[database_id] = newDatabase
save_db_settings_to_file(newDatabase) #this is for step 2)

这个脚本可以在运行时将数据库设置加载到django项目设置中。但是,如果服务器重启,这个数据库将不再存在设置中。
2)为了方便在服务器重新启动时自动重新加载这些设置,我为每个数据库创建一个文件,这个文件将在服务器启动时加载。创建这个文件的功能是由save_db_settings_to_file函数执行的。
def save_db_settings_to_file(db_settings):
    path_to_store_settings = "/path/to/your/project/YOUR_PROJECT_NAME/database_settings/"
    newDbString = """
DATABASES['%(id)s'] = {
    'ENGINE': '%(ENGINE)s', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
    'NAME': '%(NAME)s',                      # Or path to database file if using sqlite3.
    'USER': '',                      # Not used with sqlite3.
    'PASSWORD': '',                  # Not used with sqlite3.
    'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
    'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
}
    """ % db_settings
    file_to_store_settings = os.path.join(path_to_store_settings, db_settings['id'] + ".py")
    write_file(file_to_store_settings, newDbString) #psuedocode for compactness

3) 为了在服务器启动时加载这些设置,我需要在 /path/to/your/project/YOUR_PROJECT_NAME/settings.py 的最底部添加一行代码,用于加载设置文件夹中的每个文件并运行它,从而实现将数据库详细信息加载到设置中。

import settings_manager

然后,import settings_manager 将会加载位于 /path/to/your/project/YOUR_PROJECT_NAME/settings_manager.py 的文件,该文件包含以下代码:

from settings import DATABASES
import os

path_to_store_settings = "/path/to/your/project/YOUR_PROJECT_NAME/database_settings/"
for fname in os.listdir(path_to_settings):
    full_path = os.path.join(path_to_settings, fname)
    f = open(full_path)
    content = f.read()
    f.close()
    exec(content) #you'd better be sure that the file doesn't contain anything malicious

请注意,您可以直接将此代码放置在settings.py的底部,而不是使用import语句,但使用import语句可以保持settings.py的抽象级别一致。
这是一种方便的加载每个数据库设置的方式,因为要从设置中删除数据库,您只需删除设置文件,下一次服务器重新启动时,它就不会将这些详细信息加载到设置中,因此数据库将无法访问。
正如我所说,这个方法可行,我已经成功地使用过了,但这并不是理想的解决方案。如果有人能发布一个更好的解决方案,我将不胜感激。
以下是它的不足之处:
  • 它明确违反了django团队不在运行时修改设置的建议。我不知道为什么给出这个建议。
  • 它使用exec语句将数据加载到设置中。这应该没问题,但如果某个文件中有一些损坏或恶意代码,你将会变成一个伤心的熊猫。
请注意,我仍然在使用默认的数据库来存储auth和sessions数据,但我的应用程序中所有的数据都存储在用户特定的数据库中。

1
我提出了一种不同的解决方案,尽管我已经实现了您的方法(通过文件)和我的方法,使用数据库本身作为这些文件的存储。 settings.py 的最后一行加载包含数据库定义的所有模型,并将它们添加到 Django 的 DATABASE 设置中。 - George Silva
2
如果您在主数据库(用于管理用户的数据库)中编写db连接,那么它可以比文本文件更加优雅。 - JulienD
这个程序对“节流限制”也适用吗? - MadhuP
你能分享一下你是怎么做到的吗?可以将脚本作为答案示例分享给我和@JulienD吗?我正在尝试这个。 - Gary
1
@Gary 我的意思是在存储用户(或项目等)的“主”数据库中,您可以添加一个列,其中包含应为该用户使用的“个人”数据库的名称。然后,当应用程序为用户执行时,它从“主”数据库中读取db名称,并使用它打开到“个人”数据库的连接。这使得通过访问您自己的(安全)API,在执行时间内切换数据库成为可能。 - JulienD
好的,我试图做的与上面评论中的类似。我不理解的是这里。动态数据库是一个单独的数据库,由一个模块访问。主数据库连接到一个名为mod_customerdb_records的模块,用于在客户数据库记录中创建和执行CRUD操作。现在,从mod_ecomm中我访问动态数据库和主数据库,根据需要更改数据库连接以访问客户数据。这是建议的架构,还是有更好的解耦架构可用? - Gary

14
为了补充@thedawnrider的回答,在某些情况下修改settings.DATABASES可能不足够。更可靠的方法可能是编辑django.db.connections.databases,它作为settings.DATABASES的缓存和包装器。
例如:
from django.db import connections
database_id = user.username #just something unique
newDatabase = {}
newDatabase["id"] = database_id
newDatabase['ENGINE'] = 'django.db.backends.sqlite3'
newDatabase['NAME'] = '/path/to/db_%s.sql' % database_id
newDatabase['USER'] = ''
newDatabase['PASSWORD'] = ''
newDatabase['HOST'] = ''
newDatabase['PORT'] = ''
connections.databases[database_id] = newDatabase

4
这个问题已经过时了,应该更新一下。
Django支持开箱即用的多数据库功能。
有很多好的教程[1, 2]和答案可以教你如何动态管理连接。
你还可以简单地使用django-dynamic-db-router
通过使用settings对象的configure方法,可以在运行时配置数据库连接设置:
from django.conf import settings

dbs = settings.DATABASES.copy()
dbs['some_new_db'] = {'ENGINE': 'dummy'}

settings.configure(DATABASES=dbs)

1
问题是关于动态数据库,这些数据库可以在运行时添加或删除,而不是静态定义的数据库。 - Mu Sa
@MuSa 没问题,只需在 DB 路由器的 db_for_read 方法中添加 DB 创建逻辑,Django 将在每次从 DB 读取时创建新的数据库。db_for_write 同理。我只是想说所有功能都已经内置了。其他的东西取决于你的想象力。 - rzlvmp
db_for_readdb_for_write是用于数据库路由的方法,而不是用于创建数据库或数据库连接的方法。数据库连接在设置文件中定义(硬编码),并且设置文件只会被加载一次。在您的所有链接中,数据库都在设置文件中定义,没有链接提到如何在运行时创建新的数据库连接。新的数据库连接应该以某种方式存储并在每次服务器重新启动时重新加载。 - Mu Sa
@MuSa 是的,你说得对。在我的评论之后,我明白了你的意思,并添加了有关“configure”的信息。这种方法允许在飞行中添加/编辑新连接(例如,如果更新了“settings.py”)。新数据库连接应该以某种方式存储 → 我认为最好的解决方案是创建硬编码连接DB来存储其他连接。并且db_for_read内部的适当逻辑将允许这样做(如果连接仍不存在,则存储在DB中)。这比创建类似于“python”的文件并手动转义所有引号符等更灵活和可靠。 - rzlvmp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接