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

Bon*_*ono 13 python django proxy-classes django-migrations

情况


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

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

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

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

sidebarWOW应用程序是相同的项目/库的两个组成部分.我们将此存储库称为Coneybeach具有自己的数据库.但是,这kayakodb是一个完全不相关的项目.它Coneybeach通过我们安装的需求文件包含在内pip.

问题


在为新设置创建迁移时,Django会为已安装的设备创建代理模型迁移,kayakodb这当然是不行的.任何时候我们安装它的新版本kayakodb都会覆盖此迁移.更不用说kayakodb不应该知道哪些模型使用它的事实.


Ticket里面 的模型kayakodb:

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
Run Code Online (Sandbox Code Playgroud)

SidebarTicket内部代理模型sidebar:

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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

TicketWrapper内部代理模型WOW:

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
Run Code Online (Sandbox Code Playgroud)

我试过了什么


  • 我试过不指定app_label代理模型.这会创建正确的迁移,但会导致代理kayakodb.Ticket模型在Coneybeach数据库中查找模型.
  • 我已经尝试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
Run Code Online (Sandbox Code Playgroud)

Ann*_*ota 5

正如@hynekcer所说,如果kayakodb是现有数据库,则需要managed = False为其所有模型进行设置.但是,这仍然会导致在错误的app(kayakodb)内创建的代理模型的迁移问题.

可能有用的hacky修复方法是将app_label代理模型更改为任何可以迁移的应用程序(sidebar在本例中),并创建一个指向此代理模型的读取和写入的路由器kayakodb.

例如代理模型:

# in sidebar/models.py

class SidebarTicket(KayakoTicket):
    class Meta:
        proxy = True
        app_label = 'sidebar'
Run Code Online (Sandbox Code Playgroud)

以及使用它的项目中的路由器:

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
Run Code Online (Sandbox Code Playgroud)