Login

Database Routing by URL

Author:
dcwatson
Posted:
May 25, 2010
Language:
Python
Version:
1.2
Score:
3 (after 3 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 8 months ago

Comments

gijzelaerr (on July 18, 2014):

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.

#

McAnix (on February 19, 2016):

We store a DB variable in the session and this code is messy but it works - thanks!

#

Please login first before commenting.