Hi all, I've just uploaded a new patch improving the multi-db interface. This patch introduces a way to easily configure the database selection process, and enables you to assign a database selection mechanism to models not under your control (e.g., changing database usage for the contrib.auth app).
The patch is attached to #12540; if committed it will also close #12541. This patch goes a little further than I have previously indicated - it actually starts to introduce public API. I'm looking for any design feedback on this proposal. I haven't written docs yet, but here's the short version of what I have added: * There is a new setting - DATABASE_ROUTERS - which is a list of routers that will be tried in turn. * A Database Router is any class that provides three methods: - db_for_read(model, instance=None) Suggest the database that should be used for reads of objects of type Model. Return None if there is no suggestion - db_for_write(model, instance=None) Suggest the database that should be used for writes of objects of type Model. Return None if there is no suggestion - allow_relation(obj1, obj2) Return True if a relation between obj1 and obj2 should be allowed, False if the relation should be prevented, or None if the router has no opinion. (For those that were paying attention yesterday: This method is a replacement for SOURCE in a database setting) * In these methods, 'model' is the model class that is operated upon. 'instance' is any object that can be used to provide a hint as to the database that should be used. The may be the instance that is being saved, or an object that is being added in a relationship. This allows for routing based on the application, the model, or an attribute of the model. * Whenever a query needs to know which database to use, it can call django.db.router, providing a model, and a hint (if available). Django then works down the stack, trying each router in turn, until a database suggestion can be found. If no suggestion can be found, it tries the current _state.db of the hint instance; failing that, it falls back to DEFAULT_DB_ALIAS. The patch includes a whole lot of extra tests (including some tests that were disabled just before the commit to trunk). There are some tests that are probably still missing - especially for update and delete queries. So - what does this mean in practice? Say you want contrib.auth to exist on the 'credentials' database; all other models in a master/slave relationship between the databses 'master', 'slave1' and 'slave2'. To implement this, you would need 2 routers: class AuthRouter(object): def db_for_read(self, model, instance=None): # Point all operations on auth models to 'credentials' if model._meta.app_label == 'auth': return 'credentials' return None def db_for_write(self, model, instance=None): # Point all operations on auth models to 'credentials' if model._meta.app_label == 'auth': return 'credentials' return None def allow_relation(self, obj1, obj2): # Allow any relation if a model in Auth is involved if obj1._meta.app_label == 'auth' or obj2._meta.app_label == 'auth': return True return None class MasterSlaveRouter(object): def db_for_read(self, model, db=None): # Point all read operations to a random slave return random.choice(['slave1','slave2']) def db_for_write(self, model, db=None): # Point all write operations to the master return 'master' def allow_relation(self, obj1, obj2): # Allow any relation between two objects in the db pool db_list = ('master','slave1','slave2') if obj1 in db_list and obj2 in db_list: return True return None Then, in your settings file, add the following: DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.MasterSlaveRouter'] And you're set. No need to override get_query_set() or use using(xxx) or save(using=xxx) - the right database will be selected automagically. One quick point of note - the task of splitting auth onto a different database won't actually work on Postgres or MySQL InnoDB - ForeignKeys to a remote database won't work due to referential integrity problems. I'm not attempting to solve this problem right now, as there are a whole lot of issues that need to be worked out in that space. However, on MySQL MyISAM and SQLite, it will work. Ok - so that's the patch. Any comments, queries, or criticisms are welcome. Yours, Russ Magee %-)
-- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.