【问题标题】:django one app one model multiple databasesdjango 一应用一模型多数据库
【发布时间】:2016-10-11 16:35:24
【问题描述】:


我是 django webapp 开发的新手,我遇到了一个问题。我创建了 1 个具有 1 个模型的应用程序,以使用 1 个表单将数据插入数据库。我将为此使用多个数据库。每个数据库将有 1 个具有相同结构的表(目前)。现在我的问题是:

我怎样才能只为多个数据库及其各自的表使用 1 个模型、1 个视图和 1 个表单。应该打开数据库和表调用它们各自的 url。

例如http://www.example.com/x/abc/ 将访问第一个数据库及其所有操作的表。
http://www.example.com/y/abc/ 将访问第二个数据库

我已经尝试过 django 文档中提供的示例数据库路由,但它并没有多大帮助。我也找不到解决此特定问题的相关帖子/问题

我想这样做是因为稍后我将添加更多模型和表单来访问数据库表中的数据,这对我来说似乎是最干净的方式

PS:我使用的是 django 1.9.6

【问题讨论】:

  • 你必须有单独的数据库,因为......?您是否考虑过使用 url 路径段作为标识符,并利用该标识符作为外键来过滤数据的替代方案?
  • 因为里面的表结构相同但数据不同,所以现在的url问题不大。问题是“多个数据库的 1 个模型、1 个视图和 1 个表单”以避免大量重复。
  • 数据不同会有什么不同?这一切都由外键隔离。外键方法将您的复杂性降低了一个数量级。

标签: python django postgresql django-models django-1.9


【解决方案1】:

无论这是否是构建应用程序的好方法,您都可以通过 using 告诉 Django 读取和写入哪个数据库:

Person.objects.using('db1').create(...)
Person.objects.using('db2').create(...)

因此您不需要使用路由器,只需在您的设置中定义两个数据库并在两者上运行 migrate。您的模型表将在每个数据库中创建,并且在您的代码中,您可以根据您选择的任何逻辑(例如,基于请求路径)从两个数据库中读取和写入。

https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#manually-selecting-a-database

【讨论】:

  • 您还需要在 settings.py 中定义所有数据库 - 目前您可能只定义了一个默认数据库
  • 谢谢。我也阅读了这种方法并将使用它。但我想知道是否有另一种解决方案。感谢您的帮助
【解决方案2】:

我之所以做出回应,是因为您的“我想知道是否有其他解决方案”的评论。有。我在一个网站上启动并运行它......一个应用程序的多个 SQLite 数据库。您还提到了 db router 问题,我也遇到了这些问题。

首先,将包含以下内容的router.py 文件放在任何位置:

class Router(object):

    appname = ''

    def db_for_read(self, model, **hints):
        """
        Attempts to read self.appname models go to model.db.
        """
        if model._meta.app_label == self.appname:
            return model.db
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write self.appname models go to model.db.
        """
        if model._meta.app_label == self.appname:
            return model.db
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the self.appname app is involved.
        """
        if obj1._meta.app_label == self.appname or \
           obj2._meta.app_label == self.appname:
           return True
        return None

    # This is possibly the new way, for beyond 1.8.
    ''' 
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the self.appname app only appears in the self.appname
        database.
        """
        if app_label == self.appname:
            return db == self.appname
        return None
    '''

    # Contrary to Djano docs this one works with 1.8, not the one above.
    def allow_migrate(self, db, model):
            """
            Make sure the self.appname app only appears in the self.appname
            database.
            """
            if db == self.appname:
                return model._meta.app_label == self.appname
            elif model._meta.app_label == self.appname:
                return False
            return None

我只用 Django 1.8 测试过这个;当您使用 1.9 时,至少根据文档,您必须使用另一个 allow_migrate

特别注意:(1)Router()没有带有属性的松鼠基类,这意味着可以很容易地使用Python的type函数来克隆它;并且,(2) 显然可以通过外部命名空间在 Router() 内部访问当前模型实例。

现在,在您的models.py 中,执行以下操作:

from django.db import models

import os
appname = os.path.dirname(__file__).split('/')[-1]
from dirwhereyouputtherouter.router import Router
router = type( appname, (Router,), dict(appname=appname) )

class Book(models.Model):

    # This is the default, for use when there is only one db per app.
    db = appname 

    # the various fields here, etc.

为每个模型执行此操作。然后Router() 在遇到任何查询时将return model.db。在我的方案中,我在这里选择保留默认的 Django 方案……应用程序从其目录的名称中获取它的名称。

现在,在settings.py 中,您需要DATABASE_ROUTERS = [ 'appdir.models.router', ]。这通过router 引导查询,我们使用type() 函数在models.py 中初始化。特别注意,我没有在DATABASE_ROUTERS 中列出default 数据库。我确实有一个,它列在DATABASES 设置中,default 作为键。当您进行初始迁移时,各种 Django 应用程序表最终将保存在 default 数据库中,当然,默认情况下,Router() 将退到一边。

因此,在您的视图中,您将从Book.db = x 开始,其中视图签名将是myview(request, x, someothername):。您可能希望用try: finally: 覆盖整个视图主体,在finally 块中,您可以将数据库的选择恢复为默认值。

我必须承认,在迁移方面可能有美中不足。我讨厌那些,就我而言,每当我更新我的小 SQLite 数据库时,我都会重写它们的全部内容,这很频繁。因此,不必担心保存数据,只要我需要进行更改,我就会将它们和 Migrations 文件夹扔掉。但如果您的情况不同,您可能不得不使用migratemakemigrations 命令。我的每个数据库都有相同的模式,由模型定义。所以我实际上首先通过在DATABASES 设置中临时放置一个条目来创建一个空白数据库,该设置具有应用程序的名称作为键。我用它来创建 SQLite db 文件的一个副本,然后在我想添加一个新数据库时简单地复制它并重命名它(将新数据库的详细信息添加到 DATABASES 并删除具有 appname 的临时条目钥匙)。或者,您可能希望保留并充分利用 DATABASES 中的键是应用程序名称的数据库。

但遗憾的是,我们还没有完成。我必须修复admin.py。在admin.pyclass BookAdmin(admin.ModelAdmin): 中创建后,以通常的方式,您将无法在管理员中找到您的两个数据库。所以我的admin.py 看起来像:

from django.contrib import admin
from django.conf import settings
import os

class BookAdmin(admin.ModelAdmin):

    list_display = ...
    list_filter = ...
    etc.

from models import Book


appname = os.path.dirname(__file__).split('/')[-1]

dbkeys = settings.DATABASES.keys()
while 'default' in dbkeys: dbkeys.remove('default')
dbkeys = [ k for k in dbkeys if  os.path.dirname(settings.DATABASES[k]['NAME']).split(os.sep)[-1] == appname ]

for dbname in dbkeys:

    if dbname == dbkeys[0]:

        class Book_(Book):
            class Meta:
                proxy = True
                verbose_name = Book.__name__ + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
                verbose_name_plural = Book.__name__ + 's_'+ ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
                db_table = appname + '_book'
        Book_.db = dbname
        Book_.__name__ = Book.__name__ + '_' + appname.capitalize() + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
        admin.site.register(Book_, BookAdmin)


    elif dbname == dbkeys[1]:

        class Book__(Book):
            class Meta:
                proxy = True
                verbose_name = Book.__name__ + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
                verbose_name_plural = Book.__name__ + 's_'+ ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
                db_table = appname + '_book'
        Book__.db = dbname
        Book__.__name__ = Book.__name__ + '_' + appname.capitalize() + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_'))
        admin.site.register(Book__, BookAdmin)

这很有效,因为我将每个应用程序的数据库文件放在该应用程序的文件夹中。抱歉,这有点棘手。我有充分的理由想要这种能力。另请参阅我关于使用admin.site.registerhere 注册模型的未回答问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-23
    • 2014-09-17
    • 2021-04-07
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多