File: registries.py

package info (click to toggle)
django-dynamic-preferences 1.17.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 476 kB
  • sloc: python: 3,040; makefile: 3
file content (234 lines) | stat: -rw-r--r-- 8,062 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
from django.core.exceptions import FieldDoesNotExist
from django.apps import apps

# import the logging library
import warnings
import logging
import collections
import persisting_theory

# Get an instance of a logger
logger = logging.getLogger(__name__)


#: The package where autodiscover will try to find preferences to register

from .managers import PreferencesManager
from .settings import preferences_settings
from .exceptions import NotFoundInRegistry
from .types import StringPreference
from .preferences import EMPTY_SECTION, Section


class MissingPreference(StringPreference):
    """
    Used as a fallback when the preference object is not found in registries
    This can happen for example when you delete a preference in the code,
    but don't remove the corresponding entries in database
    """

    pass


class PreferenceModelsRegistry(persisting_theory.Registry):
    """Store relationships beetween preferences model and preferences registry"""

    look_into = preferences_settings.REGISTRY_MODULE

    def register(self, preference_model, preference_registry):
        self[preference_model] = preference_registry
        preference_registry.preference_model = preference_model
        if not hasattr(preference_model, "registry"):
            setattr(preference_model, "registry", preference_registry)
        self.attach_manager(preference_model, preference_registry)

    def attach_manager(self, model, registry):
        if not hasattr(model, "instance"):
            return

        def instance_getter(self):
            return registry.manager(instance=self)

        getter = property(instance_getter)
        instance_class = model._meta.get_field("instance").remote_field.model
        setattr(instance_class, preferences_settings.MANAGER_ATTRIBUTE, getter)

    def get_by_preference(self, preference):
        return self[
            preference._meta.proxy_for_model
            if preference._meta.proxy
            else preference.__class__
        ]

    def get_by_instance(self, instance):
        """Return a preference registry using a model instance"""
        # we iterate through registered preference models in order to get the instance class
        # and check if instance is an instance of this class
        for model, registry in self.items():
            try:
                instance_class = model._meta.get_field("instance").remote_field.model
                if isinstance(instance, instance_class):
                    return registry

            except FieldDoesNotExist:  # global preferences
                pass
        return None


preference_models = PreferenceModelsRegistry()


class PreferenceRegistry(persisting_theory.Registry):

    """
    Registries are special dictionaries that are used by dynamic-preferences to register and access your preferences.
    dynamic-preferences has one registry per Preference type:

    - :py:const:`user_preferences`
    - :py:const:`site_preferences`
    - :py:const:`global_preferences`

    In order to register preferences automatically, you must call :py:func:`autodiscover` in your URLconf.

    """

    look_into = preferences_settings.REGISTRY_MODULE

    #: a name to identify the registry
    name = "preferences_registry"
    preference_model = None

    #: used to reverse urls for sections in form views/templates
    section_url_namespace = None

    def __init__(self, *args, **kwargs):
        super(PreferenceRegistry, self).__init__(*args, **kwargs)
        self.section_objects = collections.OrderedDict()

    def register(self, preference_class):
        """
        Store the given preference class in the registry.

        :param preference_class: a :py:class:`prefs.Preference` subclass
        """
        preference = preference_class(registry=self)
        self.section_objects[preference.section.name] = preference.section

        try:
            self[preference.section.name][preference.name] = preference

        except KeyError:
            self[preference.section.name] = collections.OrderedDict()
            self[preference.section.name][preference.name] = preference

        return preference_class

    def _fallback(self, section_name, pref_name):
        """
        Create a fallback preference object,
        This is used when you have model instances that do not match
        any registered preferences, see #41
        """
        message = (
            "Creating a fallback preference with "
            + 'section "{}" and name "{}".'
            + "This means you have preferences in your database that "
            + "don't match any registered preference. "
            + "If you want to delete these entries, please refer to the "
            + "documentation: https://django-dynamic-preferences.readthedocs.io/en/latest/lifecycle.html"
        )  # NOQA
        warnings.warn(message.format(section_name, pref_name))

        class Fallback(MissingPreference):
            section = Section(name=section_name) if section_name else None
            name = pref_name
            default = ""
            help_text = "Obsolete: missing in registry"

        return Fallback()

    def get(self, name, section=None, fallback=False):
        """
        Returns a previously registered preference

        :param section: The section name under which the preference is registered
        :type section: str.
        :param name: The name of the preference. You can use dotted notation 'section.name' if you want to avoid providing section param
        :type name: str.
        :param fallback: Should we return a dummy preference object instead of raising an error if no preference is found?
        :type name: bool.
        :return: a :py:class:`prefs.BasePreference` instance
        """
        # try dotted notation
        try:
            _section, name = name.split(preferences_settings.SECTION_KEY_SEPARATOR)
            return self[_section][name]

        except ValueError:
            pass

        # use standard params
        try:
            return self[section][name]

        except KeyError:
            if fallback:
                return self._fallback(section_name=section, pref_name=name)
            raise NotFoundInRegistry(
                "No such preference in {0} with section={1} and name={2}".format(
                    self.__class__.__name__, section, name
                )
            )

    def get_by_name(self, name):
        """Get a preference by name only (no section)"""
        for section in self.values():
            for preference in section.values():
                if preference.name == name:
                    return preference
        raise NotFoundInRegistry(
            "No such preference in {0} with name={1}".format(
                self.__class__.__name__, name
            )
        )

    def manager(self, **kwargs):
        """Return a preference manager that can be used to retrieve preference values"""
        return PreferencesManager(registry=self, model=self.preference_model, **kwargs)

    def sections(self):
        """
        :return: a list of apps with registered preferences
        :rtype: list
        """

        return self.keys()

    def preferences(self, section=None):
        """
        Return a list of all registered preferences
        or a list of preferences registered for a given section

        :param section: The section name under which the preference is registered
        :type section: str.
        :return: a list of :py:class:`prefs.BasePreference` instances
        """

        if section is None:
            return [self[section][name] for section in self for name in self[section]]
        else:
            return [self[section][name] for name in self[section]]


class PerInstancePreferenceRegistry(PreferenceRegistry):
    pass


class GlobalPreferenceRegistry(PreferenceRegistry):
    section_url_namespace = "dynamic_preferences:global.section"

    def populate(self, **kwargs):
        return self.models(**kwargs)


global_preferences_registry = GlobalPreferenceRegistry()