Модель прокси Django в другую базу данных

ситуация


У нас есть несколько разных приложений, которые используют билеты из системы поддержки билетов для разных видов функциональности.

Прежде всего, у нас есть приложение, в котором есть несколько моделей, которые представляют модели нашей системы поддержки билетов Kayako. Это приложение не должно ничего знать о других приложениях, которые его используют, и должно оставаться как можно более универсальным. Поскольку это приложение использует существующие таблицы Kayako, оно работает в одной базе данных. Давайте назовем это приложение kayakodb,

Одно приложение связывает клиентов из нашей базы данных клиентов с билетами в системе поддержки билетов. Ранее у этой системы было собственное представление билетов в нашей системе поддержки билетов, запрашивая билеты с помощью API, предоставленного kayakodb, Затем он использовал это представление билетов для связи клиентов и доменов. Это, однако, было слишком сложно и не очень логично. Поэтому мы решили переключить его на прокси-модель и перенести модели, представляющие ссылки на клиентов и домены, на kayakodb, Давайте назовем это приложение sidebar,

Другое новое приложение отображает билеты из системы поддержки билетов в четком обзоре вместе с вызовами, чтобы наш отдел поддержки мог легко увидеть, какие звонки и билеты связаны с какими клиентами. Эта система имеет модель прокси для модели прокси sidebar, потому что некоторые из функций, предоставляемых sidebar модель также требуется для этого приложения наряду с некоторыми другими, которые объявляет новая модель прокси. Давайте назовем этот проект WOW,

sidebar а также WOW оба приложения являются частью одного проекта / хранилища. Мы назовем этот репозиторий 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

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

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)

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

Что я пробовал


  • Я пытался не указывать 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

3 ответа

Решение

Как сказал @hynekcer, если kayakodb это существующая база данных, вам нужно установить managed = False для всех своих моделей. Однако это по-прежнему оставляет проблему миграции для прокси-модели, созданной в неправильном приложении (kayakodb).

Хакерское исправление, которое может сработать, - это изменение 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 Использование

class Meta:
    managed = False

для всех моделей в БД, которые не должны контролироваться Django. (т.е. каякодб)


Следует отметить, что 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

Преимущество состоит в том, что это отключает миграцию для любой текущей или будущей модели, но я рекомендую добавить текст class Meta: managed = False также где-то в комментариях kayakodb/models.py чтобы сделать это понятным любому более позднему разработчику, потому что он может легко забыть сначала прочитать маршрутизаторы.

Вы можете написать зависимость версий вашего проекта от минимальной и максимальной версии API Kayako, но это не может быть в форме миграции.


Еще одна проблема заключается в том, что " прокси-модель должна наследоваться только от одного неабстрактного модельного класса...", но ваша прокси-модель TicketWrapper наследуется отContact а также SidebarTicket, Это похоже на глупость, и мне интересно, что вы не видите ошибку TypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class. Один и тот же контакт может быть использован несколькими билетами. (Разве это не зарегистрированный пользователь? Разве он не может изменить что-либо в своем профиле пользователя за всю историю проблемы?) Вероятно, это должен быть внешний ключ для контакта, а не множественное наследование.

Тл; д-р, но я ответил на ваш вопрос router который не упоминается, так что я думаю, что вы ищете, это маршрутизаторы базы данных

Другие вопросы по тегам