File: registry.py

package info (click to toggle)
django-ajax-selects 1.7.0-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid, trixie
  • size: 356 kB
  • sloc: python: 924; javascript: 191; makefile: 4
file content (138 lines) | stat: -rw-r--r-- 4,723 bytes parent folder | download | duplicates (3)
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