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
|
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import autodiscover_modules
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):
"""
Called when loading the application. Cannot be called a second time,
(eg. for testing) as Django will not re-import and re-register anything.
"""
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'"""
return apps.get_model(app_label, model_name)
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
|