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

2

6

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?

Certificate answered 8/10, 2015 at 8:48 Comment(0)
P
2

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.

Pella answered 14/10, 2015 at 21:7 Comment(0)
F
1

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.

Feria answered 9/1, 2020 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.