3

I'm facing a problem when using multiple databases in Django.

Here's the scenario:

I have a django project, which is divided into two applications: app1 and app2. App1 will take care of authentication and custom modules (ie. it has its own models.py) and App2 is normal web application (ie. it has its own models.py with models in it).

Settings.py looks like this:

DATABASE_ROUTERS = ['app1.router.AuthRouter', 'app1.router.App1Router', 'app2.router.App2Router']

DATABASES = {
    'default': {
      [...]
    },
    'app2': {
      [...]
    }
}

As you can see, I have two databases (both PSQL 9.2), default (used for app1 application) and app2 (used for app2 application) and I've defined such 3 routers (1. for authentification, 2. for app1 model and last for app2 model.

Code for app1.router.AuthRouter and app1.router.App1Router is following:

class AuthRouter(object):
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'auth':
                    return 'default'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'auth':
                    return 'default'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if obj1._meta.app_label == 'auth' or obj2._meta.app_label == 'auth':
            return True
        return None

    def allow_syncdb(self, db, model):
        if db == 'default':
            return model._meta.app_label == 'auth'
        elif model._meta.app_label == 'auth':
            return False
        return None


class App1Router(object):
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'app1':
                    return 'default'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'app1':
                    return 'default'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if obj1._meta.app_label == 'app1' or obj2._meta.app_label == 'app1':
            return True
        return None

    def allow_syncdb(self, db, model):
        if db == 'default':
            return model._meta.app_label == 'app1'
        elif model._meta.app_label == 'app1':
            return False
        return None

Problem is that, when I do syncdb, it does correctly create auth_ tables, but it does not create django_ tables, which fails in error.

[marek@t420 multipledb_test]$ python manage.py syncdb 
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  [more errors coming]
django.db.utils.ProgrammingError: relation "django_content_type" does not exist
LINE 1: ..."."app_label", "django_content_type"."model" FROM "django_co...

YOU SEE? THERE ARE NO DJANGO TABLES CREATED - no django_admin_log, no django_content_type, no django_session!!!

Any hints? It's driving me crazy for three days!

oz123
  • 27,559
  • 27
  • 125
  • 187
Boun
  • 123
  • 1
  • 10
  • Using multiple databases in Django is problematical at best, as you've found. I discovered myself in a similar situation earlier this year, and the least time-consuming and most effective solution was to implement a database join migration script. – Jason Jan 15 '14 at 14:23
  • Do you mean using just one database, which is shared between all applications? If it was possible, I'd love to use just 1 postgres DB with different schemas inside (1 for each application), but it doesn't work. I truly need application data separated. Whole idea is create a web portal (`default app` as a wrapper with all users, passwords etc) and other apps will be added 'independently' (`app2` is actually attendance system, `app3` a ticket system ...) Any Ideas? – Boun Jan 15 '14 at 14:34
  • Need to clarify some terminology for me... are you referring to multiple applications within the same Django project, or multiple projects themselves? – Jason Jan 15 '14 at 14:39
  • Well, as far as i know, two django projects can't speak to each other. That's why I wanted to create one Django project, which will have different applications inside (default app1, app2 - web attendance, app3 - ticket system, ...). I want to have the system as independent as possible, but these parts (app1, app2, app3) have to be able to communicate (actually app2, app3 have to authenticate using app1) - which led me to use 1 Django projects with multiple apps(each having own database). Is is clear? – Boun Jan 15 '14 at 14:42
  • Understood. Another user had a similar problem at http://stackoverflow.com/questions/8054195/django-multi-database-routing and documented the process at http://djangosteps.wordpress.com/2011/11/08/multiple-database-implementation-in-django/ Its somewhat old, but may help. – Jason Jan 15 '14 at 14:42

2 Answers2

3

Just a comment: My idea was to use a few databases, which will communicate within each other (eg. using Foreign keys from one DB to another one). I wish i read the documentation more carefully:

Cross-database relations Django doesn’t currently provide any support for foreign key or many-to-many relationships spanning multiple databases. If you have used a router to partition models to different databases, any foreign key and many-to-many relationships defined by those models must be internal to a single database.

All that mess was for nothing, because integrity constraints dont allow to use any relationships over multiple databases.

Boun
  • 123
  • 1
  • 10
1

What helped me is this post: http://diegobz.net/2011/02/10/django-database-router-using-settings/

Solution is this:

  1. using just one Router, and this router will take care of application 2 - router app2.router.App2Router,not any routers for default database. Routers for default database works fine just as it is.

  2. Alter that app2.router.App2Router to look like the code in that link in the beginning of the post provided. Doing just step 1 led to creating auth_ and django_ tables (by running syncdb) correctly, but unfortunately ALL MODEL tables were created to default database (ie. also tables from app2). Altering app2.router.App2Router made everything work.

  3. Alter settings.py to look like this:

    DATABASE_ROUTERS = ['app2.router.DatabaseAppsRouter']

    DATABASE_APPS_MAPPING = {'app2': 'app2'}

Now running syncdb database=app2 creates tables to database app2 and running syncdb creates all django system tables and own app1 tables to default. Awesome!

Thanks Jason for his ideas!

Boun
  • 123
  • 1
  • 10