I set up URL based database routing inspired by this answer in order to use the same app for different projects/databases. The projects do not need to share any data, access control is managed by every project on its own and I need an admin site for every project. As in the original post I use a database router and a middleware which determines which database to be used from the request path, e.g. /test/process/1
will be routed to database test
and /default/process/2
to database default
.
import threading
from django.conf import settings
request_cfg = threading.local()
class RouterMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
path = request.path.lstrip('/').split('/')
if path[0] in settings.DATABASES:
request_cfg.db = path[0]
def process_response(self, request, response):
if hasattr(request_cfg, 'db'):
del request_cfg.db
return response
class DatabaseRouter(object):
def _default_db(self):
if hasattr(request_cfg, 'db') and request_cfg.db in settings.DATABASES:
return request_cfg.db
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()
The url patterns then need to be extended to include the subpath which refers to a specific database. I did this by hardcoding the urls in the project level urls.py like this:
urlpatterns = [
url(r'^default/admin/', include(admin.site.urls)), # does not work
url(r'^test/admin/', include(admin.site.urls)), # does not work
url(r'^default/', include('logbook.urls', namespace='anything')),
url(r'^test/', include('logbook.urls', namespace='anything else'))]
I admit that this is not very nice but I do not expect to have to manage more than a few databases. Interestingly, it does not matter what the namespace argument is but it has to be given. The original namespace of the app was logbook
and it is used for url reversing all over the app's views and templates.
Then, in the app level urls.py the app_name has to be defined (and be equal to the original namespace):
app_name = 'logbook'
urlpatterns = [
url(r'^$', views.redirect_index, name='index'),
url(r'^(?P<date>[0-9]{4}[0-9]{2})/$', views.Index.as_view(), name='index'),
.....
In the views I added a current_app=request.resolver_match.namespace
kwarg to every call of reverse()
as explained in the django docs. URL resolving whithin the templates did not need any modifcation.
Overall this works very well with two exceptions:
django.contrib.auth.middleware.AuthenticationMiddleware
mostly, I think because LOGIN
and LOGIN_REDIRECT
are constants.I am wondering if this is a clean approach and if there is solution to the two exceptions mentioned above. If not, what would be a better solution?
This is the article you are looking for.
Django Multi DB Documentation
It explains how to set up your multiple db, and the admin console to work along with it. As it says you need to make a custom model for your second db(the one which isn't default) and register it using the method given in the documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With