Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to save with save_model using database router

I am using a database router hence i have two databases for my application. One database for default django data and the other one.

In my admin i have override the save_model function in order to save the created_by variable, but i am unable to do this.

Cannot assign "<User: testuser>": the current database router prevents this relation.

database router:

from django.conf import settings

    class DatabaseAppsRouter(object):
        def db_for_read(self, model, **hints):
            """Point all read operations to the specific database."""
            if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
                return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
            return None

        def db_for_write(self, model, **hints):
            """Point all write operations to the specific database."""
            if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
                return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
            return None

        def allow_relation(self, obj1, obj2, **hints):
            """Allow any relation between apps that use the same database."""
            db_obj1 = settings.DATABASE_APPS_MAPPING.get(obj1._meta.app_label)
            db_obj2 = settings.DATABASE_APPS_MAPPING.get(obj2._meta.app_label)
            if db_obj1 and db_obj2:
                if db_obj1 == db_obj2:
                    return True
                else:
                    return False
            return None

        def allow_syncdb(self, db, model):
            """Make sure that apps only appear in the related database."""
            if model._meta.app_label in ['south']:
                return True;
            elif db in settings.DATABASE_APPS_MAPPING.values():
                return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label) == db
            elif settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
                return False
            return None

admin:

class SomeEntityAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        user = request.user._wrapped if hasattr(request.user,'_wrapped') else request.user
        if not obj.created_by:
            obj.created_by = user
            obj.save()

I have even tried to define a different database by using save(using='thedb') but that does not work either. Is my database router wrong?

like image 352
JavaCake Avatar asked Oct 26 '14 23:10

JavaCake


2 Answers

The problem you are encountering arises from the difficulty of storing relations between objects that are stored in two distinct databases. In your example, you stated that you have created one database to store all Django contributed objects, which includes User objects created by the auth app. Meanwhile, the second model's objects will be stored in a distinct and entirely separate database. When you attempt to create a relationship between the new object stored in one database and the User object, you are attempting cross-database relations.

Cross-database relations are a difficult problem which has not been solved yet when using multiple databases in Django. If you would like more information about this issue, the Django documentation has a brief note about this problem (copied below for clarity).

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.

This is because of referential integrity. In order to maintain a relationship between two objects, Django needs to know that the primary key of the related object is valid. If the primary key is stored on a separate database, it’s not possible to easily evaluate the validity of a primary key.

If you’re using Postgres, Oracle, or MySQL with InnoDB, this is enforced at the database integrity level – database level key constraints prevent the creation of relations that can’t be validated.

However, if you’re using SQLite or MySQL with MyISAM tables, there is no enforced referential integrity; as a result, you may be able to ‘fake’ cross database foreign keys. However, this configuration is not officially supported by Django.

like image 176
MRHwick Avatar answered Oct 04 '22 02:10

MRHwick


If you want to stick with multiple dbs (with relations), all you need to do is implement a allow_relations method in your router that returns True when you are accessing the relation.

like image 33
wmoskal Avatar answered Oct 04 '22 03:10

wmoskal