django one app one model multiple databases

时间:2016-06-10 16:12:49

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


I am new to django webapp development and i am stuck with a problem. I have created 1 app with 1 model to insert data to the database using 1 form. I am going to use multiple databases for this. Each database will have 1 table(for now) with same structure. Now my problem is:

How can I use just 1 model, 1 view and 1 form for multiple databases and their respective tables. Databases and tables should be switched on calling their respective urls.

e.g. http://www.example.com/x/abc/ will access first database and it's tables for all the operations.
http://www.example.com/y/abc/ will access second database

I have already tried the sample db routing provided in django documentation, but it doesn't help much. also I couldn't find a relative post/question which addresses this particular problem

I want to do this because later i will add more models and forms for accessing the data from the database tables and this seems the cleanest way to me

PS: I am using django 1.9.6

2 个答案:

答案 0 :(得分:4)

无论这是否是构建应用程序的好方法,您都可以告诉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

答案 1 :(得分:3)

我正在回应,因为你“我想知道是否还有其他解决方案”评论。有。我把它放在网站上运行......多个SQLite数据库和一个应用程序。您还提到了数据库路由器问题,我也在努力解决这个问题。

首先,将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 settings.py。我在这个方案中选择保留默认的Django方案......应用程序从其目录名称中获取它的名称。

现在,在DATABASE_ROUTERS = [ 'appdir.models.router', ]中,您需要router。这会引导查询通过我们使用models.py函数在type()中初始化的default。请特别注意,我没有在DATABASE_ROUTERS中列出DATABASES数据库。我有一个,default设置中以default为键,列出了一个。当您进行初始迁移时,各种Django应用程序表最终会出现在Router()数据库中,当然,Book.db = x将退出。

因此,在您的视图中,您将从myview(request, x, someothername):开始,其中视图签名为try: finally:。您可能希望使用finally覆盖整个视图正文,在migrate块中,您可以将数据库的选择返回到默认值。

我必须承认,在移民问题上可能会有美中不足。我讨厌那些,在我的情况下,每当我更新它们时,我都会写我的小SQLite数据库的全部内容,这是经常的。因此,不关心保存数据,每当我需要进行更改时,我就会抛弃它们和迁移文件夹。但如果您的情况不同,您可能不得不使用makemigrationsDATABASES命令。我的每个数据库都具有相同的模式,如模型所定义。所以我实际创建了一个空白数据库,首先暂时在DATABASES设置中输入一个条目,该条目具有应用程序名称的关键字。我用它来创建SQLite db文件的一个副本,然后简单地复制它并在我想添加新数据库时重命名它(将新数据库的详细信息添加到DATABASES并删除具有该数据库的临时条目appname为关键)。或者,您可能希望保留并充分利用其admin.py中的键是应用程序名称的数据库。

但遗憾的是,我们还没有完成。我不得不修复admin.py。在class 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.register

这是有效的,因为我将每个应用程序的db文件放在该应用程序的文件夹中。对不起,这有点棘手。我有充分的理由想要这种能力。另请参阅我未回答的关于使用joinhere注册它们的子类别模型的问题。