File: registry.py

package info (click to toggle)
django-ajax-selects 1.4.3-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 356 kB
  • ctags: 212
  • sloc: python: 777; makefile: 4
file content (154 lines) | stat: -rw-r--r-- 4,997 bytes parent folder | download
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