Created
April 6, 2017 02:04
-
-
Save zacharyvoase/c8961cb5fc8a894401e1f29f80975d8c to your computer and use it in GitHub Desktop.
A Lazy Model loader for Django
This file contains hidden or 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
""" | |
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