Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Django, can I specify database when creating an object?

Look at this Django ORM code:

my_instance = MyModel()
my_instance.some_related_object = OtherModel.objects.using('other_db').get(id)

At this point, in the second line, Django will throw an error:

ValueError: Cannont assign "<OtherModel: ID>": instance is on database "default", value is on database "other_db"

To me, it doesn't make much sense. How Django can tell on which database my_instance is, if I haven't even called:

my_instance.save(using='some_database')

yet?

I guess, that during the construction of an object Django automatically assigns it to the default database. Can I change it? Can I specify database when creating an object, by passing an argument to its constructor? According to the documentation, the only arguments I can pass, when creating an object are the values of its fields. So how can I solve my problem?

In Django 1.8 There is a new method called Model.from_db (https://docs.djangoproject.com/en/1.8/ref/models/instances/) but I'm using earlier version of Django and can't switch to the newer now. Looking at the implementation all it does is setting two model's attributes:

instance._state.adding = False
instance._state.db = db

So would it be enough to change my code to:

my_instance = MyModel()
my_instance._state.adding = False
my_instance._state.db = 'other_db'
my_instance.some_related_object = OtherModel.objects.using('other_db').get(id)

or it is too late to do it because those flags are used in constructor and have to be set in constructor only?

like image 318
mnowotka Avatar asked Oct 30 '22 17:10

mnowotka


2 Answers

You might want to look into database routing, which has been supported since Django 1.2. This will let you setup multiple databases (or "routers") for different models.

You can create a custom database router (a class inheriting from the built-in object type), with db_for_read and db_for_write methods that return the name of the database (as defined in the DATABASES setting) that should be used for the model passed into that method. Return None to let Django figure it out.

It's usually used for handling master-slave replication, so you can have a separate read-only database from your writeable one, but the same logic would apply to let you specify that certain models live in certain databases.

You would probably also want to define an allow_syncdb method so that only the models you want to appear in database B will appear there, and everything else will appear in database A.

like image 133
Steadman Avatar answered Nov 08 '22 05:11

Steadman


Django knows what database each object comes from because it notes it such in its internal properties. The QuerySet too has this information stored within itself.

Actually, database routing isn't really needed to achieve what you want here.

Consider the following code fragment:

my_instance = MyModel()
my_instance.some_related_object_id = OtherModel.objects.using('other_db').get(id).id

Note how I assign just the ID, not the object itself.

You will lose the actual object here, but gain the ability to store referential data.

AFAIK there's no API to change an object's associated database.

like image 42
velis Avatar answered Nov 08 '22 04:11

velis