1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
class LookupChannelRegistry(object):
"""
Registry for LookupChannels activated for your django project.
This includes any installed apps that contain lookup.py modules (django 1.7+)
and any lookups that are explicitly declared in `settings.AJAX_LOOKUP_CHANNELS`
"""
_registry = {}
def load_channels(self):
self._registry = {}
try:
from django.utils.module_loading import autodiscover_modules
except ImportError:
pass
else:
autodiscover_modules('lookups')
if hasattr(settings, 'AJAX_LOOKUP_CHANNELS'):
self.register(settings.AJAX_LOOKUP_CHANNELS)
def register(self, lookup_specs):
"""Register a set of lookup definitions.
Args:
lookup_specs (dict): One or more LookupChannel specifications
- `{'channel': LookupChannelSubclass}`
- `{'channel': ('module.of.lookups', 'MyLookupClass')}`
- `{'channel': {'model': 'MyModelToBeLookedUp', 'search_field': 'field_to_search'}}`
"""
for channel, spec in lookup_specs.items():
if spec is None: # unset
if channel in self._registry:
del self._registry[channel]
else:
self._registry[channel] = spec
def get(self, channel):
"""Find the LookupChannel class for the named channel and instantiate it.
Args:
channel (string): - name that the lookup channel was registered at
Returns:
LookupChannel
Raises:
ImproperlyConfigured - if channel is not found.
Exception - invalid lookup_spec was stored in registery
"""
from ajax_select import LookupChannel
try:
lookup_spec = self._registry[channel]
except KeyError:
raise ImproperlyConfigured(
"No ajax_select LookupChannel named %(channel)r is registered." % {'channel': channel})
if (type(lookup_spec) is type) and issubclass(lookup_spec, LookupChannel):
return lookup_spec()
# damnit python.
# ideally this would match regardless of how you imported the parent class
# but these are different classes:
# from ajax_select.lookup_channel import LookupChannel
# from ajax_select import LookupChannel
elif isinstance(lookup_spec, dict):
# 'channel' : dict(model='app.model', search_field='title' )
# generate a simple channel dynamically
return self.make_channel(lookup_spec['model'], lookup_spec['search_field'])
elif isinstance(lookup_spec, tuple):
# a tuple
# 'channel' : ('app.module','LookupClass')
# from app.module load LookupClass and instantiate
lookup_module = __import__(lookup_spec[0], {}, {}, [''])
lookup_class = getattr(lookup_module, lookup_spec[1])
return lookup_class()
else:
raise Exception("Invalid lookup spec: %s" % lookup_spec)
def is_registered(self, channel):
return channel in self._registry
def make_channel(self, app_model, arg_search_field):
"""Automatically make a LookupChannel.
Args:
app_model (str): app_name.ModelName
arg_search_field (str): the field to search against and to display in search results
Returns:
LookupChannel
"""
from ajax_select import LookupChannel
app_label, model_name = app_model.split(".")
class MadeLookupChannel(LookupChannel):
model = get_model(app_label, model_name)
search_field = arg_search_field
return MadeLookupChannel()
registry = LookupChannelRegistry()
def get_model(app_label, model_name):
"""Loads the model given an 'app_label' 'ModelName'"""
try:
# django >= 1.7
from django.apps import apps
except ImportError:
# django < 1.7
from django.db import models
return models.get_model(app_label, model_name)
else:
return apps.get_model(app_label, model_name)
def can_autodiscover():
try:
from django.apps import AppConfig # noqa
except ImportError:
return False
return True
def register(channel):
"""Decorator to register a LookupClass.
Example::
from ajax_select import LookupChannel, register
@register('agent')
class AgentLookup(LookupClass):
def get_query(self):
...
def format_item(self):
...
"""
def _wrapper(lookup_class):
if not channel:
raise ValueError('Lookup Channel must have a channel name')
registry.register({channel: lookup_class})
return lookup_class
return _wrapper
|