Created
July 18, 2014 09:36
-
-
Save gijzelaerr/7a3130c494215a0dd9b2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Select database based on URL variable | |
Inspired by this Django snipped: | |
https://djangosnippets.org/snippets/2037/ | |
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. for example: | |
url( r'^(?P<db>\w+)/account/$', 'views.account' ) | |
The middleware and router will select a database whose alias is <db>, | |
"default" if no db argument is given and raise a 404 exception if not listed in | |
settings.DATABASES, all completely transparent to the view itself. | |
""" | |
import threading | |
from django.http import Http404 | |
request_cfg = threading.local() | |
class MultiDbRouterMiddleware(object): | |
""" | |
The Multidb router middelware. | |
he 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. | |
Add this to your middleware, for example: | |
MIDDLEWARE_CLASSES += ['bananaproject.multidb.MultiDbRouterMiddleware'] | |
""" | |
def process_view(self, request, view_func, args, kwargs): | |
if 'db' in kwargs: | |
request_cfg.db = kwargs['db'] | |
request.SELECTED_DATABASE = request_cfg.db | |
def process_response(self, request, response): | |
if hasattr(request_cfg, 'db'): | |
del request_cfg.db | |
return response | |
class MultiDbRouter(object): | |
""" | |
The multiple database router. | |
Add this to your Django database router configuration, for example: | |
DATABASE_ROUTERS += ['bananaproject.multidb.MultiDbRouter'] | |
""" | |
def _multi_db(self): | |
from django.conf import settings | |
if hasattr(request_cfg, 'db'): | |
if request_cfg.db in settings.DATABASES: | |
return request_cfg.db | |
else: | |
raise Http404 | |
else: | |
return 'default' | |
def db_for_read(self, model, **hints): | |
if model._meta.app_label != 'banana': | |
return 'default' | |
return self._multi_db() | |
db_for_write = db_for_read | |
def multidb_context_processor(request): | |
""" | |
This context processor will add a db_name to the request. | |
Add this to your Django context processors, for example: | |
TEMPLATE_CONTEXT_PROCESSORS +=[ | |
'bananaproject.multidb.multidb_context_processor'] | |
""" | |
if hasattr(request, 'SELECTED_DATABASE'): | |
return {'db_name': request.SELECTED_DATABASE} | |
else: | |
return {} |
Hi, @dkendall100 .
It's an old question, but here's my little help.
From what I understand from @gijzelaerr's code, "SELECTED_DATABASE" is any variable name used to place in the request the information of selected database, just so that the context_processor receives it and places it in a context variable "db_name".
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am going to implement this in a production environment for different databases with identical models but different data. Not the way I would have done it originally, but I am updating some code and bringing 6 different web applications into one web application. I implemented your code here and it work fantastically for most HTTP requests. It broke the Django admin page, but everything else seems to work.
Are there any possible race conditions while multiple users are querying different database. I see you use a thread local variable to handle this, but I can not find any documentation of he "SELECTED_DATABASE" attribute anywhere.
Any help would be appreciated!