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

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()