Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Created April 6, 2017 02:04
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacharyvoase/c8961cb5fc8a894401e1f29f80975d8c to your computer and use it in GitHub Desktop.
Save zacharyvoase/c8961cb5fc8a894401e1f29f80975d8c to your computer and use it in GitHub Desktop.
A Lazy Model loader for Django
"""
Do `import lazy_model_loader` as early as possible in your application's
lifecycle, then:
from lazymodels.my_app import MyModel
def some_func():
return MyModel.objects.filter(name='Foo')
We're hacking the Python import system to enable you to import lazy references
to model classes, without having to call `apps.get_model()` in each function
that may use the model. This is a nice on-ramp for pre-Django 1.9 projects that
may import models at load time, but want to save themselves a costly refactor.
`MyModel` will be an instance of `django.utils.functional.SimpleLazyObject`
that dereferences to `my_app.models.MyModel` when the first attribute on it is
accessed.
"""
import imp
import re
import sys
from django.apps import apps
from django.utils.functional import SimpleLazyObject
class LazyModelFinder(object):
"""
Implements the `sys.meta_hooks` interface for custom module loaders.
Fakes the existence of a global 'lazymodels' namespace, where references to
`lazymodels.app_name.ModelName` will be lazy proxies for
`apps.get_model('app_name', 'ModelName')`. You can import a couple of ways:
from lazymodels.my_app import MyModel
Or:
import lazymodels.my_app as lazy_my_app
lazy_my_app.MyModel
Just don't try to access attributes on `MyModel` before all apps have
loaded.
"""
def find_module(self, fullname, path=None):
if not path and re.match(r'^lazymodels(?:\.(?P<app_label>[^\.]+))?$', fullname):
return self
def load_module(self, fullname):
if fullname in sys.modules:
return sys.modules[fullname]
if fullname == 'lazymodels':
return self.get_lazy_apps_module()
match = re.match(r'^lazymodels\.(?P<app_label>[^\.]+)$', fullname)
app_label = match.group('app_label')
return self.get_lazy_app_module(app_label)
def get_lazy_apps_module(self):
module = imp.new_module('lazymodels')
module.__name__ = module.__package__ = 'lazymodels'
module.__file__ = '<lazymodels>'
module.__path__ = []
module.__loader__ = self
sys.modules['lazymodels'] = module
return module
def get_lazy_app_module(self, app_label):
module = LazyAppModule(self, app_label)
sys.modules[module.__name__] = module
return module
class LazyAppModule(object):
def __init__(self, loader, app_label):
self.app_label = app_label
self.__loader__ = loader
self.__name__ = self.__package__ = 'lazymodels.{}'.format(app_label)
self.__file__ = '<lazymodels>'
def __getattr__(self, model_name):
return SimpleLazyObject(lambda: apps.get_model(self.app_label, model_name))
LAZY_MODEL_FINDER = LazyModelFinder()
if LAZY_MODEL_FINDER not in sys.meta_path:
sys.meta_path.append(LAZY_MODEL_FINDER)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment