First of all we have an application which has a few models that represent the models of our ticket support system Kayako. This application should not know anything about other applications that make use of it and should remain as generic as possible. Since this application is using existing tables of Kayako we have it running on the same database. Let's call this application kayakodb.
One application links customers from our customer database to tickets in the ticket support system. Previously this system had it's own representation of the tickets inside our ticket support system, querying for tickets by using an API provided by kayakodb. It then used this representation of tickets to link customers and domains to. This however was too complex and not very logical. So we opted to switch it to a proxy model and move the models that represent the links to customers and domains to kayakodb. Let's call this application sidebar.
Another, new application, shows the tickets from the ticket support system in a clear overview alongside calls so our support department can easily see which calls and tickets are related to which customers. This system has a proxy model to the proxy model of sidebar, because some of the functionality provided by the sidebar model are also required for this application alongside some others which the new proxy model declare. Let's call this project WOW.
The sidebar and WOW applications are both part of the same project/repository. We'll call this repository Coneybeach which has its own database. However, kayakodb is a completely unrelated project. It is included in Coneybeach via a requirements file which we install via pip.
kayakodb which is, of course, a no go. Any time we would install a new version of kayakodb it would overwrite this migration. Let alone the fact that kayakodb shouldn't know anything about which models make use of it.
Ticket model inside 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
The SidebarTicket proxy model inside 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
The Contact class TicketWrapper inherits from (as requested by Hynekcer). This model is used as base model for TicketWrapper and another model representing calls (though there are not issues with this model as far as I'm aware):
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)
The TicketWrapper proxy model inside 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 for both the proxy models. This creates correct migrations, but causes the proxy models to look for the kayakodb.Ticket model in the Coneybeach database.abstract = True for the subclasses, but wasn't sure this was the because I still want to be able to make use of the manager for the models.kayakodb project, but I don't think this is a good solution. kayakodb shouldn't know anything about the implementations of its models or where they are used../manage.py check returns 0 issues.kayakodb.Ticket model to be unmanaged the WOW project tries to create a migration for all models in kayakodb. Result:
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
Django's admin doesn't have any explicit support for multiple databases. If you want to provide an admin interface for a model on a database other than that specified by your router chain, you'll need to write custom ModelAdmin classes that will direct the admin to use a specific database for content.
Proxy models allow us to change the Python behavior of a model without changing the database. Proxy models are declared just like normal models. In our example, we tell Django that Honda is a proxy model by setting the proxy attribute of the Honda Meta class to True .
As @hynekcer said if kayakodb is an existing database, you need to set managed = False for all its models.
However, that still leaves the problem of the migration for the proxy model created inside the wrong app (kayakodb).
The hacky fix that might work is changing the app_label of the proxy model to whatever app is okay to put the migration to (sidebar in this case), and making a router that will point this proxy model to read and write from kayakodb.
E.g. the proxy model:
# in sidebar/models.py
class SidebarTicket(KayakoTicket):
class Meta:
proxy = True
app_label = 'sidebar'
and the router inside the project that uses it:
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 but I grepped your question for a word router which is not mentioned, so I'd think the thing you are looking for are Database Routers
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With