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
答案 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数据库的全部内容,这是经常的。因此,不关心保存数据,每当我需要进行更改时,我就会抛弃它们和迁移文件夹。但如果您的情况不同,您可能不得不使用makemigrations
和DATABASES
命令。我的每个数据库都具有相同的模式,如模型所定义。所以我实际创建了一个空白数据库,首先暂时在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文件放在该应用程序的文件夹中。对不起,这有点棘手。我有充分的理由想要这种能力。另请参阅我未回答的关于使用join
,here注册它们的子类别模型的问题。