class OtherDatabaseManager(models.Manager):
"""
This Manager lets you set the DATABASE_NAME on a per-model basis.
"""
def __init__(self, database_name, *args, **kwargs):
models.Manager.__init__(self, *args, **kwargs)
self.database_name = database_name
def get_query_set(self):
qs = models.Manager.get_query_set(self)
qs.query.connection = self.get_db_wrapper()
return qs
def get_db_wrapper(self):
from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper
# Monkeypatch the settings file. This is not thread-safe!
old_db_name = settings.DATABASE_NAME
settings.DATABASE_NAME = self.database_name
wrapper = DatabaseWrapper()
wrapper._cursor(settings)
settings.DATABASE_NAME = old_db_name
return wrapper
class MyModel(models.Model):
# ...
objects = OtherDatabaseManager('my_custom_database)
This is not unlike the code here:
http://www.eflorenzano.com/blog/post/easy-multi-database-support-django/
--
One ugliness about this is that it has to monkeypatch the settings
file in order to change the DATABASE_NAME, before passing it to
DatabaseWrapper._cursor(). So my proposal is to change
DatabaseWrapper._cursor() to accept a settings *dictionary* instead of
a settings *object*.
The only place in the Django codebase that calls
DatabaseWrapper._cursor() is BaseDatabaseWrapper.cursor() in
db/backends/__init__.py. That, plus the fact that this method name is
preceded by an underscore, leads me to believe there are no
backwards-incompatibility issues with making this change. But I'm
bringing it up here in case anybody wants to point out problems before
I commit it.
Adrian
Hey, this sounds like a good improvement (passing the settings to the
constructor instead of _cursor). Do you have any patches or at least a
link your proposal, if you've written one? I can't seem to find
anything.
Adrian
I think the direction is a good one. Something that far down the chain
probably shouldn't have a hard dependency on settings. We insulate the
users from needing to pass around settings everywhere manually, but it's
not too onerous to do so ourselves.
Whether you pass the dictionary to _cursor or to the constructor as Alex
proposes is for you to decide, I think. We can evolve that over time if
we need to.
Regards,
Malcolm
Thanks for the patch, Alex. I had already started doing it on my own
and was pleased to see we came up with almost exactly the same
solution. The main differences are that I called it
BaseDatabaseWrapper.settings_dict instead of
BaseDatabaseWrapper.settings (to make it clear it's a dictionary and
make it grepable) and I assigned to a local settings_dict variable
whenever the code needed to refer to self.settings_dict many times.
http://code.djangoproject.com/changeset/10026
This will most probably break external database backends, so I'll send
out a separate django-developers note about that, in hopes of getting
their attention.
Adrian
FWIW I'm still strongly of the opinion that database connection
pooling does not belong in an ORM like Django's.
--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."
Let's just wrap those lines into a function! :)
Adrian suggested a new way to write function get_db_wrapper in comments at
http://www.eflorenzano.com/blog/post/easy-multi-database-support-django/
But why not have a method get_db_engine(engine_name) in the Django ORM code?
Yes, good idea, Yuri! It would be nice to encapsulate the logic of
loading the appropriate Django database backend class from
django.db.backends based on a given backend name.
I've created a ticket here:
http://code.djangoproject.com/ticket/10487
If anybody (Alex?) wants to code up a patch, I'll get it in ASAP.
Adrian
Thanks, Alex. I've committed it in
http://code.djangoproject.com/changeset/10045 .
Adrian