18

I've been looking over django's multi-db docs. I'd like to break a few of my models out into a different db. But I really just want those models to ALWAYS live in a particular db. I don't need special routing. And writing unique routers just to say "Models A, B, and C live in database X, models D, E, and F always live in database Y".

Is there a simpler way to set defaults like this? For instance, as a model meta field?

chrispitzer
  • 991
  • 1
  • 8
  • 26
  • 1
    possible duplicate of [Django - how to specify a database for a model?](http://stackoverflow.com/questions/3519143/django-how-to-specify-a-database-for-a-model) – Amir Ali Akbari Mar 19 '14 at 14:04

2 Answers2

23

You can easily do this by appearing custom attribute to model:

class A(models.Model):
    _DATABASE = "X"

class B(models.Model):
    _DATABASE = "Y"
...

Then you need to add router. Next one will select database by _DATABASE field, and models without _DATABASE attribute will use default database, also relationships will be allowed only for default database:

class CustomRouter(object):

    def db_for_read(self, model, **hints):
        return getattr(model, "_DATABASE", "default")
        
    def db_for_write(self, model, **hints):
        return getattr(model, "_DATABASE", "default")

    def allow_relation(self, obj1, obj2, **hints):
        """
        Relations between objects are allowed if both objects are
        in the master/slave pool.
        """
        db_list = ('default')
        return obj1._state.db in db_list and obj2._state.db in db_list

    def allow_migrate(self, db, model):
        """
        All non-auth models end up in this pool.
        """
        return True  

And the last step is specifing your router in settings.py:

DATABASE_ROUTERS = ['path.to.class.CustomRouter']

Source


BTW this solution will not work if you are going to work with many-to-many relations in non-default database because relational models will not have "_DATABASE", attribute, in this case, better to use something like model._meta.app_label as filter condition in db_for_read/db_for_write

Ivan Borshchov
  • 3,036
  • 5
  • 40
  • 62
  • This solution somehow conflicts with django-debug-toolbar as least with version 1.3.2 and django 1.6 – Vasilly.Prokopyev Jul 30 '15 at 10:12
  • 1
    to remove if/else: `return getattr(model, "_DATABASE", 'default')` – John Woo Feb 09 '17 at 07:27
  • 6
    @JohnWoo it's better to return None instead of explicit 'default', since Django will fall back to 'default' if no DB routers find a match. https://docs.djangoproject.com/en/dev/topics/db/multi-db/#automatic-database-routing "The default routing scheme ensures that if a database isn’t specified, all queries fall back to the default database." – John Carter Mar 14 '17 at 22:25
5

There is no Meta field for this (there was one at some point but it got removed because of the limitations it introduced). You need a database router to control which objects go to what database. In your case the router should be pretty easy to implement.

brutasse
  • 809
  • 5
  • 5