Skip to content

Instantly share code, notes, and snippets.

@tobiasmcnulty
Last active May 5, 2019 07:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobiasmcnulty/6314224 to your computer and use it in GitHub Desktop.
Save tobiasmcnulty/6314224 to your computer and use it in GitHub Desktop.
Django database router, based on django-balancer, that sends reads for the specified models to the RoundRobinMasterSlaveRouter router in django-balancer. Uses thread locals to determine whether or not the it has been enabled for the current request. Will raise an exception if views or other code attempt to use it for write queries.
from functools import wraps
from routers import ForceReadForModelsRouter
__all__ = ['uses_forced_read_router']
def uses_forced_read_router(func):
@wraps(func)
def wrapper(req, *args, **kwargs):
try:
ForceReadForModelsRouter.enable()
return func(req, *args, **kwargs)
finally:
ForceReadForModelsRouter.disable()
return wrapper
import threading
from balancer.routers import RoundRobinMasterSlaveRouter
class ForceReadForModelsRouter(RoundRobinMasterSlaveRouter):
"""
Router that sends reads for the specified models to the
RoundRobinMasterSlaveRouter router in django-balancer. Uses thread locals
to determine whether or not the it has been enabled for the current request.
Will raise an exception if views or other code attempt to use it for write
queries.
"""
_locals = threading.local()
app_models = {
'appname': [
'Model1',
'Model2',
],
}
@classmethod
def enable(cls):
cls._locals.enabled = True
@classmethod
def disable(cls):
cls._locals.enabled = False
@classmethod
def is_enabled(cls):
return getattr(cls._locals, 'enabled', False)
def _should_handle(self, model):
return ForceReadForModelsRouter.is_enabled() and\
model._meta.app_label in self.app_models and\
model._meta.object_name in self.app_models[model._meta.app_label]
def db_for_read(self, model, **hints):
if self._should_handle(model):
return super(ForceReadForModelsRouter, self).db_for_read(model, **hints)
def db_for_write(self, model, **hints):
if self._should_handle(model):
model_name = '.'.join([model._meta.app_label,
model._meta.object_name])
raise ValueError('The {0} model is marked as read-only'.format(model_name))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment