You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
7.9 KiB
234 lines
7.9 KiB
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()
|