Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Last active April 5, 2017 22:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacharyvoase/fdf6153e20fe4a5c459d6ffb3975cb59 to your computer and use it in GitHub Desktop.
Save zacharyvoase/fdf6153e20fe4a5c459d6ffb3975cb59 to your computer and use it in GitHub Desktop.
Django Model Injection
# -*- coding: utf-8 -*-
"""
Model class injectors, to make Django 1.9+'s app registry easier to work with.
Django 1.9 made it an error to import an app's models submodule when importing
the app module itself. Instead, you should use `apps.get_model(app_label,
model_name)` to get the model class, and signals should be wired up in the
`ready()` hook of a custom `AppConfig` subclass.
The `@inject_models` decorator makes it easy to transition functions that use
models to a dependency-injection format, meaning you can have your cake and eat
it. Code that used to do the following:
from myapp.models import MyModel
def lookup_model(name):
return MyModel.objects.filter(name=name)
Can be restructured to the following:
from modelinject import inject_models, imodel
@inject_models
def lookup_model(name, MyModel=imodel('myapp.MyModel')):
return MyModel.objects.filter(name=name)
At call-time, `@inject_models` will replace the `imodel()` with the result of
calling `django.apps.apps.get_model('myapp', 'MyModel')`. The downside is that
you're incurring a call to `get_model()` every time `lookup_model` is called,
but this should be a good stop-gap when upgrading a codebase from Django 1.8 or
prior to 1.9 or later.
"""
import collections
import functools
import inspect
from django.apps import apps
_ModelLabel = collections.namedtuple('_ModelLabel', ['app_label', 'model_name'])
def inject_models(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
call_args = inspect.getcallargs(func, *args, **kwargs)
injected_args = {}
for arg_name, arg_value in call_args.iteritems():
if isinstance(arg_value, _ModelLabel):
injected_args[arg_name] = apps.get_model(*arg_value)
else:
injected_args[arg_name] = arg_value
return func(**injected_args)
return wrapper
def imodel(model_id):
app_label, model_name = model_id.split('.')
return _ModelLabel(app_label, model_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment