An example of how to select the "default" database based on the request URL instead of the model. The basic idea is that the middleware process_view
(or process_request
) function sets some context from the URL into thread local storage, and process_response
deletes it. In between, any database operation will call the router, which checks for this context and returns an appropriate database alias.
In this snippet, it's assumed that any view in the system with a cfg
keyword argument passed to it from the urlconf may be routed to a separate database. Take this urlconf for example:
url( r'^(?P<cfg>\w+)/account/$', 'views.account' )
The middleware and router will select a database whose alias is <cfg>
, or "default" if none is listed in settings.DATABASES
, all completely transparent to the view itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import threading
request_cfg = threading.local()
class RouterMiddleware (object):
def process_view( self, request, view_func, args, kwargs ):
# Here you could do something with request.path instead.
if 'cfg' in kwargs:
request_cfg.cfg = kwargs['cfg']
def process_response( self, request, response ):
if hasattr( request_cfg, 'cfg' ):
del request_cfg.cfg
return response
class DatabaseRouter (object):
def _default_db( self ):
from django.conf import settings
if hasattr( request_cfg, 'cfg' ) and request_cfg.cfg in settings.DATABASES:
return request_cfg.cfg
else:
return 'default'
def db_for_read( self, model, **hints ):
return self._default_db()
def db_for_write( self, model, **hints ):
return self._default_db()
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
This really works well! I've enhanced it a bit, so it returns a 404 if the database doesn't exist and it adds the selected database to the context, see this gist:
https://gist.github.com/gijzelaerr/7a3130c494215a0dd9b2/
There is only one case where this doesn't work as intended, and that is when you are building queries outside of a page request. When you manually select a database with .using() this will be used, but when you follow references the database is reset to default. For example:
SomeTable.objects.using('dbname').first().othertable
For SomeTable the dbname database is used, but for OtherTable Django switches back default.
#
We store a DB variable in the session and this code is messy but it works - thanks!
#
Please login first before commenting.