[英]Django proxy model to different database
首先,我们有一个应用程序,其中有一些模型代表我们的票务支持系统Kayako的模型。 此应用程序不应该知道有关使用它的其他应用程序的任何信息,并且应尽可能保持通用。 由于此应用程序使用Kayako的现有表,因此我们将它运行在同一个数据库中。 我们把这个应用kayakodb
。
一个应用程序将客户数据库中的客户链接到票证支持系统中的票证。 以前,这个系统有自己的票证支持系统内的票证表示,使用kayakodb
提供的API查询票证。 然后,它使用此票证表示将客户和域链接到。 然而,这太复杂而且不太符合逻辑。 因此,我们选择将其切换为代理模型,并将表示客户和域链接的模型移至kayakodb
。 我们称这个应用程序sidebar
。
另一个新的应用程序显示了票务支持系统的票据,并且清晰地显示了电话,因此我们的支持部门可以轻松查看哪些电话和票证与哪些客户相关。 此系统具有sidebar
代理模型的代理模型,因为sidebar
模型提供的某些功能也是此应用程序所需的,以及新代理模型声明的其他一些功能。 让我们称这个项目为WOW
。
sidebar
和WOW
应用程序都是同一项目/存储库的一部分。 我们将此存储库Coneybeach
,它具有自己的数据库。 然而, kayakodb
是一个完全不相关的项目。 它通过我们通过pip
安装的需求文件包含在Coneybeach
。
kayakodb
创建代理模型迁移,这当然是不行的。
每当我们安装新版本的kayakodb
它都会覆盖此迁移。
更不用说kayakodb
不应该知道哪些模型使用它的事实。
kayakodb
内的Ticket
模型:
class Ticket(models.Model): """ This model is a representation of the data stored in the "kayako" database table "swtickets". Minus a lot of stuff we don't use. If you add a field make sure it has the same name as the field in kayako.swtickets. """ # Fields, functions and manager etc. class Meta: db_table = 'swtickets' managed = False
sidebar
SidebarTicket
代理模型:
from kayakodb.models import Ticket class SidebarTicket(Ticket): class Meta: # Since this class is a wrapper we don't want to create a table for it. We only want to access the original # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models proxy = True # Don't look for this model in the sidebar tables, but in the kayakodb tables. app_label = 'kayakodb' # Some extra functions
Contact
类TicketWrapper
继承自(根据Hynekcer的要求)。 此模型用作TicketWrapper
基本模型和另一个表示调用的模型(尽管我知道此模型没有问题):
class Contact(models.Model): type = None class Meta: abstract = True def __getattr__(self, attr): if attr in ['customers', 'add_customer_id', 'remove_all_customers', 'byters', 'domainnames', 'add_domain_name', 'remove_domain_name', 'add_text', 'remove_text', 'texts', 'creation_date', 'add_tag', 'get_tags', 'remove_tag', 'identifier']: raise NotImplementedError('You should implement {}'.format(attr)) raise AttributeError(attr)
WOW
里面的TicketWrapper
代理模型:
from sidebar.models import SidebarTicket class TicketWrapper(Contact, SidebarTicket): class Meta: # Since this class is a wrapper we don't want to create a table for it. We only want to access the original # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models proxy = True # Don't look for this model in the WOW database, but in the kayakodb database. app_label = 'kayakodb' # Some extra functions
app_label
。 这会创建正确的迁移,但会导致代理模型在Coneybeach数据库中查找kayakodb.Ticket
模型。 abstract = True
,但不确定这是因为我仍然希望能够使用模型的管理器。 kayakodb
项目,但我不认为这是一个很好的解决方案。 kayakodb
不应该对其模型的实现或使用它们的位置有所了解。 ./manage.py check
返回0个问题。 kayakodb.Ticket
模型设置为不受管理之后, WOW
项目尝试为kayakodb
所有模型创建迁移。
结果:
Migrations for 'sidebar': 0004_auto_20170116_1210.py: - Delete model Ticket Migrations for 'kayakodb': 0001_initial.py: - Create model Staff - Create model Tag - Create model Ticket - Create model TicketPost - Create model TicketTag - Create model TicketCustomer - Create model TicketDomain - Create proxy model SidebarTicket - Alter unique_together for ticketdomain (1 constraint(s)) - Alter unique_together for ticketcustomer (1 constraint(s)) - Create proxy model TicketWrapper
正如@hynekcer所说,如果kayakodb
是现有数据库,则需要为其所有模型设置managed = False
。 但是,这仍然留下了在错误的应用程序( kayakodb
)中创建的代理模型的迁移问题。
可能app_label
的hacky修复方法是将代理模型的app_label
更改为可以将迁移放到(在这种情况下为sidebar
)的任何应用程序,并创建一个路由器,指示此代理模型从kayakodb
读取和写入。
例如代理模型:
# in sidebar/models.py
class SidebarTicket(KayakoTicket):
class Meta:
proxy = True
app_label = 'sidebar'
以及使用它的项目中的路由器:
from django.conf import settings
from kayakodb.models import Ticket
class ProxyDatabaseRouter(object):
def allow_proxy_to_different_db(self, obj_):
# check if this is a sidebar proxy to the Ticket model in kayakodb
return isinstance(obj_, Ticket) and obj_._meta.proxy and obj_._meta.app_label == 'sidebar'
def db_for_read(self, model, **hints):
if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar':
return 'kayakodb'
# the rest of the method goes here
def db_for_write(self, model, **hints):
if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar':
return 'kayakodb'
return None
# the rest of the method goes here
def allow_relation(self, obj1, obj2, **hints):
if self.allow_proxy_to_different_db(obj1) or self.allow_proxy_to_different_db(obj2):
return True
# the rest of the method goes here
tl; dr但我问了一个没有提到的单词router
问题,所以我认为你要找的东西是数据库路由器
tl; dr使用
class Meta:
managed = False
对于不应由Django控制的db中的所有模型。 (即kayakodb)
应该注意的是,PyPI Kayako
是一个Python API(没有Djago)到一些用另一种语言编写的Kayako应用程序。
向您了解Kayako和WOW在不同的数据库中是有用的,但它不是基础信息。 Django允许例如两个数据库中存在一个模型:主数据库和辅助数据库。 最重要的是元选项,在这种情况下,选项managed = False
。 它适用于将Django与遗留数据库集成的情况。 这并不仅仅意味着一个非常古老的项目,而是一个不是用Python + Django编写或者不支持迁移的项目,并且不共享尚未应用迁移的信息。 也许如果新版本的Kayako会向数据库添加新字段,您不需要读取该字段,或者如果将其添加到模型中,Django不负责将字段添加到数据库,因为它是由Kayako升级控制的。
您也可以使用数据库路由器为kayakodb应用程序(无处)创建表格,也不为kayakodb数据库创建表格,例如Django用户和组:
文件myrouter.py或类似的
class MyRouter(object):
allow_migrate(db, app_label, model_name=None, **hints):
if app_label == 'kayakodb' or db == 'kayakodb':
return False
文件settings.py
DATABASE_ROUTERS = ['path.to.myrouter.MyRouter',...]
# ... if another router has been defined previously
优点是这会禁用任何当前或未来模型的迁移,但我建议在kayakodb/models.py
中的某些地方添加一个文本class Meta: managed = False
,以便向任何后来的开发人员说明,因为他很容易忘记先读取路由器。
您可以在最小和最大版本的Kayako API上编写项目版本的依赖项,但不能采用迁移的形式。
您的另一个问题是“ 代理模型必须从一个非抽象模型类继承...... ”,但您的代理模型TicketWrapper
继承自Contact
和SidebarTicket
。 它看起来像一个废话,我想知道你没有看到错误TypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class.
更多门票可以共享同一个联系人。 (它不是注册用户吗?在发布历史记录期间,他不能在用户配置文件中更改任何内容吗?)它应该是联系人的外键,而不是多重继承。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.