繁体   English   中英

Django代理模型到不同的数据库

[英]Django proxy model to different database

情况


我们有一些不同的应用程序,它们使用票证支持系统的票证来实现不同类型的功能。

首先,我们有一个应用程序,其中有一些模型代表我们的票务支持系统Kayako的模型。 此应用程序不应该知道有关使用它的其他应用程序的任何信息,并且应尽可能保持通用。 由于此应用程序使用Kayako的现有表,因此我们将它运行在同一个数据库中。 我们把这个应用kayakodb

一个应用程序将客户数据库中的客户链接到票证支持系统中的票证。 以前,这个系统有自己的票证支持系统内的票证表示,使用kayakodb提供的API查询票证。 然后,它使用此票证表示将客户和域链接到。 然而,这太复杂而且不太符合逻辑。 因此,我们选择将其切换为代理模型,并将表示客户和域链接的模型移至kayakodb 我们称这个应用程序sidebar

另一个新的应用程序显示了票务支持系统的票据,并且清晰地显示了电话,因此我们的支持部门可以轻松查看哪些电话和票证与哪些客户相关。 此系统具有sidebar代理模型的代理模型,因为sidebar模型提供的某些功能也是此应用程序所需的,以及新代理模型声明的其他一些功能。 让我们称这个项目为WOW

sidebarWOW应用程序都是同一项目/存储库的一部分。 我们将此存储库Coneybeach ,它具有自己的数据库。 然而, kayakodb是一个完全不相关的项目。 它通过我们通过pip安装的需求文件包含在Coneybeach

问题


在为新设置创建迁移时,Django会为已安装的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 

ContactTicketWrapper继承自(根据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继承自ContactSidebarTicket 它看起来像一个废话,我想知道你没有看到错误TypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class. 更多门票可以共享同一个联系人。 (它不是注册用户吗?在发布历史记录期间,他不能在用户配置文件中更改任何内容吗?)它应该是联系人的外键,而不是多重继承。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM