pax_global_header00006660000000000000000000000064144707524220014521gustar00rootroot0000000000000052 comment=bc9707d618d733f209483f665320cc4484eb7d52 django-constance-3.1.0/000077500000000000000000000000001447075242200147375ustar00rootroot00000000000000django-constance-3.1.0/.coveragerc000066400000000000000000000001761447075242200170640ustar00rootroot00000000000000[run] source = constance branch = 1 omit = */pytest.py [report] omit = *tests*,*migrations*,.tox/*,setup.py,*settings.py django-constance-3.1.0/.github/000077500000000000000000000000001447075242200162775ustar00rootroot00000000000000django-constance-3.1.0/.github/ISSUE_TEMPLATE.md000066400000000000000000000003241447075242200210030ustar00rootroot00000000000000### Describe the problem Tell us about the problem you're having. ### Steps to reproduce Tell us how to reproduce it. ### System configuration * Django version: * Python version: * Django-Constance version: django-constance-3.1.0/.github/workflows/000077500000000000000000000000001447075242200203345ustar00rootroot00000000000000django-constance-3.1.0/.github/workflows/release.yml000066400000000000000000000025451447075242200225050ustar00rootroot00000000000000name: Release on: push: tags: - '*' jobs: build: if: github.repository == 'jazzband/django-constance' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.8 - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: Cache uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: release-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} restore-keys: | release- - name: Install dependencies run: | python -m pip install -U pip python -m pip install -U setuptools twine wheel - name: Build package run: | python setup.py --version python setup.py sdist --format=gztar bdist_wheel twine check dist/* - name: Upload packages to Jazzband if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: user: jazzband password: ${{ secrets.JAZZBAND_RELEASE_KEY }} repository_url: https://jazzband.co/projects/django-constance/upload django-constance-3.1.0/.github/workflows/test.yml000066400000000000000000000022271447075242200220410ustar00rootroot00000000000000name: Test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 5 matrix: python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.8'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: Cache uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} restore-keys: | ${{ matrix.python-version }}-v1- - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade 'tox<4' tox-gh-actions - name: Tox tests run: | tox -v - name: Upload coverage uses: codecov/codecov-action@v3 with: name: Python ${{ matrix.python-version }} django-constance-3.1.0/.gitignore000066400000000000000000000001531447075242200167260ustar00rootroot00000000000000.project .pydevproject *.pyc *.egg-info build/ dist/ test.db .tox .coverage coverage.xml docs/_build .idea django-constance-3.1.0/.pre-commit-config.yaml000066400000000000000000000004611447075242200212210ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: python-check-blanket-noqa - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-merge-conflict - id: check-yaml ci: autoupdate_schedule: quarterly django-constance-3.1.0/.tx/000077500000000000000000000000001447075242200154505ustar00rootroot00000000000000django-constance-3.1.0/.tx/config000066400000000000000000000003421447075242200166370ustar00rootroot00000000000000[main] host = https://www.transifex.com lang_map = sr@latin:sr_Latn [django-constance.main] file_filter = constance/locale//LC_MESSAGES/django.po source_file = constance/locale/en/LC_MESSAGES/django.po source_lang = en django-constance-3.1.0/AUTHORS000066400000000000000000000031361447075242200160120ustar00rootroot00000000000000Ales Zoulek Alexander frenzel Bouke Haarsma Camilo Nova Charlie Hornsby Curtis Maloney Dan Poirier David Burke Florian Apolloner Igor Támara Ilya Chichak Jake Merdich Jannis Leidel Janusz Harkot Jiri Barton John Carter Jonas Kuba Zarzycki Leandra Finger Les Orchard Lin Xianyi Marcin Baran Mario Orlandi Mario Rosa Mariusz Felisiak Mattia Larentis Merijn Bertels Omer Katz Petr Knap Philip Neustrom Pierre-Olivier Marec Roman Krejcik Silvan Spross Sławek Ehlert Vojtech Jasny Yin Jifeng illumin-us-r3v0lution mega saw2th trbs vl <1844144@gmail.com> vl Vladas Tamoshaitis Dmitriy Tatarkin Alexandr Artemyev Elisey Zanko django-constance-3.1.0/CODE_OF_CONDUCT.md000066400000000000000000000045071447075242200175440ustar00rootroot00000000000000# Code of Conduct As contributors and maintainers of the Jazzband projects, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in the Jazzband a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery - Personal attacks - Trolling or insulting/derogatory comments - Public or private harassment - Publishing other's private information, such as physical or electronic addresses, without explicit permission - Other unethical or unprofessional conduct The Jazzband roadies have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. By adopting this Code of Conduct, the roadies commit themselves to fairly and consistently applying these principles to every aspect of managing the jazzband projects. Roadies who do not follow or enforce the Code of Conduct may be permanently removed from the Jazzband roadies. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Roadies are obligated to maintain confidentiality with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/3/0/ django-constance-3.1.0/CONTRIBUTING.md000066400000000000000000000004621447075242200171720ustar00rootroot00000000000000[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/) This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/docs/conduct) and follow the [guidelines](https://jazzband.co/docs/guidelines). django-constance-3.1.0/LICENSE000066400000000000000000000027101447075242200157440ustar00rootroot00000000000000Copyright (c) 2009-2017, Jazzband All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-constance-3.1.0/MANIFEST.in000066400000000000000000000001771447075242200165020ustar00rootroot00000000000000recursive-include constance/templates *.html recursive-include constance/locale *.po *.mo recursive-include constance/static * django-constance-3.1.0/README.rst000066400000000000000000000020161447075242200164250ustar00rootroot00000000000000Constance - Dynamic Django settings =================================== .. image:: https://jazzband.co/static/img/badge.svg :alt: Jazzband :target: https://jazzband.co/ .. image:: https://img.shields.io/readthedocs/django-constance.svg :target: https://django-constance.readthedocs.io/ :alt: Documentation .. image:: https://github.com/jazzband/django-constance/workflows/Test/badge.svg :target: https://github.com/jazzband/django-constance/actions :alt: GitHub Actions .. image:: https://codecov.io/gh/jazzband/django-constance/branch/master/graph/badge.svg :target: https://codecov.io/gh/jazzband/django-constance :alt: Coverage A Django app for storing dynamic settings in pluggable backends (Redis and Django model backend built in) with an integration with the Django admin app. For more information see the documentation at: https://django-constance.readthedocs.io/ If you have questions or have trouble using the app please file a bug report at: https://github.com/jazzband/django-constance/issues django-constance-3.1.0/constance/000077500000000000000000000000001447075242200167145ustar00rootroot00000000000000django-constance-3.1.0/constance/__init__.py000066400000000000000000000003261447075242200210260ustar00rootroot00000000000000from django.utils.functional import LazyObject __version__ = '3.1.0' class LazyConfig(LazyObject): def _setup(self): from .base import Config self._wrapped = Config() config = LazyConfig() django-constance-3.1.0/constance/admin.py000066400000000000000000000170361447075242200203650ustar00rootroot00000000000000from collections import OrderedDict from datetime import date, datetime from operator import itemgetter from django import VERSION, forms from django.apps import apps from django.contrib import admin, messages from django.contrib.admin.options import csrf_protect_m from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.template.response import TemplateResponse from django.urls import path from django.utils.formats import localize from django.utils.translation import gettext_lazy as _ from . import LazyConfig, settings from .forms import ConstanceForm from .utils import get_values config = LazyConfig() class ConstanceAdmin(admin.ModelAdmin): change_list_template = 'admin/constance/change_list.html' change_list_form = ConstanceForm def __init__(self, model, admin_site): model._meta.concrete_model = Config super().__init__(model, admin_site) def get_urls(self): info = self.model._meta.app_label, self.model._meta.module_name return [ path('', self.admin_site.admin_view(self.changelist_view), name='%s_%s_changelist' % info), path('', self.admin_site.admin_view(self.changelist_view), name='%s_%s_add' % info), ] def get_config_value(self, name, options, form, initial): default, help_text = options[0], options[1] field_type = None if len(options) == 3: field_type = options[2] # First try to load the value from the actual backend value = initial.get(name) # Then if the returned value is None, get the default if value is None: value = getattr(config, name) form_field = form[name] config_value = { 'name': name, 'default': localize(default), 'raw_default': default, 'help_text': _(help_text), 'value': localize(value), 'modified': localize(value) != localize(default), 'form_field': form_field, 'is_date': isinstance(default, date), 'is_datetime': isinstance(default, datetime), 'is_checkbox': isinstance(form_field.field.widget, forms.CheckboxInput), 'is_file': isinstance(form_field.field.widget, forms.FileInput), } if field_type and field_type in settings.ADDITIONAL_FIELDS: serialized_default = form[name].field.prepare_value(default) config_value['default'] = serialized_default config_value['raw_default'] = serialized_default config_value['value'] = form[name].field.prepare_value(value) return config_value def get_changelist_form(self, request): """ Returns a Form class for use in the changelist_view. """ # Defaults to self.change_list_form in order to preserve backward # compatibility return self.change_list_form @csrf_protect_m def changelist_view(self, request, extra_context=None): if not self.has_view_or_change_permission(request): raise PermissionDenied initial = get_values() form_cls = self.get_changelist_form(request) form = form_cls(initial=initial, request=request) if request.method == 'POST' and request.user.has_perm('constance.change_config'): form = form_cls( data=request.POST, files=request.FILES, initial=initial, request=request ) if form.is_valid(): form.save() messages.add_message( request, messages.SUCCESS, _('Live settings updated successfully.'), ) return HttpResponseRedirect('.') else: messages.add_message( request, messages.ERROR, _('Failed to update live settings.'), ) context = dict( self.admin_site.each_context(request), config_values=[], title=self.model._meta.app_config.verbose_name, app_label='constance', opts=self.model._meta, form=form, media=self.media + form.media, icon_type='gif' if VERSION < (1, 9) else 'svg', ) for name, options in settings.CONFIG.items(): context['config_values'].append( self.get_config_value(name, options, form, initial) ) if settings.CONFIG_FIELDSETS: if isinstance(settings.CONFIG_FIELDSETS, dict): fieldset_items = settings.CONFIG_FIELDSETS.items() else: fieldset_items = settings.CONFIG_FIELDSETS context['fieldsets'] = [] for fieldset_title, fieldset_data in fieldset_items: if type(fieldset_data) == dict: fields_list = fieldset_data['fields'] collapse = fieldset_data.get('collapse', False) else: fields_list = fieldset_data collapse = False absent_fields = [field for field in fields_list if field not in settings.CONFIG] assert not any(absent_fields), ( "CONSTANCE_CONFIG_FIELDSETS contains field(s) that does " "not exist: %s" % ', '.join(absent_fields)) config_values = [] for name in fields_list: options = settings.CONFIG.get(name) if options: config_values.append( self.get_config_value(name, options, form, initial) ) fieldset_context = { 'title': fieldset_title, 'config_values': config_values } if collapse: fieldset_context['collapse'] = True context['fieldsets'].append(fieldset_context) if not isinstance(settings.CONFIG_FIELDSETS, (OrderedDict, tuple)): context['fieldsets'].sort(key=itemgetter('title')) if not isinstance(settings.CONFIG, OrderedDict): context['config_values'].sort(key=itemgetter('name')) request.current_app = self.admin_site.name return TemplateResponse(request, self.change_list_template, context) def has_add_permission(self, *args, **kwargs): return False def has_delete_permission(self, *args, **kwargs): return False def has_change_permission(self, request, obj=None): if settings.SUPERUSER_ONLY: return request.user.is_superuser return super().has_change_permission(request, obj) class Config: class Meta: app_label = 'constance' object_name = 'Config' concrete_model = None model_name = module_name = 'config' verbose_name_plural = _('config') abstract = False swapped = False def get_ordered_objects(self): return False def get_change_permission(self): return 'change_%s' % self.model_name @property def app_config(self): return apps.get_app_config(self.app_label) @property def label(self): return '%s.%s' % (self.app_label, self.object_name) @property def label_lower(self): return '%s.%s' % (self.app_label, self.model_name) _meta = Meta() admin.site.register([Config], ConstanceAdmin)django-constance-3.1.0/constance/apps.py000066400000000000000000000004401447075242200202270ustar00rootroot00000000000000from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class ConstanceConfig(AppConfig): name = 'constance' verbose_name = _('Constance') default_auto_field = 'django.db.models.AutoField' def ready(self): from . import checks django-constance-3.1.0/constance/backends/000077500000000000000000000000001447075242200204665ustar00rootroot00000000000000django-constance-3.1.0/constance/backends/__init__.py000066400000000000000000000011231447075242200225740ustar00rootroot00000000000000""" Defines the base constance backend """ class Backend: def get(self, key): """ Get the key from the backend store and return the value. Return None if not found. """ raise NotImplementedError def mget(self, keys): """ Get the keys from the backend store and return a list of the values. Return an empty list if not found. """ raise NotImplementedError def set(self, key, value): """ Add the value to the backend store given the key. """ raise NotImplementedError django-constance-3.1.0/constance/backends/database.py000066400000000000000000000110421447075242200226020ustar00rootroot00000000000000from django.core.cache import caches from django.core.cache.backends.locmem import LocMemCache from django.core.exceptions import ImproperlyConfigured from django.db import ( IntegrityError, OperationalError, ProgrammingError, transaction, ) from django.db.models.signals import post_save from constance.backends import Backend from constance import settings, signals, config class DatabaseBackend(Backend): def __init__(self): from constance.models import Constance self._model = Constance self._prefix = settings.DATABASE_PREFIX self._autofill_timeout = settings.DATABASE_CACHE_AUTOFILL_TIMEOUT self._autofill_cachekey = 'autofilled' if self._model._meta.app_config is None: raise ImproperlyConfigured( "The constance.backends.database app isn't installed " "correctly. Make sure it's in your INSTALLED_APPS setting.") if settings.DATABASE_CACHE_BACKEND: self._cache = caches[settings.DATABASE_CACHE_BACKEND] if isinstance(self._cache, LocMemCache): raise ImproperlyConfigured( "The CONSTANCE_DATABASE_CACHE_BACKEND setting refers to a " "subclass of Django's local-memory backend (%r). Please " "set it to a backend that supports cross-process caching." % settings.DATABASE_CACHE_BACKEND) else: self._cache = None self.autofill() # Clear simple cache. post_save.connect(self.clear, sender=self._model) def add_prefix(self, key): return "%s%s" % (self._prefix, key) def autofill(self): if not self._autofill_timeout or not self._cache: return full_cachekey = self.add_prefix(self._autofill_cachekey) if self._cache.get(full_cachekey): return autofill_values = {} autofill_values[full_cachekey] = 1 for key, value in self.mget(settings.CONFIG): autofill_values[self.add_prefix(key)] = value self._cache.set_many(autofill_values, timeout=self._autofill_timeout) def mget(self, keys): if not keys: return keys = {self.add_prefix(key): key for key in keys} try: stored = self._model._default_manager.filter(key__in=keys) for const in stored: yield keys[const.key], const.value except (OperationalError, ProgrammingError): pass def get(self, key): key = self.add_prefix(key) if self._cache: value = self._cache.get(key) if value is None: self.autofill() value = self._cache.get(key) else: value = None if value is None: try: value = self._model._default_manager.get(key=key).value except (OperationalError, ProgrammingError, self._model.DoesNotExist): pass else: if self._cache: self._cache.add(key, value) return value def set(self, key, value): key = self.add_prefix(key) created = False queryset = self._model._default_manager.all() # Set _for_write attribute as get_or_create method does # https://github.com/django/django/blob/2.2.11/django/db/models/query.py#L536 queryset._for_write = True try: constance = queryset.get(key=key) except (OperationalError, ProgrammingError): # database is not created, noop return except self._model.DoesNotExist: try: with transaction.atomic(using=queryset.db): queryset.create(key=key, value=value) created = True except IntegrityError as error: # Allow concurrent writes constance = queryset.get(key=key) if not created: old_value = constance.value constance.value = value constance.save() else: old_value = None if self._cache: self._cache.set(key, value) signals.config_updated.send( sender=config, key=key, old_value=old_value, new_value=value ) def clear(self, sender, instance, created, **kwargs): if self._cache and not created: keys = [self.add_prefix(k) for k in settings.CONFIG] keys.append(self.add_prefix(self._autofill_cachekey)) self._cache.delete_many(keys) self.autofill() django-constance-3.1.0/constance/backends/memory.py000066400000000000000000000016761447075242200223620ustar00rootroot00000000000000from threading import Lock from . import Backend from .. import signals, config class MemoryBackend(Backend): """ Simple in-memory backend that should be mostly used for testing purposes """ _storage = {} _lock = Lock() def __init__(self): super().__init__() def get(self, key): with self._lock: return self._storage.get(key) def mget(self, keys): if not keys: return result = [] with self._lock: for key in keys: value = self._storage.get(key) if value is not None: result.append((key, value)) return result def set(self, key, value): with self._lock: old_value = self._storage.get(key) self._storage[key] = value signals.config_updated.send( sender=config, key=key, old_value=old_value, new_value=value ) django-constance-3.1.0/constance/backends/redisd.py000066400000000000000000000053441447075242200223200ustar00rootroot00000000000000from pickle import loads, dumps from threading import RLock from time import monotonic from django.core.exceptions import ImproperlyConfigured from . import Backend from .. import settings, utils, signals, config class RedisBackend(Backend): def __init__(self): super().__init__() self._prefix = settings.REDIS_PREFIX connection_cls = settings.REDIS_CONNECTION_CLASS if connection_cls is not None: self._rd = utils.import_module_attr(connection_cls)() else: try: import redis except ImportError: raise ImproperlyConfigured( "The Redis backend requires redis-py to be installed.") if isinstance(settings.REDIS_CONNECTION, str): self._rd = redis.from_url(settings.REDIS_CONNECTION) else: self._rd = redis.Redis(**settings.REDIS_CONNECTION) def add_prefix(self, key): return "%s%s" % (self._prefix, key) def get(self, key): value = self._rd.get(self.add_prefix(key)) if value: return loads(value) return None def mget(self, keys): if not keys: return prefixed_keys = [self.add_prefix(key) for key in keys] for key, value in zip(keys, self._rd.mget(prefixed_keys)): if value: yield key, loads(value) def set(self, key, value): old_value = self.get(key) self._rd.set(self.add_prefix(key), dumps(value, protocol=settings.REDIS_PICKLE_VERSION)) signals.config_updated.send( sender=config, key=key, old_value=old_value, new_value=value ) class CachingRedisBackend(RedisBackend): _sentinel = object() _lock = RLock() def __init__(self): super().__init__() self._timeout = settings.REDIS_CACHE_TIMEOUT self._cache = {} self._sentinel = object() def _has_expired(self, value): return value[0] <= monotonic() def _cache_value(self, key, new_value): self._cache[key] = (monotonic() + self._timeout, new_value) def get(self, key): value = self._cache.get(key, self._sentinel) if value is self._sentinel or self._has_expired(value): with self._lock: new_value = super().get(key) self._cache_value(key, new_value) return new_value return value[1] def set(self, key, value): with self._lock: super().set(key, value) self._cache_value(key, value) def mget(self, keys): if not keys: return for key in keys: value = self.get(key) if value is not None: yield key, value django-constance-3.1.0/constance/base.py000066400000000000000000000015761447075242200202110ustar00rootroot00000000000000from . import settings, utils class Config: """ The global config wrapper that handles the backend. """ def __init__(self): super().__setattr__('_backend', utils.import_module_attr(settings.BACKEND)()) def __getattr__(self, key): try: if not len(settings.CONFIG[key]) in (2, 3): raise AttributeError(key) default = settings.CONFIG[key][0] except KeyError: raise AttributeError(key) result = self._backend.get(key) if result is None: result = default setattr(self, key, default) return result return result def __setattr__(self, key, value): if key not in settings.CONFIG: raise AttributeError(key) self._backend.set(key, value) def __dir__(self): return settings.CONFIG.keys() django-constance-3.1.0/constance/checks.py000066400000000000000000000051571447075242200205360ustar00rootroot00000000000000from typing import Tuple, Set, List from django.core import checks from django.core.checks import CheckMessage from django.utils.translation import gettext_lazy as _ @checks.register("constance") def check_fieldsets(*args, **kwargs) -> List[CheckMessage]: """ A Django system check to make sure that, if defined, CONFIG_FIELDSETS is consistent with settings.CONFIG. """ from . import settings errors = [] if hasattr(settings, "CONFIG_FIELDSETS") and settings.CONFIG_FIELDSETS: missing_keys, extra_keys = get_inconsistent_fieldnames() if missing_keys: check = checks.Warning( _( "CONSTANCE_CONFIG_FIELDSETS is missing " "field(s) that exists in CONSTANCE_CONFIG." ), hint=", ".join(sorted(missing_keys)), obj="settings.CONSTANCE_CONFIG", id="constance.E001", ) errors.append(check) if extra_keys: check = checks.Warning( _( "CONSTANCE_CONFIG_FIELDSETS contains extra " "field(s) that does not exist in CONFIG." ), hint=", ".join(sorted(extra_keys)), obj="settings.CONSTANCE_CONFIG", id="constance.E002", ) errors.append(check) return errors def get_inconsistent_fieldnames() -> Tuple[Set, Set]: """ Returns a pair of values: 1) set of keys from settings.CONFIG that are not accounted for in settings.CONFIG_FIELDSETS 2) set of keys from settings.CONFIG_FIELDSETS that are not present in settings.CONFIG If there are no fieldnames in settings.CONFIG_FIELDSETS, returns an empty set. """ from . import settings if isinstance(settings.CONFIG_FIELDSETS, dict): fieldset_items = settings.CONFIG_FIELDSETS.items() else: fieldset_items = settings.CONFIG_FIELDSETS unique_field_names = set() for fieldset_title, fields_list in fieldset_items: # fields_list can be a dictionary, when a fieldset is defined as collapsible # https://django-constance.readthedocs.io/en/latest/#fieldsets-collapsing if isinstance(fields_list, dict) and 'fields' in fields_list: fields_list = fields_list['fields'] unique_field_names.update(fields_list) if not unique_field_names: return unique_field_names, unique_field_names config_keys = set(settings.CONFIG.keys()) missing_keys = config_keys - unique_field_names extra_keys = unique_field_names - config_keys return missing_keys, extra_keys django-constance-3.1.0/constance/context_processors.py000066400000000000000000000005271447075242200232400ustar00rootroot00000000000000import constance def config(request): """ Simple context processor that puts the config into every RequestContext. Just make sure you have a setting like this: TEMPLATE_CONTEXT_PROCESSORS = ( # ... 'constance.context_processors.config', ) """ return {"config": constance.config} django-constance-3.1.0/constance/forms.py000066400000000000000000000131461447075242200204210ustar00rootroot00000000000000import hashlib from datetime import date, datetime, time, timedelta from decimal import Decimal from os.path import join from django import conf, forms from django.contrib import messages from django.contrib.admin import widgets from django.core.exceptions import ImproperlyConfigured from django.core.files.storage import default_storage from django.forms import fields from django.utils import timezone from django.utils.encoding import smart_bytes from django.utils.module_loading import import_string from django.utils.text import normalize_newlines from django.utils.translation import gettext_lazy as _ from . import LazyConfig, settings from .checks import get_inconsistent_fieldnames config = LazyConfig() NUMERIC_WIDGET = forms.TextInput(attrs={'size': 10}) INTEGER_LIKE = (fields.IntegerField, {'widget': NUMERIC_WIDGET}) STRING_LIKE = (fields.CharField, { 'widget': forms.Textarea(attrs={'rows': 3}), 'required': False, }) FIELDS = { bool: (fields.BooleanField, {'required': False}), int: INTEGER_LIKE, Decimal: (fields.DecimalField, {'widget': NUMERIC_WIDGET}), str: STRING_LIKE, datetime: ( fields.SplitDateTimeField, {'widget': widgets.AdminSplitDateTime} ), timedelta: ( fields.DurationField, {'widget': widgets.AdminTextInputWidget} ), date: (fields.DateField, {'widget': widgets.AdminDateWidget}), time: (fields.TimeField, {'widget': widgets.AdminTimeWidget}), float: (fields.FloatField, {'widget': NUMERIC_WIDGET}), } def parse_additional_fields(fields): for key in fields: field = list(fields[key]) if len(field) == 1: field.append({}) field[0] = import_string(field[0]) if 'widget' in field[1]: klass = import_string(field[1]['widget']) field[1]['widget'] = klass( **(field[1].get('widget_kwargs', {}) or {}) ) if 'widget_kwargs' in field[1]: del field[1]['widget_kwargs'] fields[key] = field return fields FIELDS.update(parse_additional_fields(settings.ADDITIONAL_FIELDS)) class ConstanceForm(forms.Form): version = forms.CharField(widget=forms.HiddenInput) def __init__(self, initial, request=None, *args, **kwargs): super().__init__(*args, initial=initial, **kwargs) version_hash = hashlib.sha256() only_view = request and not request.user.has_perm('constance.change_config') if only_view: messages.warning( request, _("You don't have permission to change these values"), ) for name, options in settings.CONFIG.items(): default = options[0] if len(options) == 3: config_type = options[2] if config_type not in settings.ADDITIONAL_FIELDS and not isinstance(default, config_type): raise ImproperlyConfigured(_("Default value type must be " "equal to declared config " "parameter type. Please fix " "the default value of " "'%(name)s'.") % {'name': name}) else: config_type = type(default) if config_type not in FIELDS: raise ImproperlyConfigured(_("Constance doesn't support " "config values of the type " "%(config_type)s. Please fix " "the value of '%(name)s'.") % {'config_type': config_type, 'name': name}) field_class, kwargs = FIELDS[config_type] if only_view: kwargs['disabled'] = True self.fields[name] = field_class(label=name, **kwargs) version_hash.update(smart_bytes(initial.get(name, ''))) self.initial['version'] = version_hash.hexdigest() def save(self): for file_field in self.files: file = self.cleaned_data[file_field] self.cleaned_data[file_field] = default_storage.save(join(settings.FILE_ROOT, file.name), file) for name in settings.CONFIG: current = getattr(config, name) new = self.cleaned_data[name] if isinstance(new, str): new = normalize_newlines(new) if conf.settings.USE_TZ and isinstance(current, datetime) and not timezone.is_aware(current): current = timezone.make_aware(current) if current != new: setattr(config, name, new) def clean_version(self): value = self.cleaned_data['version'] if settings.IGNORE_ADMIN_VERSION_CHECK: return value if value != self.initial['version']: raise forms.ValidationError(_('The settings have been modified ' 'by someone else. Please reload the ' 'form and resubmit your changes.')) return value def clean(self): cleaned_data = super().clean() if not settings.CONFIG_FIELDSETS: return cleaned_data missing_keys, extra_keys = get_inconsistent_fieldnames() if missing_keys or extra_keys: raise forms.ValidationError(_('CONSTANCE_CONFIG_FIELDSETS is missing ' 'field(s) that exists in CONSTANCE_CONFIG.')) return cleaned_data django-constance-3.1.0/constance/locale/000077500000000000000000000000001447075242200201535ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/ar/000077500000000000000000000000001447075242200205555ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/ar/LC_MESSAGES/000077500000000000000000000000001447075242200223425ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/ar/LC_MESSAGES/django.mo000066400000000000000000000041551447075242200241460ustar00rootroot00000000000000,R hOoW8 #5:b?  ^,.?@  /<K\   CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2020-12-01 12:05+0100 Language: ar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last-Translator: Language-Team: X-Generator: Poedit 2.4.2 لا يحتوي CONSTANCE_CONFIG_FIELDSETS على حقول موجودة في CONSTANCE_CONFIG.كونستانسلا يعتمد كونستانس قيم التكوين من النوع %(config_type)s. الرجاء إصلاح قيمة '%(name)s'.افتراضييجب أن يكون نوع القيمة الافتراضي مساوياً لنوع معلمة التكوين المعلن. الرجاء إصلاح القيمة الافتراضية لـ '%(name)s'.الحصول على / تعيين إعدادات التكوين في قاعدة البيانات التي تعالجها كونستانسالصفحة الرئيسيةتم تعديلهتم تحديث الإعدادات المباشرة بنجاح.الإسمحفظتم تعديل الإعدادات بواسطة شخص آخر. الرجاء إعادة تحميل النموذج وإعادة إرسال التغييرات.القيمةالتكوينكونستانسكونستانسdjango-constance-3.1.0/constance/locale/ar/LC_MESSAGES/django.po000066400000000000000000000056361447075242200241560ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2020-11-30 23:15+0100\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: \n" "Language-Team: \n" "X-Generator: \n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "يجب أن يكون نوع القيمة الافتراضي مساوياً لنوع معلمة التكوين المعلن. الرجاء " "إصلاح القيمة الافتراضية لـ '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "لا يعتمد كونستانس قيم التكوين من النوع %(config_type)s. الرجاء إصلاح قيمة " "'%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "تم تعديل الإعدادات بواسطة شخص آخر. الرجاء إعادة تحميل النموذج وإعادة إرسال " "التغييرات." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" "لا يحتوي CONSTANCE_CONFIG_FIELDSETS على حقول موجودة في CONSTANCE_CONFIG." #: admin.py:224 msgid "Live settings updated successfully." msgstr "تم تحديث الإعدادات المباشرة بنجاح." #: admin.py:285 msgid "config" msgstr "التكوين" #: apps.py:8 msgid "Constance" msgstr "كونستانس" #: backends/database/models.py:19 msgid "constance" msgstr "كونستانس" #: backends/database/models.py:20 msgid "constances" msgstr "كونستانس" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "الحصول على / تعيين إعدادات التكوين في قاعدة البيانات التي تعالجها كونستانس" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "حفظ" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "الصفحة الرئيسية" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "الإسم" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "افتراضي" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "القيمة" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "تم تعديله" django-constance-3.1.0/constance/locale/cs_CZ/000077500000000000000000000000001447075242200211545ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/cs_CZ/LC_MESSAGES/000077500000000000000000000000001447075242200227415ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/cs_CZ/LC_MESSAGES/django.mo000066400000000000000000000017331447075242200245440ustar00rootroot00000000000000 t #*NSX^ e ozYk q%     DefaultHomeIs modifiedLive settings updated successfully.NameSaveValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/django-constance/language/cs_CZ/) Language: cs_CZ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; Výchozí hodnotaDomůJe změněna?Nastavení bylo úspěšně uloženo.NázevUložitHodnotanastaveníkonstantakonstantydjango-constance-3.1.0/constance/locale/cs_CZ/LC_MESSAGES/django.po000066400000000000000000000045051447075242200245470ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/" "django-constance/language/cs_CZ/)\n" "Language: cs_CZ\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Nastavení bylo úspěšně uloženo." #: admin.py:285 msgid "config" msgstr "nastavení" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "konstanta" #: backends/database/models.py:20 msgid "constances" msgstr "konstanty" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Uložit" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Domů" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Název" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Výchozí hodnota" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Hodnota" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Je změněna?" #~ msgid "Constance config" #~ msgstr "Nastavení konstant" django-constance-3.1.0/constance/locale/de/000077500000000000000000000000001447075242200205435ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/de/LC_MESSAGES/000077500000000000000000000000001447075242200223305ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/de/LC_MESSAGES/django.mo000066400000000000000000000027121447075242200241310ustar00rootroot00000000000000H IhS #bfl s } 6|@5|%       ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2014-11-27 18:17+0000 Last-Translator: Jannis Leidel Language-Team: German (http://www.transifex.com/projects/p/django-constance/language/de/) Language: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); KonstanzeKonstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. Bitte den Ausgangswert von '%(name)s' ändern.VoreinstellungStartIst modifiziertDie Livekonfiguration wurde erfolgreich aktualisiert.NameSichernDie Konfiguration wurde seit Öffnen dieser Seite verändert. Bitte die Seite neuladen und die Änderungen erneut vornehmen.WertKonfigurationKonstanzeKonstanzesdjango-constance-3.1.0/constance/locale/de/LC_MESSAGES/django.po000066400000000000000000000055471447075242200241450ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Jannis Leidel , 2014 msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:17+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: German (http://www.transifex.com/projects/p/django-constance/" "language/de/)\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Konstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. " "Bitte den Ausgangswert von '%(name)s' ändern." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Konstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. " "Bitte den Ausgangswert von '%(name)s' ändern." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Die Konfiguration wurde seit Öffnen dieser Seite verändert. Bitte die Seite " "neuladen und die Änderungen erneut vornehmen." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Die Livekonfiguration wurde erfolgreich aktualisiert." #: admin.py:285 msgid "config" msgstr "Konfiguration" #: apps.py:8 msgid "Constance" msgstr "Konstanze" #: backends/database/models.py:19 msgid "constance" msgstr "Konstanze" #: backends/database/models.py:20 msgid "constances" msgstr "Konstanzes" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Sichern" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Start" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Name" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Voreinstellung" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Wert" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Ist modifiziert" #~ msgid "Constance config" #~ msgstr "Constance Konfiguration" django-constance-3.1.0/constance/locale/en/000077500000000000000000000000001447075242200205555ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/en/LC_MESSAGES/000077500000000000000000000000001447075242200223425ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/en/LC_MESSAGES/django.mo000066400000000000000000000005211447075242200241370ustar00rootroot00000000000000$,89Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit django-constance-3.1.0/constance/locale/en/LC_MESSAGES/django.po000066400000000000000000000041161447075242200241460ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 21:00+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "" #: admin.py:267 msgid "Failed to update live settings." msgstr "" #: admin.py:285 msgid "config" msgstr "" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "" #: backends/database/models.py:20 msgid "constances" msgstr "" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "" django-constance-3.1.0/constance/locale/es/000077500000000000000000000000001447075242200205625ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/es/LC_MESSAGES/000077500000000000000000000000001447075242200223475ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/es/LC_MESSAGES/django.mo000066400000000000000000000027021447075242200241470ustar00rootroot00000000000000H IhS #bfl s } 7vA9 o(      ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Igor Támara Language-Team: Spanish (http://www.transifex.com/projects/p/django-constance/language/de/ Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); ConstanceConstance no soporta valores de configuración de los tipos %(config_type)s. Por favor arregle el valor de '%(name)s'.PredeterminadoInicioEstá modificadoLas configuraciones en vivo se actualizaron exitosamente.NombreGuardarLa configuración ha sido modificada por alguien más. Por favor recargue el formulario y reenvíe sus cambios.Valorconfiguraciónconstanceconstancesdjango-constance-3.1.0/constance/locale/es/LC_MESSAGES/django.po000066400000000000000000000055371447075242200241630ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Igor Támara , 2015 msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Igor Támara \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/django-constance/" "language/de/\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Constance no soporta valores de configuración de los tipos %(config_type)s. " "Por favor arregle el valor de '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance no soporta valores de configuración de los tipos %(config_type)s. " "Por favor arregle el valor de '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "La configuración ha sido modificada por alguien más. Por favor recargue el " "formulario y reenvíe sus cambios." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Las configuraciones en vivo se actualizaron exitosamente." #: admin.py:285 msgid "config" msgstr "configuración" #: apps.py:8 msgid "Constance" msgstr "Constance" #: backends/database/models.py:19 msgid "constance" msgstr "constance" #: backends/database/models.py:20 msgid "constances" msgstr "constances" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Guardar" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Inicio" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nombre" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Predeterminado" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valor" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Está modificado" #~ msgid "Constance config" #~ msgstr "Configuración de Constance" django-constance-3.1.0/constance/locale/et/000077500000000000000000000000001447075242200205635ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/et/LC_MESSAGES/000077500000000000000000000000001447075242200223505ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/et/LC_MESSAGES/django.mo000066400000000000000000000032651447075242200241550ustar00rootroot00000000000000 h ihso8T #b*0 7 AkL BL)d(     ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); SeadedConstance ei toeta seadete seda tüüpi %(config_type) seadete väärtusi. Palunsisestage korrektne väärtus väljale '%(name)s'.VaikimisiVaikimisi väärtus peab olema vastav seade parameetri tüübile. Palun sisestagekorrekne vaikimisi väärtus väljale '%(name)s'Loe/salvesta andmebaasi põhiseid seadeidMuudetudSeaded edukalt muudetud.NimiSalvestaSeadeid on vahepeal muudetud, palun esitage oma muudatused peale seda kui olete lehe uuesti laadinudVäärtuskonfiguratsioonseadedseadeddjango-constance-3.1.0/constance/locale/et/LC_MESSAGES/django.po000066400000000000000000000050731447075242200241570ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Vaikimisi väärtus peab olema vastav seade parameetri tüübile. Palun " "sisestagekorrekne vaikimisi väärtus väljale '%(name)s'" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance ei toeta seadete seda tüüpi %(config_type) seadete väärtusi. " "Palunsisestage korrektne väärtus väljale '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Seadeid on vahepeal muudetud, palun esitage oma muudatused peale seda kui " "olete lehe uuesti laadinud" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Seaded edukalt muudetud." #: admin.py:285 msgid "config" msgstr "konfiguratsioon" #: apps.py:8 msgid "Constance" msgstr "Seaded" #: backends/database/models.py:19 msgid "constance" msgstr "seaded" #: backends/database/models.py:20 msgid "constances" msgstr "seaded" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "Loe/salvesta andmebaasi põhiseid seadeid" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Salvesta" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nimi" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Vaikimisi" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Väärtus" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Muudetud" django-constance-3.1.0/constance/locale/fa/000077500000000000000000000000001447075242200205415ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/fa/LC_MESSAGES/000077500000000000000000000000001447075242200223265ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/fa/LC_MESSAGES/django.mo000066400000000000000000000044051447075242200241300ustar00rootroot00000000000000<R hwo8( -#9]bsbx  Mbq*;%     CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2020-09-24 17:33+0330 Language: fa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last-Translator: Mehdi Namaki Language-Team: X-Generator: Poedit 2.3 CONSTANCE_CONFIG_FIELDSETS شامل فیلدهای CONSTANCE_CONFIG نیست.تنظیماتتنظیمات مقادیر پیکربندی از نوع %(config_type)s را پشتیبانی نمی کند. لطفاً مقدار '%(name)s' را اصلاح کنید.پیش‌فرضنوع مقدار پیش فرض باید برابر با نوع پارامتر پیکربندی اعلام شده باشد. لطفاً مقدار پیش فرض '%(name)s' را اصلاح کنید.دریافت/تنظیم تنظیمات پیکربندی درون پایگاه داده که توسط تنظیمات بکار برده می شودخانهتغییر یافتهتنظیمات زنده با موفقیت به روز شد.نامبازنشانی به پیش‌فرضذخیرهتنظیمات توسط شخص دیگری تغییر یافته است. لطفاً فرم را بارگیری کنید و تغییرات خود را دوباره ارسال کنید.مقدارپیکربندیتنظیماتتنظیماتdjango-constance-3.1.0/constance/locale/fa/LC_MESSAGES/django.po000066400000000000000000000061341447075242200241340ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2020-09-24 17:33+0330\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: Mehdi Namaki \n" "Language-Team: \n" "X-Generator: Poedit 2.3\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "نوع مقدار پیش فرض باید برابر با نوع پارامتر پیکربندی اعلام شده باشد. لطفاً " "مقدار پیش فرض '%(name)s' را اصلاح کنید." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "تنظیمات مقادیر پیکربندی از نوع %(config_type)s را پشتیبانی نمی کند. لطفاً " "مقدار '%(name)s' را اصلاح کنید." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "تنظیمات توسط شخص دیگری تغییر یافته است. لطفاً فرم را بارگیری کنید و تغییرات " "خود را دوباره ارسال کنید." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "CONSTANCE_CONFIG_FIELDSETS شامل فیلدهای CONSTANCE_CONFIG نیست." #: admin.py:224 msgid "Live settings updated successfully." msgstr "تنظیمات زنده با موفقیت به روز شد." #: admin.py:285 msgid "config" msgstr "پیکربندی" #: apps.py:8 msgid "Constance" msgstr "تنظیمات" #: backends/database/models.py:19 msgid "constance" msgstr "تنظیمات" #: backends/database/models.py:20 msgid "constances" msgstr "تنظیمات" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "دریافت/تنظیم تنظیمات پیکربندی درون پایگاه داده که توسط تنظیمات بکار برده می " "شود" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "ذخیره" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "خانه" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "نام" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "پیش‌فرض" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "مقدار" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "تغییر یافته" #: templates/admin/constance/includes/results_list.html:44 msgid "Reset to default" msgstr "بازنشانی به پیش‌فرض" django-constance-3.1.0/constance/locale/fr/000077500000000000000000000000001447075242200205625ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/fr/LC_MESSAGES/000077500000000000000000000000001447075242200223475ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000035201447075242200241460ustar00rootroot00000000000000x yho8d #b?E L Vla ^f\\ b%o -4 ; E   ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2017-07-02 19:01+0100 PO-Revision-Date: 2017-07-03 12:35+0100 Last-Translator: Bruno Alla Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Language-Team: X-Generator: Poedit 2.0.2 ConstanceConstance ne prend pas en charge les valeurs de configuration du type %(config_type)s. Veuillez corriger la valeur de ‘%(name)s’.DéfautLe type de la valeur par défaut doit être le même que le type déclaré dans la configuration. Veuillez corriger la valeur par défaut de '%(name)s'.Obtenir/définir les paramètres de configuration de base de données gérées par ConstanceIndexEst modifiéParamètres mis à jour avec succès.NomEnregistrerLes paramètres ont été modifiés par quelqu'un d'autre. Veuillez rafraichir le formulaire et soumettre de nouveau vos modifications.Valeurconfigconstanceconstancesdjango-constance-3.1.0/constance/locale/fr/LC_MESSAGES/django.po000066400000000000000000000051051447075242200241520ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-02 19:01+0100\n" "PO-Revision-Date: 2017-07-03 12:35+0100\n" "Last-Translator: Bruno Alla \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language-Team: \n" "X-Generator: Poedit 2.0.2\n" #: admin.py:111 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Le type de la valeur par défaut doit être le même que le type déclaré dans " "la configuration. Veuillez corriger la valeur par défaut de '%(name)s'." #: admin.py:121 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance ne prend pas en charge les valeurs de configuration du type " "%(config_type)s. Veuillez corriger la valeur de ‘%(name)s’." #: admin.py:145 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Les paramètres ont été modifiés par quelqu'un d'autre. Veuillez rafraichir " "le formulaire et soumettre de nouveau vos modifications." #: admin.py:209 msgid "Live settings updated successfully." msgstr "Paramètres mis à jour avec succès." #: admin.py:271 msgid "config" msgstr "config" #: apps.py:8 msgid "Constance" msgstr "Constance" #: backends/database/models.py:19 msgid "constance" msgstr "constance" #: backends/database/models.py:20 msgid "constances" msgstr "constances" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "Obtenir/définir les paramètres de configuration de base de données gérées " "par Constance" #: templates/admin/constance/change_list.html:68 msgid "Save" msgstr "Enregistrer" #: templates/admin/constance/change_list.html:77 msgid "Home" msgstr "Index" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nom" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Défaut" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valeur" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Est modifié" django-constance-3.1.0/constance/locale/it/000077500000000000000000000000001447075242200205675ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/it/LC_MESSAGES/000077500000000000000000000000001447075242200223545ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/it/LC_MESSAGES/django.mo000066400000000000000000000035271447075242200241620ustar00rootroot00000000000000xRy h?oG #bY_ f p{O  ph}tc j;uj'. = J   CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.HomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2018-03-13 15:26+0100 Last-Translator: Paolo Melchiorre Language-Team: Italian (http://www.transifex.com/projects/p/django-constance/language/it/) Language: it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 2.0.4 CONSTANCE_CONFIG_FIELDSETS non contiene campi che esistono in CONSTANCE_CONFIG.ImpostazioniConstance non supporta valori di impostazioni di tipo %(config_type)s. Modifica il valore di '%(name)s'.DefaultIl tipo dei valori di default deve essere uguale al tipo del parametro. Modifica il valore di default di '%(name)s'.InizioModificatoLe impostazioni attive sono state aggiornate correttamente.NomeSalvaLe impostazioni sono state modificate da qualcuno. Ricarica la pagina e invia nuovamente le tue modifiche.Valoreconfigurazioniimpostazioneimpostazionidjango-constance-3.1.0/constance/locale/it/LC_MESSAGES/django.po000066400000000000000000000054101447075242200241560ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2018-03-13 15:26+0100\n" "Last-Translator: Paolo Melchiorre \n" "Language-Team: Italian (http://www.transifex.com/projects/p/django-constance/" "language/it/)\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.4\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Il tipo dei valori di default deve essere uguale al tipo del parametro. " "Modifica il valore di default di '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance non supporta valori di impostazioni di tipo %(config_type)s. " "Modifica il valore di '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Le impostazioni sono state modificate da qualcuno. Ricarica la pagina e " "invia nuovamente le tue modifiche." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" "CONSTANCE_CONFIG_FIELDSETS non contiene campi che esistono in " "CONSTANCE_CONFIG." #: admin.py:224 msgid "Live settings updated successfully." msgstr "Le impostazioni attive sono state aggiornate correttamente." #: admin.py:285 msgid "config" msgstr "configurazioni" #: apps.py:8 msgid "Constance" msgstr "Impostazioni" #: backends/database/models.py:19 msgid "constance" msgstr "impostazione" #: backends/database/models.py:20 msgid "constances" msgstr "impostazioni" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Salva" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Inizio" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nome" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Default" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valore" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Modificato" #~ msgid "Constance config" #~ msgstr "Configurazione Impostazioni" django-constance-3.1.0/constance/locale/pl/000077500000000000000000000000001447075242200205665ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/pl/LC_MESSAGES/000077500000000000000000000000001447075242200223535ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/pl/LC_MESSAGES/django.mo000066400000000000000000000017621447075242200241600ustar00rootroot00000000000000 t"* /#;_di o yl   !     Constance configDefaultHomeIs modifiedLive settings updated successfully.NameSaveValueconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2014-11-27 19:05+0100 PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Polish (http://www.transifex.com/projects/p/django-constance/language/pl/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: pl Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Konfiguracja ConstanceDomyślniePoczątekZmodyfikowanaParametry zostały zaktualizowaneNazwaZapiszWartośćparametrparametrydjango-constance-3.1.0/constance/locale/pl/LC_MESSAGES/django.po000066400000000000000000000045021447075242200241560ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Polish (http://www.transifex.com/projects/p/django-constance/" "language/pl/)\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Parametry zostały zaktualizowane" #: admin.py:285 msgid "config" msgstr "" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "parametr" #: backends/database/models.py:20 msgid "constances" msgstr "parametry" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Zapisz" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Początek" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nazwa" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Domyślnie" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Wartość" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Zmodyfikowana" #~ msgid "Constance config" #~ msgstr "Konfiguracja Constance" django-constance-3.1.0/constance/locale/ru/000077500000000000000000000000001447075242200206015ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/ru/LC_MESSAGES/000077500000000000000000000000001447075242200223665ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000022511447075242200241650ustar00rootroot00000000000000 | !)I N#Z~  z:5,=Pp   DefaultFailed to update live settings.HomeIs modifiedLive settings updated successfully.NameSaveValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Russian (http://www.transifex.com/projects/p/django-constance/language/ru/) Language: ru MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); По умолчаниюНе удалось сохранить настройки.ГлавнаяБыло измененоНастройки успешно сохранены.НазваниеСохранитьТекущее значениенастройкинастройкинастройкиdjango-constance-3.1.0/constance/locale/ru/LC_MESSAGES/django.po000066400000000000000000000051201447075242200241660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 20:59+0500\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Russian (http://www.transifex.com/projects/p/django-constance/" "language/ru/)\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Настройки успешно сохранены." #: admin.py:267 msgid "Failed to update live settings." msgstr "Не удалось сохранить настройки." #: admin.py:285 msgid "config" msgstr "настройки" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "настройки" #: backends/database/models.py:20 msgid "constances" msgstr "настройки" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Сохранить" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Главная" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Название" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "По умолчанию" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Текущее значение" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Было изменено" #~ msgid "Constance config" #~ msgstr "Настройки" django-constance-3.1.0/constance/locale/tr/000077500000000000000000000000001447075242200206005ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/tr/LC_MESSAGES/000077500000000000000000000000001447075242200223655ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/tr/LC_MESSAGES/django.mo000066400000000000000000000035241447075242200241700ustar00rootroot00000000000000,Oh BOoW #bz  HVw<  [e(uu3: ? I   CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.HomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Ozcan Yarimdunya Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); CONSTANCE_CONFIG içinde mevcut olan alan(lar) için CONSTANCE_CONFIG_FIELDSETS eksik.Constance %(config_type)s tipinin yapılandırma değerlerini desteklemiyor. Lütfen '%(name)s' in değerini düzeltin.Geçerli dosyaVarsayılanVarsayılan değer tipi, tanımlanan ayarlar parametresi tipi ile aynı olmalıdır. Lütfen '%(name)s' in varsayılan değerini düzeltin.Ana SayfaDeğiştiril miCanlı ayarlar başarıyla güncellendi.İsimVarsayılana dönKaydetAyarlar başkası tarafından değiştirildi. Lütfen formu tekrar yükleyin ve değişikliklerinizi tekrar kaydedin.Değerayarconstanceconstancesdjango-constance-3.1.0/constance/locale/tr/LC_MESSAGES/django.po000066400000000000000000000060031447075242200241660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-09 19:14+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Ozcan Yarimdunya \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: constance/admin.py:116 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Varsayılan değer tipi, tanımlanan ayarlar parametresi tipi ile aynı olmalıdır. Lütfen " "'%(name)s' in varsayılan değerini düzeltin." #: constance/admin.py:126 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance %(config_type)s tipinin yapılandırma değerlerini desteklemiyor. Lütfen " "'%(name)s' in değerini düzeltin." #: constance/admin.py:160 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Ayarlar başkası tarafından değiştirildi. Lütfen formu tekrar yükleyin ve " "değişikliklerinizi tekrar kaydedin." #: constance/admin.py:172 constance/checks.py:19 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "" "CONSTANCE_CONFIG içinde mevcut olan alan(lar) için " "CONSTANCE_CONFIG_FIELDSETS eksik." #: constance/admin.py:240 msgid "Live settings updated successfully." msgstr "Canlı ayarlar başarıyla güncellendi." #: constance/admin.py:305 msgid "config" msgstr "ayar" #: constance/backends/database/models.py:19 msgid "constance" msgstr "constance" #: constance/backends/database/models.py:20 msgid "constances" msgstr "constances" #: constance/management/commands/constance.py:32 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: constance/templates/admin/constance/change_list.html:60 msgid "Save" msgstr "Kaydet" #: constance/templates/admin/constance/change_list.html:69 msgid "Home" msgstr "Anasayfa" #: constance/templates/admin/constance/includes/results_list.html:6 msgid "Name" msgstr "İsim" #: constance/templates/admin/constance/includes/results_list.html:7 msgid "Default" msgstr "Varsayılan" #: constance/templates/admin/constance/includes/results_list.html:8 msgid "Value" msgstr "Değer" #: constance/templates/admin/constance/includes/results_list.html:9 msgid "Is modified" msgstr "Değiştiril mi" #: constance/templates/admin/constance/includes/results_list.html:22 msgid "Current file" msgstr "Geçerli dosya" #: constance/templates/admin/constance/includes/results_list.html:39 msgid "Reset to default" msgstr "Varsayılana dön" django-constance-3.1.0/constance/locale/uk/000077500000000000000000000000001447075242200205725ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/uk/LC_MESSAGES/000077500000000000000000000000001447075242200223575ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000055761447075242200241730ustar00rootroot00000000000000\Oh! o8/h m#yb0!R Y cni3Mg>tO ^ ;v D    E 3 L e     CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Failed to update live settings.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueYou don't have permission to change these valuesconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Vasyl Dizhak Language-Team: (http://www.transifex.com/projects/p/django-constance/language/uk/) Language: uk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Одне чи кілька полів з CONSTANCE_CONFIG відсутні в CONSTANCE_CONFIG_FIELDSETS.Constance не підтрумує значення наступного типу %(config_type)s. Будь ласка, змініть тип для значення '%(name)s'Поточний файлЗа замовчуваннямТип значення за замовчуванням повинен співпадати із вказаним типом параметра конфігурації. Будь ласка, виправте значення за замовчуванням для '%(name)s'.Не вдалося зберегти налаштування.Отримати/встановити налашування в базі даних, якими керує ConstanceГоловнаБуло зміненоНалаштування успішно збережені.НазваСкинути до значення за замовчуваннямЗберегтиНалаштування було змінено кимось іншим. Буд ласка, перезавантажте форму та повторно збережіть зміни.Поточне значенняУ вас немає прав для зміни цих значеньналаштуванняналаштуванняналаштуванняdjango-constance-3.1.0/constance/locale/uk/LC_MESSAGES/django.po000066400000000000000000000076111447075242200241660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 16:00+0000\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Vasyl Dizhak \n" "Language-Team: (http://www.transifex.com/projects/p/django-constance/" "language/uk/)\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:109 msgid "You don't have permission to change these values" msgstr "У вас немає прав для зміни цих значень" #: admin.py:117 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Тип значення за замовчуванням повинен співпадати із вказаним типом параметра " "конфігурації. Будь ласка, виправте значення за замовчуванням для '%(name)s'." #: admin.py:127 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance не підтрумує значення наступного типу %(config_type)s. Будь ласка, " "змініть тип для значення '%(name)s'" #: admin.py:166 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Налаштування було змінено кимось іншим. Буд ласка, перезавантажте форму та " "повторно збережіть зміни." #: admin.py:178 checks.py:19 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "" "Одне чи кілька полів з CONSTANCE_CONFIG відсутні в " "CONSTANCE_CONFIG_FIELDSETS." #: admin.py:250 msgid "Live settings updated successfully." msgstr "Налаштування успішно збережені." #: admin.py:267 msgid "Failed to update live settings." msgstr "Не вдалося зберегти налаштування." #: admin.py:326 msgid "config" msgstr "налаштування" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "налаштування" #: backends/database/models.py:20 msgid "constances" msgstr "налаштування" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "Отримати/встановити налашування в базі даних, якими керує Constance" #: templates/admin/constance/change_list.html:61 msgid "Save" msgstr "Зберегти" #: templates/admin/constance/change_list.html:70 msgid "Home" msgstr "Головна" #: templates/admin/constance/includes/results_list.html:6 msgid "Name" msgstr "Назва" #: templates/admin/constance/includes/results_list.html:7 msgid "Default" msgstr "За замовчуванням" #: templates/admin/constance/includes/results_list.html:8 msgid "Value" msgstr "Поточне значення" #: templates/admin/constance/includes/results_list.html:9 msgid "Is modified" msgstr "Було змінено" #: templates/admin/constance/includes/results_list.html:26 msgid "Current file" msgstr "Поточний файл" #: templates/admin/constance/includes/results_list.html:44 msgid "Reset to default" msgstr "Скинути до значення за замовчуванням" #~ msgid "Constance config" #~ msgstr "Настройки" django-constance-3.1.0/constance/locale/zh_CN/000077500000000000000000000000001447075242200211545ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/zh_CN/LC_MESSAGES/000077500000000000000000000000001447075242200227415ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/zh_CN/LC_MESSAGES/django.mo000066400000000000000000000025301447075242200245400ustar00rootroot00000000000000H IhS #bfl s }2SB N-18H    ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2015-03-15 18:40+0000 Last-Translator: Yifu Yu Language-Team: Chinese (China) (http://www.transifex.com/jezdez/django-constance/language/zh_CN/) Language: zh_CN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; Constance模块Constance不支持保存类型为%(config_type)s的值,请修正%(name)s的值。默认值首页是否修改过成功更新实时配置名称保存设置已经被他人修改过,请刷新页面并重新提交您的更改。值配置Constance模块Constance模块django-constance-3.1.0/constance/locale/zh_CN/LC_MESSAGES/django.po000066400000000000000000000052611447075242200245470ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Yifu Yu , 2015 msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2015-03-15 18:40+0000\n" "Last-Translator: Yifu Yu \n" "Language-Team: Chinese (China) (http://www.transifex.com/jezdez/django-" "constance/language/zh_CN/)\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "Constance不支持保存类型为%(config_type)s的值,请修正%(name)s的值。" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "Constance不支持保存类型为%(config_type)s的值,请修正%(name)s的值。" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "设置已经被他人修改过,请刷新页面并重新提交您的更改。" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "成功更新实时配置" #: admin.py:285 msgid "config" msgstr "配置" #: apps.py:8 msgid "Constance" msgstr "Constance模块" #: backends/database/models.py:19 msgid "constance" msgstr "Constance模块" #: backends/database/models.py:20 msgid "constances" msgstr "Constance模块" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "保存" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "首页" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "名称" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "默认值" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "值" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "是否修改过" #~ msgid "Constance config" #~ msgstr "Constance 配置页面" django-constance-3.1.0/constance/locale/zh_Hans/000077500000000000000000000000001447075242200215455ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001447075242200233325ustar00rootroot00000000000000django-constance-3.1.0/constance/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000035651447075242200251420ustar00rootroot00000000000000<Oh jwo8( -#9]bsbx  dKbS  P9jN DNUe   CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-19 11:31+0800 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; CONSTANCE_CONFIG_FIELDSETS中缺少在CONSTANCE_CONFIG中声明的字段。Constance不支持保存类型为%(config_type)s的值,请修正%(name)s的值。当前文件默认值默认值的类型必须与参数声明类型相同,请修正%(name)s的值。获取或设置由Constance模块处理的数据库配置首页是否修改过实时配置更新成功名称重置至默认值保存设置已经被他人修改过,请刷新页面并重新提交您的更改。当前值配置Constance模块Constance模块django-constance-3.1.0/constance/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000053411447075242200251370ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Yifu Yu , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-19 11:31+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: YinKH <614457662@qq.com>\n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: .\admin.py:115 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "默认值的类型必须与参数声明类型相同,请修正%(name)s的值。" #: .\admin.py:125 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "Constance不支持保存类型为%(config_type)s的值,请修正%(name)s的值。" #: .\admin.py:157 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "设置已经被他人修改过,请刷新页面并重新提交您的更改。" #: .\admin.py:173 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "CONSTANCE_CONFIG_FIELDSETS中缺少在CONSTANCE_CONFIG中声明的字段。" #: .\admin.py:240 msgid "Live settings updated successfully." msgstr "实时配置更新成功" #: .\admin.py:301 msgid "config" msgstr "配置" #: .\backends\database\models.py:19 msgid "constance" msgstr "Constance模块" #: .\backends\database\models.py:20 msgid "constances" msgstr "Constance模块" #: .\management\commands\constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "获取或设置由Constance模块处理的数据库配置" #: .\templates\admin\constance\change_list.html:60 msgid "Save" msgstr "保存" #: .\templates\admin\constance\change_list.html:69 msgid "Home" msgstr "首页" #: .\templates\admin\constance\includes\results_list.html:5 msgid "Name" msgstr "名称" #: .\templates\admin\constance\includes\results_list.html:6 msgid "Default" msgstr "默认值" #: .\templates\admin\constance\includes\results_list.html:7 msgid "Value" msgstr "当前值" #: .\templates\admin\constance\includes\results_list.html:8 msgid "Is modified" msgstr "是否修改过" #: .\templates\admin\constance\includes\results_list.html:21 msgid "Current file" msgstr "当前文件" #: .\templates\admin\constance\includes\results_list.html:36 msgid "Reset to default" msgstr "重置至默认值" django-constance-3.1.0/constance/management/000077500000000000000000000000001447075242200210305ustar00rootroot00000000000000django-constance-3.1.0/constance/management/__init__.py000066400000000000000000000000001447075242200231270ustar00rootroot00000000000000django-constance-3.1.0/constance/management/commands/000077500000000000000000000000001447075242200226315ustar00rootroot00000000000000django-constance-3.1.0/constance/management/commands/__init__.py000066400000000000000000000000001447075242200247300ustar00rootroot00000000000000django-constance-3.1.0/constance/management/commands/constance.py000066400000000000000000000072351447075242200251670ustar00rootroot00000000000000from django import VERSION from django.conf import settings from django.core.exceptions import ValidationError from django.core.management import BaseCommand, CommandError from django.utils.translation import gettext as _ from ... import config from ...forms import ConstanceForm from ...utils import get_values from ...models import Constance def _set_constance_value(key, value): """ Parses and sets a Constance value from a string :param key: :param value: :return: """ form = ConstanceForm(initial=get_values()) field = form.fields[key] clean_value = field.clean(field.to_python(value)) setattr(config, key, clean_value) class Command(BaseCommand): help = _('Get/Set In-database config settings handled by Constance') def add_arguments(self, parser): subparsers = parser.add_subparsers(dest='command') # API changed in Django>=2.1. cmd argument was removed. parser_list = self._subparsers_add_parser(subparsers, 'list', cmd=self, help='list all Constance keys and their values') parser_get = self._subparsers_add_parser(subparsers, 'get', cmd=self, help='get the value of a Constance key') parser_get.add_argument('key', help='name of the key to get', metavar='KEY') parser_set = self._subparsers_add_parser(subparsers, 'set', cmd=self, help='set the value of a Constance key') parser_set.add_argument('key', help='name of the key to get', metavar='KEY') # use nargs='+' so that we pass a list to MultiValueField (eg SplitDateTimeField) parser_set.add_argument('value', help='value to set', metavar='VALUE', nargs='+') self._subparsers_add_parser( subparsers, 'remove_stale_keys', cmd=self, help='delete all Constance keys and their values if they are not in settings.CONSTANCE_CONFIG (stale keys)', ) def _subparsers_add_parser(self, subparsers, name, **kwargs): # API in Django >= 2.1 changed and removed cmd parameter from add_parser if VERSION >= (2, 1) and 'cmd' in kwargs: kwargs.pop('cmd') return subparsers.add_parser(name, **kwargs) def handle(self, command, key=None, value=None, *args, **options): if command == 'get': try: self.stdout.write("{}".format(getattr(config, key)), ending="\n") except AttributeError as e: raise CommandError(key + " is not defined in settings.CONSTANCE_CONFIG") elif command == 'set': try: if len(value) == 1: # assume that if a single argument was passed, the field doesn't expect a list value = value[0] _set_constance_value(key, value) except KeyError as e: raise CommandError(key + " is not defined in settings.CONSTANCE_CONFIG") except ValidationError as e: raise CommandError(", ".join(e)) elif command == 'list': for k, v in get_values().items(): self.stdout.write("{}\t{}".format(k, v), ending="\n") elif command == 'remove_stale_keys': actual_keys = settings.CONSTANCE_CONFIG.keys() stale_records = Constance.objects.exclude(key__in=actual_keys) if stale_records: self.stdout.write("The following record will be deleted:", ending="\n") else: self.stdout.write("There are no stale records in database.", ending="\n") for stale_record in stale_records: self.stdout.write("{}\t{}".format(stale_record.key, stale_record.value), ending="\n") stale_records.delete() django-constance-3.1.0/constance/migrations/000077500000000000000000000000001447075242200210705ustar00rootroot00000000000000django-constance-3.1.0/constance/migrations/0001_initial.py000066400000000000000000000014661447075242200235420ustar00rootroot00000000000000from django.db import migrations, models import picklefield.fields class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name='Constance', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('key', models.CharField(max_length=255, unique=True)), ('value', picklefield.fields.PickledObjectField(blank=True, editable=False, null=True)), ], options={ 'verbose_name': 'constance', 'verbose_name_plural': 'constances', 'permissions': [('change_config', 'Can change config'), ('view_config', 'Can view config')], }, ), ] django-constance-3.1.0/constance/migrations/0002_migrate_from_old_table.py000066400000000000000000000020211447075242200265560ustar00rootroot00000000000000from django.core.management.color import no_style from django.db import migrations, connection, DatabaseError def _migrate_from_old_table(apps, schema_editor) -> None: """ Copies values from old table. On new installations just ignore error that table does not exist. """ try: with connection.cursor() as cursor: cursor.execute('INSERT INTO constance_constance ( id, key, value ) SELECT id, key, value FROM constance_config', []) cursor.execute('DROP TABLE constance_config', []) except DatabaseError: pass Constance = apps.get_model('constance', 'Constance') sequence_sql = connection.ops.sequence_reset_sql(no_style(), [Constance]) with connection.cursor() as cursor: for sql in sequence_sql: cursor.execute(sql) class Migration(migrations.Migration): dependencies = [('constance', '0001_initial')] atomic = False operations = [ migrations.RunPython(_migrate_from_old_table, reverse_code=lambda x, y: None), ] django-constance-3.1.0/constance/migrations/__init__.py000066400000000000000000000000001447075242200231670ustar00rootroot00000000000000django-constance-3.1.0/constance/models.py000066400000000000000000000015231447075242200205520ustar00rootroot00000000000000from django.db import models from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext_lazy as _ try: from picklefield import PickledObjectField except ImportError: raise ImproperlyConfigured("Couldn't find the the 3rd party app " "django-picklefield which is required for " "the constance database backend.") class Constance(models.Model): key = models.CharField(max_length=255, unique=True) value = PickledObjectField(null=True, blank=True) class Meta: verbose_name = _('constance') verbose_name_plural = _('constances') permissions = [ ('change_config', 'Can change config'), ('view_config', 'Can view config'), ] def __str__(self): return self.key django-constance-3.1.0/constance/settings.py000066400000000000000000000024361447075242200211330ustar00rootroot00000000000000import pickle from django.conf import settings BACKEND = getattr( settings, 'CONSTANCE_BACKEND', 'constance.backends.redisd.RedisBackend' ) CONFIG = getattr(settings, 'CONSTANCE_CONFIG', {}) CONFIG_FIELDSETS = getattr(settings, 'CONSTANCE_CONFIG_FIELDSETS', {}) ADDITIONAL_FIELDS = getattr(settings, 'CONSTANCE_ADDITIONAL_FIELDS', {}) FILE_ROOT = getattr(settings, 'CONSTANCE_FILE_ROOT', '') DATABASE_CACHE_BACKEND = getattr( settings, 'CONSTANCE_DATABASE_CACHE_BACKEND', None ) DATABASE_CACHE_AUTOFILL_TIMEOUT = getattr( settings, 'CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT', 60 * 60 * 24 ) DATABASE_PREFIX = getattr(settings, 'CONSTANCE_DATABASE_PREFIX', '') REDIS_PREFIX = getattr(settings, 'CONSTANCE_REDIS_PREFIX', 'constance:') REDIS_CACHE_TIMEOUT = getattr(settings, 'CONSTANCE_REDIS_CACHE_TIMEOUT', 60) REDIS_CONNECTION_CLASS = getattr( settings, 'CONSTANCE_REDIS_CONNECTION_CLASS', None ) REDIS_CONNECTION = getattr(settings, 'CONSTANCE_REDIS_CONNECTION', {}) REDIS_PICKLE_VERSION = getattr(settings, 'CONSTANCE_REDIS_PICKLE_VERSION', pickle.DEFAULT_PROTOCOL) SUPERUSER_ONLY = getattr(settings, 'CONSTANCE_SUPERUSER_ONLY', True) IGNORE_ADMIN_VERSION_CHECK = getattr( settings, 'CONSTANCE_IGNORE_ADMIN_VERSION_CHECK', False ) django-constance-3.1.0/constance/signals.py000066400000000000000000000001021447075242200207170ustar00rootroot00000000000000import django.dispatch config_updated = django.dispatch.Signal() django-constance-3.1.0/constance/static/000077500000000000000000000000001447075242200202035ustar00rootroot00000000000000django-constance-3.1.0/constance/static/admin/000077500000000000000000000000001447075242200212735ustar00rootroot00000000000000django-constance-3.1.0/constance/static/admin/css/000077500000000000000000000000001447075242200220635ustar00rootroot00000000000000django-constance-3.1.0/constance/static/admin/css/constance.css000066400000000000000000000010711447075242200245510ustar00rootroot00000000000000#result_list .changed { background-color: #ffc; } #changelist table thead th .text { padding: 2px 5px; } #changelist table tbody td:first-child { text-align: left; } #changelist-form ul.errorlist { margin: 0 !important; } .help { font-weight: normal !important; } #results { overflow-x: auto; } .item-anchor { visibility: hidden; margin-left: .1em; } .item-name { white-space: nowrap; } .item-name:hover .item-anchor { visibility: visible; } .sticky-footer { position: sticky; width: 100%; left: 0; bottom: 0; } django-constance-3.1.0/constance/static/admin/js/000077500000000000000000000000001447075242200217075ustar00rootroot00000000000000django-constance-3.1.0/constance/static/admin/js/constance.js000066400000000000000000000022101447075242200242150ustar00rootroot00000000000000(function($) { 'use strict'; $(function() { $('#content-main').on('click', '.reset-link', function(e) { e.preventDefault(); var field_selector = this.dataset.fieldId.replace(/ /g, "\\ ") var field = $('#' + field_selector); var fieldType = this.dataset.fieldType; if (fieldType === 'checkbox') { field.prop('checked', this.dataset.default === 'true'); } else if (fieldType === 'date') { var defaultDate = new Date(this.dataset.default * 1000); $('#' + this.dataset.fieldId).val(defaultDate.strftime(get_format('DATE_INPUT_FORMATS')[0])); } else if (fieldType === 'datetime') { var defaultDate = new Date(this.dataset.default * 1000); $('#' + this.dataset.fieldId + '_0').val(defaultDate.strftime(get_format('DATE_INPUT_FORMATS')[0])); $('#' + this.dataset.fieldId + '_1').val(defaultDate.strftime(get_format('TIME_INPUT_FORMATS')[0])); } else { field.val(this.dataset.default); } }); }); })(django.jQuery); django-constance-3.1.0/constance/templates/000077500000000000000000000000001447075242200207125ustar00rootroot00000000000000django-constance-3.1.0/constance/templates/admin/000077500000000000000000000000001447075242200220025ustar00rootroot00000000000000django-constance-3.1.0/constance/templates/admin/constance/000077500000000000000000000000001447075242200237575ustar00rootroot00000000000000django-constance-3.1.0/constance/templates/admin/constance/change_list.html000066400000000000000000000053271447075242200271340ustar00rootroot00000000000000{% extends "admin/base_site.html" %} {% load admin_list static i18n %} {% block extrastyle %} {{ block.super }} {{ media.css }} {% endblock %} {% block extrahead %} {% url 'admin:jsi18n' as jsi18nurl %} {{ block.super }} {{ media.js }} {% endblock %} {% block bodyclass %}{{ block.super }} change-list{% endblock %} {% block content %}
{% csrf_token %} {% if form.non_field_errors %}
    {% for error in form.non_field_errors %}
  • {{ error }}
  • {% endfor %}
{% endif %} {% if form.errors %}
    {% endif %} {% for field in form.hidden_fields %} {% for error in field.errors %}
  • {{ error }}
  • {% endfor %} {{ field }} {% endfor %} {% if form.errors %}
{% endif %} {% if fieldsets %} {% for fieldset in fieldsets %}

{{ fieldset.title }}

{% with config_values=fieldset.config_values %} {% include "admin/constance/includes/results_list.html" %} {% endwith %}
{% endfor %} {% else %} {% include "admin/constance/includes/results_list.html" %} {% endif %}
{% endblock %} {% block breadcrumbs %} {% endblock %} django-constance-3.1.0/constance/templates/admin/constance/includes/000077500000000000000000000000001447075242200255655ustar00rootroot00000000000000django-constance-3.1.0/constance/templates/admin/constance/includes/results_list.html000066400000000000000000000051431447075242200312120ustar00rootroot00000000000000{% load admin_list static i18n %}
{% for item in config_values %} {% endfor %}
{% trans "Name" %}
{% trans "Default" %}
{% trans "Value" %}
{% trans "Is modified" %}
{{ item.name }}
{{ item.help_text|linebreaksbr }}
{{ item.default|linebreaks }} {{ item.form_field.errors }} {% if item.is_file %}{% trans "Current file" %}: {{ item.value }}{% endif %} {{ item.form_field }} {% if not item.is_file %}
{% trans "Reset to default" %} {% endif %}
{% if item.modified %} {{ item.modified }} {% else %} {{ item.modified }} {% endif %}
django-constance-3.1.0/constance/test/000077500000000000000000000000001447075242200176735ustar00rootroot00000000000000django-constance-3.1.0/constance/test/__init__.py000066400000000000000000000000721447075242200220030ustar00rootroot00000000000000from .unittest import override_config # pragma: no cover django-constance-3.1.0/constance/test/pytest.py000066400000000000000000000040011447075242200215700ustar00rootroot00000000000000""" Pytest constance override config plugin. Inspired by https://github.com/pytest-dev/pytest-django/. """ import pytest from contextlib import ContextDecorator from constance import config as constance_config @pytest.hookimpl(trylast=True) def pytest_configure(config): # pragma: no cover """ Register override_config marker. """ config.addinivalue_line( "markers", ( "override_config(**kwargs): " "mark test to override django-constance config" ) ) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): # pragma: no cover """ Validate constance override marker params. Run test with overrided config. """ marker = item.get_closest_marker("override_config") if marker is not None: if marker.args: pytest.fail( "Constance override can not not accept positional args" ) with override_config(**marker.kwargs): yield else: yield class override_config(ContextDecorator): """ Override config while running test function. Act as context manager and decorator. """ def enable(self): """ Store original config values and set overridden values. """ for key, value in self._to_override.items(): self._original_values[key] = getattr(constance_config, key) setattr(constance_config, key, value) def disable(self): """ Set original values to the config. """ for key, value in self._original_values.items(): setattr(constance_config, key, value) def __init__(self, **kwargs): self._to_override = kwargs.copy() self._original_values = {} def __enter__(self): self.enable() def __exit__(self, exc_type, exc_val, exc_tb): self.disable() @pytest.fixture(name="override_config") def _override_config(): """ Make override_config available as a function fixture. """ return override_config django-constance-3.1.0/constance/test/unittest.py000066400000000000000000000047451447075242200221360ustar00rootroot00000000000000from functools import wraps from django.test import SimpleTestCase from django.test.utils import override_settings from .. import config __all__ = ('override_config',) class override_config(override_settings): """ Decorator to modify constance setting for TestCase. Based on django.test.utils.override_settings. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.original_values = {} def __call__(self, test_func): """ Modify the decorated function to override config values. """ if isinstance(test_func, type): if not issubclass(test_func, SimpleTestCase): raise Exception( "Only subclasses of Django SimpleTestCase can be " "decorated with override_config") return self.modify_test_case(test_func) else: @wraps(test_func) def inner(*args, **kwargs): with self: return test_func(*args, **kwargs) return inner def modify_test_case(self, test_case): """ Override the config by modifying TestCase methods. This method follows the Django <= 1.6 method of overriding the _pre_setup and _post_teardown hooks rather than modifying the TestCase itself. """ original_pre_setup = test_case._pre_setup original_post_teardown = test_case._post_teardown def _pre_setup(inner_self): self.enable() original_pre_setup(inner_self) def _post_teardown(inner_self): original_post_teardown(inner_self) self.disable() test_case._pre_setup = _pre_setup test_case._post_teardown = _post_teardown return test_case def enable(self): """ Store original config values and set overridden values. """ # Store the original values to an instance variable for config_key in self.options: self.original_values[config_key] = getattr(config, config_key) # Update config with the overriden values self.unpack_values(self.options) def disable(self): """ Set original values to the config. """ self.unpack_values(self.original_values) @staticmethod def unpack_values(options): """ Unpack values from the given dict to config. """ for name, value in options.items(): setattr(config, name, value) django-constance-3.1.0/constance/utils.py000066400000000000000000000012241447075242200204250ustar00rootroot00000000000000from importlib import import_module from . import LazyConfig, settings config = LazyConfig() def import_module_attr(path): package, module = path.rsplit('.', 1) return getattr(import_module(package), module) def get_values(): """ Get dictionary of values from the backend :return: """ # First load a mapping between config name and default value default_initial = ((name, options[0]) for name, options in settings.CONFIG.items()) # Then update the mapping with actually values from the backend initial = dict(default_initial, **dict(config._backend.mget(settings.CONFIG))) return initialdjango-constance-3.1.0/docs/000077500000000000000000000000001447075242200156675ustar00rootroot00000000000000django-constance-3.1.0/docs/Makefile000066400000000000000000000152231447075242200173320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-constance.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-constance.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/django-constance" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-constance" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." django-constance-3.1.0/docs/backends.rst000066400000000000000000000140071447075242200201750ustar00rootroot00000000000000.. _backends: .. highlight:: python Backends ======== Constance ships with a bunch of backends that are used to store the configuration values. By default it uses the Redis backend. To override the default please set the :setting:`CONSTANCE_BACKEND` setting to the appropriate dotted path. Redis ----- The configuration values are stored in a redis store and retrieved using the `redis-py`_ library. Please install it like this:: pip install django-constance[redis] Configuration is simple and defaults to the following value, you don't have to add it to your project settings:: CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend' Default redis backend retrieves values every time. There is another redis backend with local cache. `CachingRedisBackend` stores the value from a redis to memory at first access and checks a value ttl at next. Configuration installation is simple:: CONSTANCE_BACKEND = 'constance.backends.redisd.CachingRedisBackend' # optionally set a value ttl CONSTANCE_REDIS_CACHE_TIMEOUT = 60 .. _`redis-py`: https://pypi.python.org/pypi/redis Settings ^^^^^^^^ There are a couple of options: ``CONSTANCE_REDIS_CONNECTION`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A dictionary of parameters to pass to the to Redis client, e.g.:: CONSTANCE_REDIS_CONNECTION = { 'host': 'localhost', 'port': 6379, 'db': 0, } Alternatively you can use a URL to do the same:: CONSTANCE_REDIS_CONNECTION = 'redis://username:password@localhost:6379/0' ``CONSTANCE_REDIS_CONNECTION_CLASS`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An (optional) dotted import path to a connection to use, e.g.:: CONSTANCE_REDIS_CONNECTION_CLASS = 'myproject.myapp.mockup.Connection' If you are using `django-redis `_, feel free to use the ``CONSTANCE_REDIS_CONNECTION_CLASS`` setting to define a callable that returns a redis connection, e.g.:: CONSTANCE_REDIS_CONNECTION_CLASS = 'django_redis.get_redis_connection' ``CONSTANCE_REDIS_PREFIX`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ The (optional) prefix to be used for the key when storing in the Redis database. Defaults to ``'constance:'``. E.g.:: CONSTANCE_REDIS_PREFIX = 'constance:myproject:' ``CONSTANCE_REDIS_PICKLE_VERSION`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The (optional) protocol version of pickle you want to use to serialize your python objects when storing in the Redis database. Defaults to ``pickle.DEFAULT_PROTOCOL``. E.g.:: CONSTANCE_REDIS_PICKLE_VERSION = pickle.DEFAULT_PROTOCOL You might want to pin this value to a specific protocol number, since ``pickle.DEFAULT_PROTOCOL`` means different things between versions of Python. ``CONSTANCE_REDIS_CACHE_TIMEOUT`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The (optional) ttl of values in seconds used by `CachingRedisBackend` for storing in a local cache. Defaults to `60` seconds. Database -------- Database backend stores configuration values in a standard Django model. It requires the package `django-picklefield`_ for storing those values. You must set the ``CONSTANCE_BACKEND`` Django setting to:: CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' Please make sure to apply the database migrations:: python manage.py migrate .. note:: If you're upgrading Constance to 1.0 and use Django 1.7 or higher please make sure to let the migration system know that you've already created the tables for the database backend. You can do that using the ``--fake`` option of the migrate command:: python manage.py migrate database --fake Just like the Redis backend you can set an optional prefix that is used during database interactions (it defaults to an empty string, ``''``). To use something else do this:: CONSTANCE_DATABASE_PREFIX = 'constance:myproject:' Caching ^^^^^^^ The database backend has the ability to automatically cache the config values and clear them when saving. Assuming you have a :setting:`CACHES` setting set you only need to set the the :setting:`CONSTANCE_DATABASE_CACHE_BACKEND` setting to the name of the configured cache backend to enable this feature, e.g. "default":: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CONSTANCE_DATABASE_CACHE_BACKEND = 'default' .. warning:: The cache feature won't work with a cache backend that is incompatible with cross-process caching like the local memory cache backend included in Django because correct cache invalidation can't be guaranteed. If you try this, Constance will throw an error and refuse to let your application start. You can work around this by subclassing ``constance.backends.database.DatabaseBackend`` and overriding `__init__` to remove the check. You'll want to consult the source code for that function to see exactly how. We're deliberately being vague about this, because it's dangerous; the behavior is undefined, and could even cause your app to crash. Nevertheless, there are some limited circumstances in which this could be useful, but please think carefully before going down this path. .. note:: By default Constance will autofill the cache on startup and after saving any of the config values. If you want to disable the cache simply set the :setting:`CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT` setting to ``None``. .. _django-picklefield: https://pypi.org/project/django-picklefield/ Memory ------ The configuration values are stored in a memory and do not persist between process restarts. In order to use this backend you must set the ``CONSTANCE_BACKEND`` Django setting to:: CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend' The main purpose of this one is to be used mostly for testing/developing means, so make sure you intentionally use it on production environments. django-constance-3.1.0/docs/changes.rst000066400000000000000000000245141447075242200200370ustar00rootroot00000000000000Changelog --------- v3.1.0 (2023/08/21) ~~~~~~~~~~~~~~~~~~~ * Add support for using a subdirectory of `MEDIA_ROOT` for file fields * Remove pypy from tox tests v3.0.0 (2023/07/27) ~~~~~~~~~~~~~~~~~~~ * Refactor database backend Backward incompatible changes: remove ``'constance.backends.database'`` from ``INSTALLED_APPS`` * Dropped support for python < 3.7 and django < 3.2 * Example app now supports django 4.1 * Add support for django 4.2 * Forward the request when saving the admin changelist form v2.9.1 (2022/08/11) ~~~~~~~~~~~~~~~~~~ * Add support for gettext in fieldset headers * Add support for Django 4.1 * Fix text format for MultiValueField usage v2.9.0 (2022/03/11) ~~~~~~~~~~~~~~~~~~~ * Added arabic translation * Add concrete_model class attribute to fake admin model * Added tests for django 3.2 * Fix do not detect datetime fields as date type * Added support for python 3.10 * Fixes for Ukrainian locale * Added documentation for constance_dbs config * Add caching redis backend * Serialize according to widget * Add default_auto_field to database backend v2.8.0 (2020/11/19) ~~~~~~~~~~~~~~~~~~~ * Prevent reset to default for file field * Fields_list can be a dictionary, when a fieldset is defined as collapsible * Create and add fa language translations files * Respect other classes added by admin templates * Removed deprecated url() * Use gettext_lazy instead of ugettext_lazy * Updated python and django version support v2.7.0 (2020/06/22) ~~~~~~~~~~~~~~~~~~~ * Deleted south migrations * Improve grammar of documentation index file * Simplify documentation installation section * Fix IntegrityError after 2.5.0 release (Allow concurrent calls to `DatabaseBackend.set()` method) * Make groups of fieldsets collapsable * Allow override_config for pytest * Put back wheel generation in travis * Fix wrong "is modified" in admin for multi line strings * Switch md5 to sha256 * Fix Attempts to change config values fail silently and appear to succeed when user does not have change permissions * Make constance app verbose name translatable * Update example project for Django>2 * Add anchors in admin for constance settings * Added a sticky footer in django constance admin * Add memory backend * Added Ukrainian locale * Added lazy checks for pytest v2.6.0 (2020/01/29) ~~~~~~~~~~~~~~~~~~~ * Drop support py<3.5 django<2.2 * Set pickle protocol version for the Redis backend * Add a command to delete stale records v2.5.0 (2019/12/23) ~~~~~~~~~~~~~~~~~~~ * Made results table responsive for Django 2 admin * Add a Django system check that CONFIG_FIELDSETS accounts for all of CONFIG * Rewrite set() method of database backend to reduce number of queries * Fixed "can't compare offset-naive and offset-aware datetimes" when USE_TZ = True * Fixed compatibility issue with Django 3.0 due to django.utils.six * Add Turkish language v2.4.0 (2019/03/16) ~~~~~~~~~~~~~~~~~~~ * Show not existing fields in field_list * Drop Django<1.11 and 2.0, fix tests vs Django 2.2b * Fixed "Reset to default" button with constants whose name contains a space * Use default_storage to save file * Allow null & blank for PickleField * Removed Python 3.4 since is not longer supported v2.3.1 (2018/09/20) ~~~~~~~~~~~~~~~~~~~ * Fixes javascript typo. v2.3.0 (2018/09/13) ~~~~~~~~~~~~~~~~~~~ * Added zh_Hans translation. * Fixed TestAdmin.test_linebreaks() due to linebreaksbr() behavior change on Django 2.1 * Improved chinese translation * Fix bug of can't change permission chang_config's name * Improve consistency of reset value handling for `date` * Drop support for Python 3.3 * Added official Django 2.0 support. * Added support for Django 2.1 v2.2.0 (2018/03/23) ~~~~~~~~~~~~~~~~~~~ * Fix ConstanceForm validation. * `CONSTANCE_DBS` setting for directing constance permissions/content_type settings to certain DBs only. * Added config labels. * Updated italian translations. * Fix `CONSTANCE_CONFIG_FIELDSETS` mismatch issue. v2.1.0 (2018/02/07) ~~~~~~~~~~~~~~~~~~~ * Move inline JavaScript to constance.js. * Remove translation from the app name. * Added file uploads. * Update information on template context processors. * Allow running set while database is not created. * Moved inline css/javascripts out to their own files. * Add French translations. * Add testing for all supported Python and Django versions. * Preserve sorting from fieldset config. * Added datetime.timedelta support. * Added Estonian translations. * Account for server timezone for Date object. v2.0.0 (2017/02/17) ~~~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Added the old value to the config_updated signal. * Added a `get_changelist_form` hook in the ModelAdmin. * Fix create_perm in apps.py to use database alias given by the post_migrate signal. * Added tests for django 1.11. * Fix Reset to default to work with boolean/checkboxes. * Fix handling of MultiValueField's (eg SplitDateTimeField) on the command line. v1.3.4 (2016/12/23) ~~~~~~~~~~~~~~~~~~~ * Fix config ordering issue * Added localize to check modified flag * Allow to rename Constance in Admin * Preserve line breaks in default value * Added functionality from django-constance-cli * Added "Reset to default" feature v1.3.3 (2016/09/17) ~~~~~~~~~~~~~~~~~~~ * Revert broken release v1.3.2 (2016/09/17) ~~~~~~~~~~~~~~~~~~~ * Fixes a bug where the signal was sent for fields without changes v1.3.1 (2016/09/15) ~~~~~~~~~~~~~~~~~~~ * Improved the signal path to avoid import errors * Improved the admin layout when using fieldsets v1.3 (2016/09/14) ~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Dropped support for Django < 1.8). * Added ordering constance fields using OrderedDict * Added a signal when updating constance fields v1.2.1 (2016/09/1) ~~~~~~~~~~~~~~~~~~ * Added some fixes to small bugs * Fix cache when key changes * Upgrade django_redis connection string * Autofill cache key if key is missing * Added support for fieldsets v1.2 (2016/05/14) ~~~~~~~~~~~~~~~~~ * Custom Fields were added as a new feature * Added documentation on how to use Custom settings form * Introduced ``CONSTANCE_IGNORE_ADMIN_VERSION_CHECK`` * Improved documentation for ``CONSTANCE_ADDITIONAL_FIELDS`` v1.1.2 (2016/02/08) ~~~~~~~~~~~~~~~~~~~ * Moved to Jazzband organization (https://github.com/jazzband/django-constance) * Added Custom Fields * Added Django 1.9 support to tests * Fixes icons for Django 1.9 admin v1.1.1 (2015/10/01) ~~~~~~~~~~~~~~~~~~~ * Fixed a regression in the 1.1 release that prevented the rendering of the admin view with constance values when using the context processor at the same time. v1.1 (2015/09/24) ~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Dropped support for Python 2.6 The supported versions are 2.7, 3.3 (on Django < 1.9) and 3.4. * **BACKWARD INCOMPATIBLE** Dropped support for Django 1.4, 1.5 and 1.6 The supported versions are 1.7, 1.8 and the upcoming 1.9 release * Added compatibility to Django 1.8 and 1.9. * Added Spanish and Chinese (``zh_CN``) translations. * Added :class:`override_config` decorator/context manager for easy :doc:`testing `. * Added the ability to use linebreaks in config value help texts. * Various testing fixes. v1.0.1 (2015/01/07) ~~~~~~~~~~~~~~~~~~~ * Fixed issue with import time side effect on Django >= 1.7. v1.0 (2014/12/04) ~~~~~~~~~~~~~~~~~ * Added docs and set up Read The Docs project: https://django-constance.readthedocs.io/ * Set up Transifex project for easier translations: https://www.transifex.com/projects/p/django-constance * Added autofill feature for the database backend cache which is enabled by default. * Added Django>=1.7 migrations and moved South migrations to own folder. Please upgrade to South>=1.0 to use the new South migration location. For Django 1.7 users that means running the following to fake the migration:: django-admin.py migrate database --fake * Added consistency check when saving config values in the admin to prevent accidentally overwriting other users' changes. * Fixed issue with South migration that would break on MySQL. * Fix compatibility with Django 1.6 and 1.7 and current master (to be 1.8). * Fixed clearing database cache en masse by applying prefix correctly. * Fixed a few translation related issues. * Switched to tox as test script. * Fixed a few minor cosmetic frontend issues (e.g. padding in admin table header). * Deprecated a few old settings: ============================== =================================== deprecated replacement ============================== =================================== ``CONSTANCE_CONNECTION_CLASS`` ``CONSTANCE_REDIS_CONNECTION_CLASS`` ``CONSTANCE_CONNECTION`` ``CONSTANCE_REDIS_CONNECTION`` ``CONSTANCE_PREFIX`` ``CONSTANCE_REDIS_PREFIX`` ============================== =================================== * The undocumented feature to use an environment variable called ``CONSTANCE_SETTINGS_MODULE`` to define which module to load settings from has been removed. v0.6 (2013/04/12) ~~~~~~~~~~~~~~~~~ * Added Python 3 support. Supported versions: 2.6, 2.7, 3.2 and 3.3. For Python 3.x the use of Django > 1.5.x is required. * Fixed a serious issue with ordering in the admin when using the database backend. Thanks, Bouke Haarsma. * Switch to django-discover-runner as test runner to be able to run on Python 3. * Fixed an issue with refering to static files in the admin interface when using Django < 1.4. v0.5 (2013/03/02) ~~~~~~~~~~~~~~~~~ * Fixed compatibility with Django 1.5's swappable model backends. * Converted the ``key`` field of the database backend to use a ``CharField`` with uniqueness instead of just ``TextField``. For South users we provide a migration for that change. First you have to "fake" the initial migration we've also added to this release:: django-admin.py migrate database --fake 0001 After that you can run the rest of the migrations:: django-admin.py migrate database * Fixed compatibility with Django>1.4's way of refering to static files in the admin. * Added ability to add custom authorization checks via the new ``CONSTANCE_SUPERUSER_ONLY`` setting. * Added Polish translation. Thanks, Janusz Harkot. * Allow ``CONSTANCE_REDIS_CONNECTION`` being an URL instead of a dict. * Added ``CONSTANCE_DATABASE_PREFIX`` setting allow setting a key prefix. * Switched test runner to use django-nose. django-constance-3.1.0/docs/conf.py000066400000000000000000000206331447075242200171720ustar00rootroot00000000000000# django-constance documentation build configuration file, created by # sphinx-quickstart on Tue Nov 25 19:38:51 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tests.settings') # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('extensions')) sys.path.insert(0, os.path.abspath('..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.intersphinx', 'settings'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'django-constance' copyright = '2017, Jazzband' # The short X.Y version. try: from constance import __version__ # The short X.Y version. version = '.'.join(__version__.split('.')[:2]) # The full version, including alpha/beta/rc tags. release = __version__ except ImportError: version = release = 'dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'django-constancedoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'django-constance.tex', 'django-constance Documentation', 'Jazzband', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'django-constance', 'django-constance Documentation', ['Jazzband'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'django-constance', 'django-constance Documentation', 'Jazzband', 'django-constance', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'https://docs.python.org/': None, 'django': ('https://docs.djangoproject.com/en/dev/', 'https://docs.djangoproject.com/en/dev/_objects/'), } django-constance-3.1.0/docs/extensions/000077500000000000000000000000001447075242200200665ustar00rootroot00000000000000django-constance-3.1.0/docs/extensions/__init__.py000066400000000000000000000000001447075242200221650ustar00rootroot00000000000000django-constance-3.1.0/docs/extensions/settings.py000066400000000000000000000002311447075242200222740ustar00rootroot00000000000000def setup(app): app.add_crossref_type( directivename="setting", rolename="setting", indextemplate="pair: %s; setting", ) django-constance-3.1.0/docs/index.rst000066400000000000000000000315051447075242200175340ustar00rootroot00000000000000Constance - Dynamic Django settings =================================== Features -------- * Easily migrate your static settings to dynamic settings. * Edit the dynamic settings in the Django admin interface. .. image:: screenshot2.png Quick Installation ------------------ .. code-block:: pip install "django-constance[redis]" For complete installation instructions, including how to install the database backend, see :ref:`Backends `. Configuration ------------- Modify your ``settings.py``. Add ``'constance'`` to your :setting:`INSTALLED_APPS`, and move each key you want to turn dynamic into the :setting:`CONSTANCE_CONFIG` section, like this: .. code-block:: python INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.staticfiles', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', ... 'constance', ) CONSTANCE_CONFIG = { 'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' 'The Universe, and Everything'), } .. note:: Add constance *before* your project apps. .. note:: If you use admin extensions like `Grapelli `_, ``'constance'`` should be added in :setting:`INSTALLED_APPS` *before* those extensions. Here, ``42`` is the default value for the key ``THE_ANSWER`` if it is not found in the backend. The other member of the tuple is a help text the admin will show. See the :ref:`Backends ` section how to setup the backend and finish the configuration. ``django-constance``'s hashes generated in different instances of the same application may differ, preventing data from being saved. Use :setting:`CONSTANCE_IGNORE_ADMIN_VERSION_CHECK` in order to skip hash verification. .. code-block:: python CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True Signals ------- Each time a value is changed it will trigger a ``config_updated`` signal. .. code-block:: python from constance.signals import config_updated @receiver(config_updated) def constance_updated(sender, key, old_value, new_value, **kwargs): print(sender, key, old_value, new_value) The sender is the ``config`` object, and the ``key`` and ``new_value`` are the changed settings. Custom fields ------------- You can set the field type with the third value in the ``CONSTANCE_CONFIG`` tuple. The value can be one of the supported types or a string matching a key in your :setting:``CONSTANCE_ADDITIONAL_FIELDS`` The supported types are: * ``bool`` * ``int`` * ``float`` * ``Decimal`` * ``str`` * ``datetime`` * ``date`` * ``time`` For example, to force a value to be handled as a string: .. code-block:: python 'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' 'The Universe, and Everything', str), Custom field types are supported using the dictionary :setting:``CONSTANCE_ADDITIONAL_FIELDS``. This is a mapping between a field label and a sequence (list or tuple). The first item in the sequence is the string path of a field class, and the (optional) second item is a dictionary used to configure the field. The ``widget`` and ``widget_kwargs`` keys in the field config dictionary can be used to configure the widget used in admin, the other values will be passed as kwargs to the field's ``__init__()`` Note: Use later evaluated strings instead of direct classes for the field and widget classes: .. code-block:: python CONSTANCE_ADDITIONAL_FIELDS = { 'yes_no_null_select': ['django.forms.fields.ChoiceField', { 'widget': 'django.forms.Select', 'choices': ((None, "-----"), ("yes", "Yes"), ("no", "No")) }], } CONSTANCE_CONFIG = { 'MY_SELECT_KEY': ('yes', 'select yes or no', 'yes_no_null_select'), } If you want to work with images or files you can use this configuration: .. code-block:: python CONSTANCE_ADDITIONAL_FIELDS = { 'image_field': ['django.forms.ImageField', {}] } CONSTANCE_CONFIG = { 'LOGO_IMAGE': ('default.png', 'Company logo', 'image_field'), } When used in a template you probably need to use: .. code-block:: html {% load static %} {% get_media_prefix as MEDIA_URL %} Images and files are uploaded to ``MEDIA_ROOT`` by default. You can specify a subdirectory of ``MEDIA_ROOT`` to use instead by adding the ``CONSTANCE_FILE_ROOT`` setting. E.g.: .. code-block:: python MEDIA_ROOT = os.path.join(BASE_DIR, 'media') CONSTANCE_FILE_ROOT = 'constance' This will result in files being placed in ``media/constance`` within your ``BASE_DIR``. You can use deeper nesting in this setting (e.g. ``constance/images``) but other relative path components (e.g. ``../``) will be rejected. Ordered Fields in Django Admin ------------------------------ To sort the fields, you can use an OrderedDict: .. code-block:: python from collections import OrderedDict CONSTANCE_CONFIG = OrderedDict([ ('SITE_NAME', ('My Title', 'Website title')), ('SITE_DESCRIPTION', ('', 'Website description')), ('THEME', ('light-blue', 'Website theme')), ]) Fieldsets --------- You can define fieldsets to group settings together: .. code-block:: python CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', 'Website title'), 'SITE_DESCRIPTION': ('', 'Website description'), 'THEME': ('light-blue', 'Website theme'), } CONSTANCE_CONFIG_FIELDSETS = { 'General Options': ('SITE_NAME', 'SITE_DESCRIPTION'), 'Theme Options': ('THEME',), } .. note:: CONSTANCE_CONFIG_FIELDSETS must contain all fields from CONSTANCE_CONFIG. .. image:: screenshot3.png Fieldsets collapsing -------------------- To make some fieldsets collapsing you can use new format in CONSTANCE_CONFIG_FIELDSETS. Here's an example: .. code-block:: python CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', 'Website title'), 'SITE_DESCRIPTION': ('', 'Website description'), 'THEME': ('light-blue', 'Website theme'), } CONSTANCE_CONFIG_FIELDSETS = { 'General Options': { 'fields': ('SITE_NAME', 'SITE_DESCRIPTION'), 'collapse': True }, 'Theme Options': ('THEME',), } Field internationalization -------------------------- Field description and fieldset headers can be integrated into Django's internationalization using the ``gettext_lazy`` function. Note that the ``CONSTANCE_CONFIG_FIELDSETS`` must be converted to a tuple instead of dict as it is not possible to have lazy proxy objects as dictionary keys in the settings file. Example: .. code-block:: python from django.utils.translation import gettext_lazy as _ CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', _('Website title')), 'SITE_DESCRIPTION': ('', _('Website description')), 'THEME': ('light-blue', _('Website theme')), } CONSTANCE_CONFIG_FIELDSETS = ( ( _('General Options'), { 'fields': ('SITE_NAME', 'SITE_DESCRIPTION'), 'collapse': True, }, ), (_('Theme Options'), ('THEME',)), ) Usage ----- Constance can be used from your Python code and from your Django templates. Python ^^^^^^ Accessing the config variables is as easy as importing the config object and accessing the variables with attribute lookups:: from constance import config # ... if config.THE_ANSWER == 42: answer_the_question() Django templates ^^^^^^^^^^^^^^^^ To access the config object from your template you can pass the object to the template context: .. code-block:: python from django.shortcuts import render from constance import config def myview(request): return render(request, 'my_template.html', {'config': config}) You can also use the included context processor. Insert ``'constance.context_processors.config'`` at the top of your ``TEMPLATES['OPTIONS']['context_processors']`` list. See the `Django documentation`_ for details. .. _`Django documentation`: https://docs.djangoproject.com/en/1.11/ref/templates/upgrading/#the-templates-settings This will add the config instance to the context of any template rendered with a ``RequestContext``. Then, in your template you can refer to the config values just as any other variable, e.g.: .. code-block:: django

Welcome on {{ config.SITE_NAME }}

{% if config.BETA_LAUNCHED %} Woohoo! Head over here to use the beta. {% else %} Sadly we haven't launched yet, click here to signup for our newletter. {% endif %} Command Line ^^^^^^^^^^^^ Constance settings can be get/set on the command line with the manage command `constance` Available options are: list - output all values in a tab-separated format:: $ ./manage.py constance list THE_ANSWER 42 SITE_NAME My Title get KEY - output a single values:: $ ./manage.py constance get THE_ANSWER 42 set KEY VALUE - set a single value:: $ ./manage.py constance set SITE_NAME "Another Title" If the value contains spaces it should be wrapped in quotes. .. note:: Set values are validated as per in admin, an error will be raised if validation fails: E.g., given this config as per the example app:: CONSTANCE_CONFIG = { ... 'DATE_ESTABLISHED': (date(1972, 11, 30), "the shop's first opening"), } Setting an invalid date will fail as follow:: $ ./manage.py constance set DATE_ESTABLISHED '1999-12-00' CommandError: Enter a valid date. .. note:: If the admin field is a `MultiValueField`, then the separate field values need to be provided as separate arguments. E.g., a datetime using `SplitDateTimeField`:: CONSTANCE_CONFIG = { 'DATETIME_VALUE': (datetime(2010, 8, 23, 11, 29, 24), 'time of the first commit'), } Then this works (and the quotes are optional):: ./manage.py constance set DATETIME_VALUE '2011-09-24' '12:30:25' This doesn't work:: ./manage.py constance set DATETIME_VALUE '2011-09-24 12:30:25' CommandError: Enter a list of values. remove_stale_keys - delete all stale records in database:: $ ./manage.py constance remove_stale_keys Record is considered stale if it exists in database but absent in config Editing ------- Fire up your ``admin`` and you should see a new app called ``Constance`` with ``THE_ANSWER`` in the ``Config`` pseudo model. By default, changing the settings via the admin is only allowed for superusers. To change this, feel free to set the :setting:`CONSTANCE_SUPERUSER_ONLY` setting to ``False`` and give users or user groups access to the ``constance.change_config`` permission. .. figure:: screenshot1.png The virtual application ``Constance`` among your regular applications. Custom settings form -------------------- If you aim at creating a custom settings form this is possible in the following way: You can inherit from ``ConstanceAdmin`` and set the ``form`` property on your custom admin to use your custom form. This allows you to define your own formsets and layouts, similar to defining a custom form on a standard Django ModelAdmin. This way you can fully style your settings form and group settings the way you like. .. code-block:: python from constance.admin import ConstanceAdmin, Config from constance.forms import ConstanceForm class CustomConfigForm(ConstanceForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) #... do stuff to make your settings form nice ... class ConfigAdmin(ConstanceAdmin): change_list_form = CustomConfigForm change_list_template = 'admin/config/settings.html' admin.site.unregister([Config]) admin.site.register([Config], ConfigAdmin) You can also override the ``get_changelist_form`` method which is called in ``changelist_view`` to get the actual form used to change the settings. This allows you to pick a different form according to the user that makes the request. For example: .. code-block:: python class SuperuserForm(ConstanceForm): # Do some stuff here class MyConstanceAdmin(ConstanceAdmin): def get_changelist_form(self, request): if request.user.is_superuser: return SuperuserForm: else: return super().get_changelist_form(request) Note that the default method returns ``self.change_list_form``. More documentation ------------------ .. toctree:: :maxdepth: 2 backends testing changes Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-constance-3.1.0/docs/make.bat000066400000000000000000000151021447075242200172730ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-constance.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-constance.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end django-constance-3.1.0/docs/screenshot1.png000066400000000000000000000641341447075242200206430ustar00rootroot00000000000000PNG  IHDRY\=sRGBbKGD pHYs a @tIME /):}tEXtCommentCreated with GIMPW IDATxy`řV-Y-Y, c !@k؄,$a !@@e7!GB}d˲eْu_3澻~H%K0=駪Z߷ c A.@AAA-AAK@AAA-AAK@AAA-AAK@AAA-AAK@AAAd*@V  F  Z [2OT_~} 0j٢rmկl9CZ )+*/RTsብI?uahKBX}ˬ4-onUmwh~/_?JϚ+zBcwa U0``c,\#rZp'Y@NYx׊xzA0X=|+!E{S:?C%*\_n/}2)KmG?tPwxs|"CDx>nKXz&Kˆޑ=f8]{v,u{lxrR}30˜Y}q0p?ج"q[y{ϚGspW<~R2Y(RkjBo*wݔN4/Ys/G8NVq]1V{`,v|zC(-jd+54{W;E7>S̛V䘜vm?Ew\x3W *uͺ΂H҃\@?y/@zG}-V?E>bnden1ϝ WZ{sض[a\ﶕW9bEP..U;z\{Vf34]-嫏M R|h؁cȱwŘeY"r9E4D cʑF<&15,Сl)ʇץֳ I$s)8h d>t1<_Z>2cڽ~HhL,ϧTq |ZgYE/>E.Iibx?9[ & 8}96s4ƵĸAK.JL iBea_lݜw{Yxt@wHf!";L-C! 1<4 0^ ./&=CZ%C-SPQ¹ B77f?x_diM vNy, %(L$pF٬U?+v7hW&~2t-r/\ar 6(tucC;}wT񳁱 \YEJrZ-( zN){FJ|.˰-u_dl wI(nke\iʍ!;`)$W3L.!˜ V75dJMqDQf8}q4Y@ds^s ,, A_FL](x¸u!G/W~i2#, a) @ƒ2纳\_W͇'q~ ܳڽj{ԿxSvip>y/lnڽvR _[vqFݪ_ݫI.{~ٖ e>`ҡ'4 #5~Yg⿟U> Ly& UڳOyOpI?|#}p7 rñy8ɗNBK;ȣ \8&q'Jgy2 2Ǽp}Nˋ9UӨ^7.gD n_*]WZrͲoftU/>+ AB2Ar߽ %  h  Z  %  h  Z 2Qc {A(AAK@AAA-AAK@AAA-AAK@AAA!J}m#)v_V&S˦\`K`޶~@6&$e,YsL9Ceg,!yRIrB!#Am {3d[[.âaKPG<Ӧ*jef5p3a^Ps?E2ڷoXhzz,O=:&8mjNa9̯'x+t/>Bӿ??Z`2`ʕ20RypD,WӭkNw9CrCj^5k֏cXkWO'𚸌ҥW/_qӧ^{>(l}]'zʄ7ucco|r)* W~"#lŵ[1qZuw|}t-U˂=՟s8)!%mZ57.J݇CoG{O>Χas zhl6~Z~[0M=A.'h-%yu p ė,1rvx7~|U 4Mt{6t#5?ϛsռ8^sɣό6?Y*VvȖO_|QJGg5-9ZMƎOUXx~ni ^x^w~~}5B?u )tS-L]KPW @ϡ^:oH+(lł~ta?|;>H;r4ֽhgZ|F,~ѿ'+G,3_.Fk6h aAo?>\L\~`}m˦Aw>8:gλ ._] nmݿ{Vޘ2vo}oZiؼj=Bփ% =*j;W(Ϩzo(XvɌxw@ؘ}uG;7|jK4ßco{yGc&A%}7ׄή]u6ᑇobSJC|Ԝ:6l.e]]+ל ygeI#7,IdG]vm8#ҕv᷿jO~u3j~kU4Ӿ}kmoy*i߹6 0K|w/0r3^70Gx1>1%=-V''OYV?|j)6f_)_~zij}Sj PP8#jmYWW-Lp7riݡCGNjhhã{5 +:{p! ܙ5 0%}_ɿXj~N*Hr#DZGnLbA8^ޒ0#I "D6J^kcF*2W\X 896~ZNe+^>'Nv}5#M$Ti<Al *p鍅K7O4'4KsΝǸ9k./~lbL% ;k&ZeDU⋊ۺ-=v?6|'|&`UFwjS%t Scu´jNXbi'MǴBƒJxCz zohJp0}ݿY` _=,dL1±~ ,@ge*?cJ.ks6fч#G_X8OJs7vɌt\5iˊgd'{jM@AR@Ǿ/ՙ:13ݨ]Y=~=oU7,/J3BG{ 3GXphm R=ΡȓJK̟oxJmK3mϻ=S}~?I~Q^VjIO;"}8O_M|Y%8y4"%rN1OGg|s/$Td*o{;ڸ` `@?L%(A JP2%NA;GHLz3JPdJ&%P $l+ ʔH$}Lڏ0%(AL>JTIX$v` P%(A JP2$` R|@_VPmi_0>ьՙ$kt tX0T Kbb!PYo#I(P)R,m%(A JP2dr 20( g`e d0i16$EalZP%cIF'm#^olG$Bm4 $ td3R6"!¤Ȉ1 C%(ds MD&%@R Zmj6ə12z=0ʘHPv_|:#+.oxQFT3"tlѣ t)S6a(A JPqJFsO?TUU:C);|٫ þE JPL)I1'RJ#7E4 8,|OZ$*A JPLY$GC:D" a!r\ԝP%SS2GC,h<2xaLZ@ JPLMɤ,Ad; "|Rd{pEvC JPLIɤGSH؎Xikd$%(ATL6)Arjv  %  h  Z  لNX 7ƛ n[. ȗǪWF?o~pla㕩Oν?_[>7ٱzg;ZB ȗ/?ڷBo_F:`}Uqlb:e_q!nYT=,tJbS'OSӯnIЊZVfƞ^-A.8Ajuz:D%I5{FElA5q2~xgsGԳg-W,9?NAd;n/vZ9ОMOQ^Qv<`qmGm}c{Z; .*SRk퉗wXAJkι1C>WJ~P8}6 ]KT\sa7Yp{"*S ݇[[oYZaߟl %dd>=.;^ *A.|Qs0Q8ή Pw[%K~X{?;3`kf,x v>B+('닙5_=`XrSC.\E# ]o4494z~ꩆ``.*p=ek\/t<64n{W_{4?P9+ I9Y :XuYy{8X4p 7czYcU7X~Pܲ2WC _2Tg @`08͘.#|nF {b89wW+e$|a: j%ֽ܃L3+SjvS)vࣖ.[^gȹSbN2onv8eC3e_|,-X48Ej=V'B. 4Z=@ OXh @uNo@J_(tڛ[|}R|2C҇3|>ks˙#`)P5@PZ1&2";た#nh|@ܭn@zwsGݗO ,zZ7i=sÊ s5˺(f_[PTst͏~:'לu"{~Ȟ?fH.6M 5]=ugo=`ڂ @ꋕԤd \&Dg V!8k8syWݡGvt-! g)}v(B#m<?XrS{^۸xcd?^t'캫xY0e+3(MP1b?GBPk`LaIT=yS[II_u x[ÎS#G)>va޴40\8! n҂]jTںֿ7** IDAT֗%' 7wG{G]oɴKg}gND=}t[F΄WJ\ 겲5whW:(5~)ÍqBH0sќoo'Td긄kIUs/mXLL՝˧85ٱ9A#< O{WY@]zCIj~TUU]_3֧"r1nU&-gGrv*"/>r'zkh;^W#2Rw,ox3W}x©L׭OQ rm`! h  Z  q8lS h f{ AG Z  %  h  Z 2~&$ԳYOF0="W>9A_<oonqv }Uqlb:YԞ[6N\#_e{^XuY8i w+ wi%Л`|x'RmՍ=bvv?")y kgW&"QΚ%'(gh썃$]ޭ JTRCt[|fѨ-7Ǹ`z߰8% 7}m.xT+SUi] #+,;tuz o{lO?S+yCEeJj=5Ht97f'.0}z\vJrATέijGF]{tGk(04xp*"8J9\1"B3 g}` 5ndOKuoUXggs,PaMÌ/'_he$|}1s& ޻ri%4430w6TT/3pi髧 K díC*>i[ro }:_M|P@A$ JCF1LPrC=ė{$r@Xeӱ!yԗbV F0|~? Fhr3žBk y4`q~n6Zua]2ͬLfSMإZnAtx>6ʞ!N:d29`b.'$qj#87GA;,V8*-ִ6_T)5pܣmò⹈@pZF+W4!ȕ3 g|3G^S>j`9]T'cLdDF#nh|@ܭn@zwsG q^?1xcMfjQdL`D +R0}ָ/ZyB 7^3G{׶%MoBIr+IEj'1]hܾn(MT2Gou]4C @t`Ջ+}>_([Z3?'pztkGrm{ٿpk5%w=絍:FK~Qͮ%{[vo)&(grlUrm{5p簋$srB 줤uhK5&'+A,$?88^aK;+!r* ޝ]&;ܢKԄH' #1Hw@EK/h1,Q?Tݹ|ӮXy>>Go{DХg=4&Yѩ*++_JA. SVk~{$Wk 29_{*/'y6U7 S)u֎W9sW?@'tݺ p1Z %1Uh %  h  Z r.5lٰAA.{ƾU ABA-AAK@AAs1*&Aˌ? ծHCl0'%7+3 (`Lz0_%(A Jd‰##0uB@""Tڈ%(’ G  F!>#I(# %(A JP2E%"UDcXh(E JPL1, NKѦJ@E}㢃3% KI@x= X4Βp%1P 2`K4JP\Y@ @@dzC hCBhR pd|Z.Ox!X$:asHD#'%(A(Ig∅'uz=Ǝl*&fQZ/#Zln|Nl,rޜT^ ULLܔҰQ(i8B_xI1T);~N,r cny1/ln eNU %d4lFSm%W%P \3$Q =a,mzLP{_IS)0%(I$< uXEBι;]x u}/zCW[]܆%(AɔLvQx?'1cZ]:;^}GPAfxgϮ򢴄nkg?8#{rx]ɜxEo}x'RmՍ="Z\V q0$8wղrS?߻t`AJJ}no 0~QϞݶ_ ; ѻﴻ>j@{*6m B+('닙5_=`XrSCS.͢䑆f@w7JyfsTB= ?}TCai00luȞ@Ň5 y S{:mz~OvW竉(Ȝĸ>Kce 7Z|Z<8ĸm)ͼK꘡^Ab ىgu"斕 _nyOܘ? L B~Vnts367W3AȹSr^DcrTXSL&#@[/F,)Nj\t1&3~9SsrG?@|NVn 爓U@W+}8ionno_ooMkI% A./=$}Q869U q瘟:c"#39Ɖ dwkܗuM<ٹS`mjoPdNSG8tu}[TuՏlaI⋣_=3CG{.b55(Y-`4 قU/|@Zni3|dUwѭ]gdȵ&fYJ_&q #ڥKL?f]d=-;\x,YΜ-sJ-4zTvDޓw.1UNha@t\QLsTȆHbf}Ce'\?N}q3sVLS[tH rI@sUpa! h  Z  qM/l6)A&)A˞oUCAp.AAK@AAA\⨪ A2cOB+ұA.L F y2F%ߒɤ%(b 'JX#|f,Pi#JPd K&%P $l+ "H$}Lڏ0%(AL>JTIX$v` P%(A JP2$` R|8&/Eb+ fΖP)R:+bTl /KKhAz]$%(A J<,Ad a Q 2|^YD!O4)TdQ8C2DS[уQBd],,d%(Aɕ'ힴq&Xx:\k(hf/OK3)<\=.1Nxnl=bbRBm}"cĘS^\ 7' p"*&Iz]δxy@c9sٹA%(%,i1 uMR4!edif xa)`cqe1fH|VQRkM4Lt YL41/khu X{=bx<%UkJfcT'+BjLA9 3j~)\L|,}B0>ܡ/d1P䒒L*J)BAt{!YfZ,#@BrLaH GYd+ SjΓE)Q*.ye'7'p(DBT$Lj$)%#v5Y0z,$"v]C%(A J.9ɤ,1BH[jntdrjnS*cc=ݡ@S?MW5s=:l;Fe,:3A#.EQ*ҊtxS&RJh@:R q:6 G0bd-Q!ܘq庠h&pɉ. uطBRE91ƀ ?|gPRLz.AAU,SV9PaHCm:&Qlx) cbx.QD*JunAI:mchg22+>W}eӧdgWH&g11R"5&7+I7;GT8Tl-J0 Rʢyz)wGN4u%d~fUUU8tb×J9,2[%(Aɔ*4r\dMC =ZcpJPdJ&80a%Y ;%(ɮ8 /a'(E[ctJPdjJ&e "yÖ"݃s,JPdJJ&8v2*E&/2'I-(A JP(%lXS BUUՈ9A-AAK@AAA-A7 +CvG[뷟^be DMF:6?"f|:ceJni0ELK֤I+U7Al~b:_"}ۏQ̉A͞6dwV'l~Gx„AxF !@wҳnNe_q!nYT=,tJ",:!/JKX|q32xĮ}V(K%)u%6}u%sVUD~gn[ ˯ Yr~pwv_xJr=U09ZyG<1K rx( Z4p 7cz7@^Av`'~ _ Vn{8#h#fVFf)ZnAtx>6ʞ!N:d29`bd ML*M6?wٍݾ?|Ej=V'B. 4Z=@JMAfqmD?&O=**Hlo~b`֛ԺMɘV$`(ܭq/&4Eɪy<8=?=k;V~?9kVihܰ(Gyo_r'Jm`] N?Pj̼kU" _1[;:.@誯~O8uI`j҈A/v\ %  h  Z r.ƵfaO!%455aO!\#I(# %(A JP2E%"UDcXh(E JPL1, Y)jP9p_djaIL,*R5::,^PRq&$SvZ\  1FP6:r?3o P%,#Adv+c\j# :=dEg(Q"˼ PeX\PtE:GiZgĸ|(t)= ꥀ9CL3h1J&FFBEFFqcJP䲒"$|47ٜ"}q) 8JPJeڒ2y!FH(J1-38/պ\[z\bBF2{̠G"e3o1%(A%spm644ΜaGQ%JRN"iEdZ*Me 6 %(A JPh% M u; ѕs%(Y8;Ģ;(MG>D&tJPdqJd 2n=R4=fP%RlbBU) SӜ";iL%(A Jd!mK!  sX5 Z  %  h  Z r(穓Smo}hccfg=%+Ml]$2 0WAO/o|}?]k zIaηrށ7k?n)Zg˶}ٙJF 2g%u7, Wwh>} ?IT&:r 漚[Ne7 eҫ7sc~~Lq(i>nue:ʱARiݫT-ܪLޑc5T=/+o,ކnɬ֍_U률Ii9 ް1)nԂGTƯܸ؄/C(~TuYL5K JPwɞfoE*ؿko/ ,k}յqԱp},fuA\Aƀm]+JTyyp|_Wl{`ڙJ`=wo*bU}~{C(zKy:r<t'% %]z:Fx+.'g3]gycrCcWTr{R@QEQyD3D~Λyg<a~ƻcP(#mVX/ VgD?WSe䭷{$̰Tn5(Jtq{ 64_K{=͗I)+7xNA/HS:tR[bymޛE=T"{Ş~ow5M_I)JrCrtE~a~(v\v~z Jo1(Q^笅@/?r+rȳ/$믵+ozjLw5癌IoI)~īu%PWr3:@ FgrD@d-7g?8;Gl_W}/Fc<\Bl/o Ba! MB ޲DZ۫*})Xͯ-҄HsOWwT߲>|d%aPgϱ16G,_f43zR ӽЈ+̢>~{Tv&;%%(co[gΎdfoٔ[aU*ӽwh~|ZQEF*pѝ_SIJe绚=: (jر ȢfQX.:wrӚ|_^g!Z 51~೽n{ %  h  Z r)(xAA{.T AA\  %  h  ȥ&AUg`!\7DG,r`(A JPE*Y(!z1 '@a(A JPE&Y%#"(u%:pxɨ(Sb @# gEM. JP䳐,dԹ6c}t1j=2 @8EՐ5g$TǔAi&ji>Qqc(A JP+̵& Gl"q=1S|4y ]媄Mia Per5zFGM_P*AS4k#_U!{3@JsMiZ  Sefr51а6"2Y$f~6%(A JnF tq"vCA,Ϻ>xy_ gJ@{%-Q \JVҪ.;3"kroVȇυL&Pk֮`eJDJTMUW4, 𡥱ƜP/1sb%xhbYl0 U>jI Z]Z U_/HĄx`2Jft ERF$ & "WYPz,h SYk$tv uvRoTbj@).aɔt" 2e dFe0J)(Y8)rPFRy{\h"D1Li4OL{3? &1PƐ,#Adv+c\j# :=dR^P2R ܸ"FiZzetf3@=6jc@L)k'r88 KEeiɜvDKѳR8:=72x",2b ȌBD@ JPD\Bd0D&U񁱏;<0BZ,]-1:͇tT8qXjҐk3dFidxn̔$^3w*Rj%. 6QIJeGG"PB|(R&G'&4G JP@2fCCC8=O1iqH8!֤;f? 1%($qJ).iu D֘tU%jxwY_W*(A JPrH8w$,:+&JdG-L%7d3"XtXdfD%(A JP8% EW@D7l)}i(A JPE)YPQ6~yb!M^iN4&JP%QKضA녆Y9A-AAK@AAA-Ab|ө7> Kޒ&~ ~TEM A_8,;>mxƃ'cה6@ks@^A>s;~606W q/5U?|ȶ=CEz.F h@,PMIa`]M9:>[K𹺖j>zIaηrd9vUKkG^vWsށ7k?n)Zg˶}ٙJ*!uu6i}:<}%G/+0oǻp_hOI"\9VS#OV'.__Vg%ߪ %%ܸ=u|^4|3˓cѕYI6쭈Swp~Pz6n:Ŭ43+(pkEI3//UCkmL;S)lMEjoo@xC|)OGB߲?V$W6[c^OGH=s8BPř"t˂33[iL  e#n%7&w?4fxI%7'E_).0 A^řGA.y_ =ah&xs[ JdFl9k|$ (crኜc8$Kkʛj2/uy&c#*n[R q je 8EhhNP$l.Q@`pqZG}t^`0hC.xڒQi~,*ԓ=^LKYa)Ex~f2[pCPXEP==m{̪k;Y"Mh8g-~_|uGZ~ -M?{oMV&u_0ks*y$eF{<+<+]2pI,'MEkwkSR>Q2uV숽 MfnMf.5K\ZOz8>jUH%?P)pY9ӗmwwUjҭwd`nA"~p!Lh$.J 6 `C [lg:3f+WyŪo?8 .1yӦٻ^C:%p)d589Y" ʚ?{ ) j24ιihh(++JARMx[WN%IV(UBj~2|7aǾ4 Eb8hyVNk}y)Dh  Xł  %  h  ȥBAK0XS =8BAAA-AAK@AA+g|ө7> Kޒvշv?[ ?~ '!ȍ{VO۵%0=G5eM%)$?U*g߹>݊OLCJ?}w^[\Mp8[|ZZpd۞њbIDAT"z]d{Y Ew?Q&$GǰJ1SSJ2<(r9jNA 6ۡ> yJ>WZ'Q/)[@;{~?Plٶ~5;7Gluy^;Jl^4V`֏wІ$*\GEjjs^$2kYGN\$(/<ϪnsqqFBwM/ޟN:UX7kxeտ@W7(R6ڴ94,% ȵxŅS$a [}_NU5%Zn9M'$FK<.\Tn]zu*vDOu͉ Di&5:xCk+ի5> eҫ7sc~~Lq(i>nue:ʱARiݫT-LLޑc5T=/+o,8}LM1Os/ⱢKArDc1Ás}aK!}c:Sҥ٩$Y_1ES !_χxQΩ|==gz&}犩O~ bд x̴hߘИ{$ܴ-~D|(yQ/vrJL?]ͼ3TnݱjP(yy}+azk?yOSe䭷{$̰Tn5(Jtq{ zZ漤aﻧ5)eƲr`O׉ 2 ,h/U bM M84-x@kn7z3/DrK=n?kzRtȋPU;D#0AƛbP2&3$3Y ;> ^@~ W!@"!y^]yS[Mf:9dLbDxKJ!`$^.,Uսz\ ։1 %]jf{ڞ:.? /5Yv.^75~pwO'  6:?ؾ <^LKYa)Ex ق_ B.i3.eYcmW9+3UBS_[ {=fdOWwT߲>|d%aPgϱ1>6JI|Ѭb%J/4O kC#> \2kIS*ݚ씔{mU:;bosBeSn7ퟌ bKWXGC3kR_|KwWthaBzgtg:|i9V ﶻ;*5{;r20 ,?&4yW%G;|mtb0T-3?~Dh#nyqWAP7mʘ_?>X )NaKVcQ!Pj󿎽'`Ȱ=&]bʮ_U{kETmSz/V>SIJe绚=: (jر ȵAZ T}qgc; iM_//3ؐs%  vAAK@AAAR\ь#ǃ5 Յ5 rsI AA-AAK@A.y]"5IENDB`django-constance-3.1.0/docs/screenshot2.png000066400000000000000000001766631447075242200206570ustar00rootroot00000000000000PNG  IHDRRBE oFFs9p, pHYs  ~ vpAg@wIDATxg`U7%m{H%$!" ࣈ""M""w{ly?H !$Qf633ѢuxP]}]}]k 2ʕ_]^~qr9kÇ'Nyן~JQboV'%iZDKoѢ5|6dͻ{W4`KKŢ(k5k[uUt4-Kυ<*CރR ػTjfFtk\nqJ$Y[بREE)<s>Р 4Ȩ=z4-N p{R >p\IJrccbZ H sq!$#}--92$S,-MIONMe|OnݤRùxFkMWW*]MLhOLIa2bcc{R(lrMM5IǓ>Əd>ر,L!C<=d6};^b4L6oGEhw'Nhs*V^ >VO2g^uS*++x3gRRX*>_. J}|h4:nР޽R6x'1|}p[[-&&1 Vo($V;n\u*Ban.wؽ{\YY,VNN}=I뛙)RÇNfDBAQ$Y]]SC r;{LF}Nz:^VF76>yXZr E~~~ TEG?x$$32uujuG[EGttƹY5 uh..Cn޽X|dJLqw8~|B>?-,>^sM,Vx1>IVV=[RR="gp~svn߮(,Y+"<\_?'ƍB==-2eРYƎ/0p??ss(5U*oRCGCC/͛%g32"P/7 ! ܯ\^TTYY_֧[H\^WdDPQXzPK*ӛ5kz˟w;}:/߆ol:ɉ /$> Μٶp߾rCLNf2? JyHBruG( ph+W34즣sIIFUWA<9| H2!)Co}N#>˽rn({rgϾpl`̎E9;h11]xNSSnjZ>IG.]ڱhϞ5efIm}44o~ LfS_H.73Nᇀư0##.w,,B1`EDqW_:ֶvH\S$9Դ-({6lx6Vtse,O.. IrXʊQ*i4[[wwX,:D*Ul>OΝ?B!޽YYgL|}ib0tT*%IOA֮2Dڱ?YJ(MGKKgNGCC$EYc2XԫWٕ0ADDXPxvZZAA[/%اV+Mc0(nkT;e)$IɊ|˝U*&3#Fk)(PR]XQ)J&c0)+#p;;ؘ mPPBB\\j{2BQ4ZBNUWcURT!p66VZ/dVVW0yymŽ6j5A44xG>B(޽[jOF]g;{Y4e~ne%A `k`>`4I}^^NR宮A/_׺iZP& 27g/\(-^~H;|ϣ]:\x5 H$eeyy<.I44J11"р::::7oJRBаM.$9Çwt=ޞd2i4jks**Ztt M;q++MRB$Ξ?رzϦN HkkwϞռE{nb[8ݝ75T;wNξtw|||KK]]֭SS91Q& vs`ARIQ2YϏ $S>}zӓH^}wΜvoOGF[Y%';gUUj$+ر XS>>6uBqǏsBPTnmW{XL&VWW:uٳwJ饵xq^qqޭ l33_%Kf֍$E##??C;V<|86V*դtCCG꽠9wBl&mKSR *UqΝw?dE&em/I2~EXZRQFřϝ{HQQcc,. 0az{wٿHqv~}Ӧ]~$&N>=:xO  ccO>:W@ (`׮|٪*z.,LH<=uu℄ӧ-x$MMrƌ^||FkE\ޠA"ѣ%%$b%wkW;$U|?>MFqzzGI%%uqq_јLb4R*IR&kNWIe2}ַJ|j"fd#d2$:uNWlltt ʖCo E}= D=U*;;MX,+kmpBAgdDhRihHDۃ}+1M:59-)a2SYWdVTA<;اPDtXrKگ &X3ڞm/ZϦP0?%Ι{}$0LfbbNsrJ UZڙ3x|8:υS]EI$"Qllm-ӻ5qd~^` аeKnn]&&LfuuU>[RQAuDLf^GddCR\NQ$i`~VV7? 7~{oHi4$!CƏ77s'1qʔ۷e2S`/Ǐ75}`ֲ2[Y,.ѱ*#cܟHt;;']fM_>ɴwrI]\>P*ۗ =:p=&w71qݺB&R,._zU( U*6_?cٳ'MH冇yHEPXYP'LPxb۟`X,{'?4:Ų ۾}Ҥ@ԫW 29ҥO>4!5ήOMƎvm[9j>U*]݈c{щIbQG$۫уddDGCM޿'ozg2,-548x Ǝuwsg֔ e0,-gΌ04<~]kkC4a>'(kK$gΤjP 458"Ύj "QttrD}MMEE]]YL_*jB EEE]]Eca h娳}B=[Tt 4) JJ֭Q24 66o*ym#"\\X\^QӖa^-'++*^ZGO|][Ug{ >30P&d+*6(oo JJ,ٿ˭zr`+/ώJEDѽRib`p&S__01iyB!A }2\nUӿ0*Ki{&ڜ* RN  S˿J%0'&jjR{CúuK3fT^z$x} wytsL$nj12 RxtX"Q)jP77T.s'?_hK4R)رeKbɓuuϺ+pZZJQmlۚEQ E1.jA5x\-lb0BqZtÇgVWs8OloddcqʲǽBQWP_ۣ9uÇGQj%OQl*y&b==kk:p`FZG}Æ u{K55Lfuv8z:嚡AQ::::#\ T>==3ɓ=N卍nUUq8l6A$iniffiYYYSYiolŋ?5 v;u'lcAVUW7c'dl&eORן?bIϨQ}1MK::..#Gv릫hG!e2zL"I>0]kkܩ׏ФF33tqiOټ<]ퟬw.Z_ܜ戚==-,=c`xvvQ__VVXxqq6;P]maacca)鶯I ENNjj]=zٕ66޾]Vfh8mڀFFEE~sryMͭ[UUOҲ2/mgb~ukW5caabQWWYڗKR33K* nݺu{T*DִٳOHtuJM Jgg4(ܜB"a_ ۺwp!3F M( Tӧsr T<<<=%N B~Y=ʺ~]W/&Oor߯% gg$2̌FӤD%I997n^QW>|xM͊VV<^llYIRȑ Y_n۞yx;H2; ]7CC'Nrݫ!KKmNE"P_UXrE}V__Pܿ_X`h|RH4ŋ ƓI,?N(bpͥe2FKJJOgobh;:+\&%UUt g\Jp23kjonP}ٳlR5S %%'OFGy?/__}wp +(?_.o 9rn3fLo߀"Q~Ç奥R)uFQ?nh8lXXد~E$RQTS_tiqq}&; P(JJ÷mP Pwό m=5$d6iҠA7͞LQbq||tŋ2&<޶559/(-/r--kkSS$srtuy7 Rm[U:u{Ԕ]aֶ3Q /\8|ޚ4i߾0(??'X5fo뫧WSPPОҥ_}A*UMMrE6ݸܵ]=v,r߾3gW1㭷[$ ssJJѣ2YEѣ 4ڗ_Ν;ӧDEEmѢuuV\ۻ5JN/..+c22283g՛⒞.̘qI&IXJ 'ff@BAx;vܸAwsO߿z22LQ~~UV~|;O^8cctƌ(("^b)F+(rwz0a֬z;ewt>_.6l@hpL.Դ4oϞ˗y[2ڣ3J 4˥iӢD(GGi]W0c|A}}K2}䊊KYʜut믩SeCLL/NOoyQt*,,,L$pq]\jG$ʊs_9vC:T:] hl\㏅B۷ ?…=ͻ4Ο(:Rs꯿&;l).<:0^Ձh4[&  Ft);=wZtR9foVUq8鍍tJ&DD٩Ivåʂ}LR9aBRI߻sg~7@VhYYyy FHOco4V(d2oݺqCWw׮9Jt鵼: E5 E E E E E E EEQx1Po: "t:N t Fa>.≞}2YE՛7QlvSssq@*%($| 4t@L|]T*ػw{`0 \ MH˫ (J"I.յGnLMή5JKJ/_y3/O"X,cc?0Wײ7 17g2q)E54dgYs 6f̸q!!s$I%߾]_Td Tbql%%<^@9GQRia͛ݻG 8bDzzPer> kj$gn݂rs b:'<< ୷<<8MC^t)#B\Pj566,*-$HkZX]-1FFzzSVK$7o޸Q[dh--;ٲy<& (J(MK[ĉgwqvlwLVP{Z~߀\n듔(RifPeaf?obeRZjjW\RYXך{xxz:9T55G^.;GDTبVX ޽!ED~PP@@Pk[7/{w&SH22邂=gtun{t,..::9}ssǏx<ԤJb2i4*)ךZ-7689s8ϯ5(;bwa&Lpw⌌gOy37**]GGS>*PX_/ 4lXPJU^~ֱc11uuzGES[{@jjnFEEjBuryA3hH$ξu+)I.1bD77>_,}"eҷfM`0LL eMޙL@c++''[[cc__[[>3*STZ^^P_WEJUY)UU)amTT wrCx#IGSryU՛Pk*U]]QQ}F$cgꪧw~^^]J'NWWS4%/I$itu]\lmnkblK$I)X2Mf-&NoڳTk˛7BB{0EeeJIEKkX|TRYR?=xM}2iR:o„0_ߊڌ7ۻ76<̬T(>yH_5kX,M,..͛(zV*ݷͪ*SS@8L42Bx\^R\ޭ[dd<޳|~>egfdܽ;igv[ \\:LjF333336 2336ё޵R77'I:˥TFRN(mlT*t==& I099??/O.׆>F-,$=bq\\Nu+.ѣ7Q\^SQNPhbnc# مbf mAAvP(0$IJaA_{ޚ PST$;9xbqaavvu{*QK'?y}Gpw>|_'r'N;DS*[skG|z9ٳg}s))+VlܘV4קZZEl HkΚK~~STV֞=^oLF)2HRGp:Ng2;LB\9v,1݆b0t( qwg5 ý(jۯ\9~721㭷Gۗ[?._|c,-YG8QOHJZ_j|gg_/22z]ۇBߺuwZSI37t654gKNڿqz8MWWSk*Ut==W׾}.\8z<KK]]y05ZV(MR(T*iZ,ͽt3CG࿌BJeMN]TR)r9fb3`_!EQMA]}]}]#>>>>>]tttttttttttt @WV YYݻŋ EE"Zh௿^lۼ9+KG[`/rx3ub7._~=>>--'R,V(( 53qr ۷o_kk$+Pݼy͛ee TCB 17g2$#P{ƍǎ%%d(NQXs7;.B!&$hz)n:u\UNN!%ݽ<̧0i[oYYq8 f &-\`XGVVl6JM`BQRrk;Q`A:РЀ''33]]:].((|۷>x(J.ga5_f[Y9;(J3ȶꚚ7~+;[p||f>Ԕhm[P43ק,??.s]?##?B,V* t>- _##|SSH$k>VٳW;V(< 23+*$b0##'M8qGG>)IQryEEB¹s'N\pNZZ~~e6MAq8vvݻ:bD^ffLf)yߞV%'GG>|Rrr~~IIEEUU}DRiy[7=TQBQUzʙ3/޺SRР⬧ge6tС}89:0LRoli]M Z:zkS*kj+W$MW& <|#LLN曭[@{յII׮5$ƍ?Ν֮e232(J(LJ~ز޽ƖKC*++R32bckk[[*/}žֱ{˽@PP?i͛uuJ(,,wȑζx /S(*+˛/ 00xRY^~폇Qt+QϞ=v,6H$01qϞr/̚|:U`nR]SRu;!!CXX(|С'⢯O"QuuIɣGYY*U}?DoƍǏk?9'Mrvּ1ɷhzznneh zz|>b1$IQryccuu^^l#))b1A]xȃΟ߫׳EʄP4d+"bȐ޽LMl 59Ԅ)1=mFQTUUjKG\#D{y…#Oc_] zݙ3̢쬧WQWݹ<*pFctt4!$TL"HjheϢT|x^FF\.ARiV?ltE=/e^_MMi7@>74eݻ5t99ƭXZZTP3FFRQY((( y)>dA ($|AJ%fe;jڵWUAD 2vPVӛʣ@JU__Rx*GQL:4(H;|Ҭ85꫰픔޽ll4SlTThx={ΝbŻjrrLf2[ܹmmj 5 KJd=4횢ҢԲ2\N^h4g)SFvyH&Ƅ].]<1qРЀ''ss:]. ++ 23n߾y3+hkrMM{ԩÇ5oFw׿ r℄gMLlƦoߞ=?Ck[H$ɛ7\yIy{:8XYX4TZYx@P4碢7oHHry<53g0t]] 򢣏tT.+KJp[c%4Tff㏿VQ1dH^&&lBQWWT~~NQ|ee㹸 秤hu ΰaaa B!֖䤥%&&&VVzz~ҥ&۷m㎦hi2k, ̙5àE7wܵdܸ}mm)*??&;wZ^LKˈO??_ھߎOO B,-}o}kCf aSfg^II"A({ll:73{YJ+* ** _APPXӽX,KK??޽tMƤÇ-)I+W.Yfdd؄ hgsH~>EEߺumn5~IIV֓aǗpZ/MJ:{ᅮΞ}| #G`$Is„4hjJss];t(7ڵ֎l`jSoo/ic9eJHH\܆ 9yzzzIUʴlx=hI..C̝rw͙3n\X xxN}|5j֬]\qe2[##y,i41 2/?̬m}^im=`ȑ}XYxOmh4G 4i%K&Mrski0*IGDxx<0*aa>w矏dg|65,쭷,^ei^_3xp` X_M⢧Ixxx~:~|~ݺY[<:d͝CCGzY"#8Xj"I/S-裨(//33K|U@ȑ͝eebxFRt-\#HR'?H㫯֭}[3$ zٲQ߼o.<3yyGl|,E۽%ݻw޽Ma> 3=w77GU8<EI$EEׯ'&^uLGǑ#g|Qxg u߿rP|JqFF66..>>z 4xp߾nnxg@ACt ttt/8gf|||||<e lVWH'77777Ho|w:xܹDvy7gei،w_~ifP4֭[חE@U Ir=z 4bDTT`M9"EIo۶eˉ55J%[+W.Xb`@>yPT}?嗻veeff#F|7 ja`hhh{wݺ/ vҤ+/ظW YO_m ug"_~yyw,玈ARч'&ᇞ8U<2wG#4==6…[Μپ=!!-bɒ a_2Ç'Oj1c@ww>caDxs^n{x0YYo߻WQ9|%AQBazkYY*Ac~~~4xlѣ?2?o?wLL1z#Oo~̙ d9<۾:(gt:Ad9p{(Rnܾ}C+*U*{11qwg3Ft$1?߲_ OLkhь}| hh=>66; &NwL;LVVv =^wu ֍NMMH};.l#--EsrjBCIRmɣ|x7>7V.'`2mm{7MCΎܹsѷogfjl##Wa&M3&8ҲidZАru|ff}=ohؼyJssݴwO>܁'Ϫ{CC `fF55WfedlssI}gΜ:52͍*+/\ؽчeJR}}wqaʪ'N-,,4TcΜ쇢ⴴ{i˗=22w„ӧO0a 'm߾Ɨ[-`kgGAQII/^lQ{/2\s5U/~}qwgϞ={ΜS/IΡ<ADUU\\\\iLV_~&dzxzzin_wh%|xFF7n [y[;J[Rx ˗3њ)'Ϳi4&N:T幣}=i~}Wӷ ?|Z]㙥(R( t}}3333!#CQ}<Q^d9mc%%WV޼y!!..&?xgj:~GEEYYwch}WܤN+*n߾xƍJ:qԨ 58X/rj:ܴ[:10 kl,,zܹWB-%[j1<E!ܹsփ4޽H e^AAFFW?_]]W饧׷OgݡJ;4xwZ-奦k&''$\ϚLko]׷SJVvj9k^߷] ?q#ث5Ғ RR =:>:u(;G<ؾO{پ4ק(>"bH[3=]xSGǡCG)*:|8)):z۶;mmYDm yG 8zܹ;wbckBicc'=}|xN"{fw7t: +Y:+-----E!h"Y C>SS"@]]EE^o+ۖ"""""/sbqL…C~ybӟs7;;x$: EN{hٽϔ)vvLfݻ_?##u6:z˖m[ff6e޽;vLZRWᘘxy [ ~YxdtL^^f(- 08x!C,-kעoɹxq͚_~wQ,,-GNxQ?wgtvn =I,zuݺ]0i}myf/ thL*)WSRuY[%>;~X$F, ML\  + o6m>F޿˖>eMcưa(OxOO'N|?PJǸqÆhw^=S?l?먽9(խ[lٳ…JR.W|{!C{oc͢)?<;@۪tkWܸq'ǿ'f{x_Q8w^mhؽ{n<999 7n8<<|(+ 98 {s熄xuu+WB!)Ri^ޙ3gVTAxrGEEGGO>wD+W/ߺ5&iҤ_=qb$ՠ)_O☘WB..,ƍ;Ξ-.^vXkkF% =uJGgȦ`ףǢE[֧Z]Sto`|KjX\Xxg׫NN.Zt*{[/h;7l(/_nݺ?HnݺuKWGgܸ[oG+ =TƦA [YY=H)ff9sJe%A~g_`+Ν;8;{xp8OeĈ~jjNv5k֯8pӦ秳#j<b<}ȑOoCQAQrymmNNL̅ ffoj JeIɕ+}>zT]-rgˢEC2!\~/S\,(j59r…~;i^@{~~{\Kh-ĉ?_pTŢ)${xݕ+2D HHXEӇ rnfZrȐӧٿyO7~i'j$I}}/ >gkzرkeeUX^PP[+xOO[RYRXx_%%yy͟oߠA0$|>}~5K⼼ίy/Ӯ]ׯdr\Pp8փ-^lٜ9zhӖB[?biYXVXXS#1cƼuutٳEEB!xvv,fc#ӽyIINNaaMZ2~eWO驣I틴ի^llJJ23KJj7y֬?N_r~zpU7mZ| CCmhFF-]|Rx{s8mK_E&+*JI0UWmάM;ֻR6%%aѣcX[3%WTdgŚo"7sRcc|՚nkx5eB,]_miI[̚{nn[n[~_GffDҥޚ}$co?a?^]˟>gκuǏm@*MHh^`zx8;hkhai~s2ԩ$"03ݽ)׹uGr E2YbvO>|yyNNNNryfff&oahlA=ZU%WW76TAQ :uH]_(LKw'#DG ~~-а_?=n駻weŷm;;"0p0#sgܺ57dϞ6o=Y࿊F#IԆ(T*40 ]j׮K{d 17uG:;?ޛH&KMݹs &O^,X~]3hԼЀ6jfJ-ei٭@P\|VfPؿp n}T( SÇӑ#w$$%$79quMHߖڏ$5eYU*R&I:]j]o"5@ګ;}v۶mܸre cf&AjB}ݻ'AOaùsw?իV vαcff|ټy^^m6ߚݸСw[]vr1㫯M()ٿoIKklM>43 ӇNjӛJe}ÇW/I),ӻvI$MTbqu\ndӳ1Vee%%UV*2 ŵOZq..;vxJH Bazk)ڗ=ng1mgbڵ?p\nX͗Z-޹_;w>]Pc6WRi^7ns :e'̝;ɦ%%ɕ jK/E)R\ d.=Z^2h&LZkbfWTK$7VU%'߻WV&RR~!4XXkBڥ?RQqxAǏ׿ܴ?޿&Pna11 Mͧ/4#?V㏌ ]PxҌ={ΘqvޜRD}EÆY[[[[Z7cO?]^]Ru ,2j-{~/OJj< /N쿚5Y,7777k|g&8;~8wndA^rx\a 8x ;;6~xlن ?XUE\.bq66!!~7>><A0Ֆξ22s޼cwlh jK?dٲe. k3 tIGA~l/ŋcb/,фE׭7/#ʕ#֯?~զPGm$gS?rs]S'X&O~],8p С3-b2W$US6mڱ#+ZySbtuv~Uwܽ 6d9!?""h'LBuQ/Y' Eh۶˓'w6dȾ}%%zz斖::::AA?tĴi۶l<*Uukk̚7VU$I|ƍ۶-\دT>|" 蘑qNVVyy}ӖԺϮUw0 !aժZBl2Ç{Ο;K<(I$r96eo;/hזW2SC#22n.+c>䫯,)ٽ{ǎKrrkk9UN޻wl77G >#">| ;T`}}MHc}aÜ>Sw_ʕ~c0&Mrtl#AKCfGr+1wXO$ZW~^SGgѣe~ &jړh]@U(01?rr /\=KW B&KN^jҤ~[rs KJ22~u[*1&9/O>Yg˗,پ=+A 1XVVYYVVRRZ1_}7"ATԖ-7hygg92y哽s겲Çyg<=-989<<ז«Wgpw0^"I Ivw/78&fڴ+a45[\\\|ڴi˗CSA$g'N$'痕Ym?4t͚SBCQlCxxh= (9:YZ66nPST*O<AYل 4\^_RS^ ٵWnb2dw}wED,]zر'>BQVvŠgoz{2YccyyUU@@g:[O>ٺW/@W>S[[{ C֟W>/+}}Pmw.qo+yw "#{/2r__==MꬶԖawj$,A[&VVzzׯGF|X|zj X,k^58p$'O?ݻ7;["?ٙ$_uX66{wnn_HeJd4M|g0t.h4Zj$MMǍ;p +k߾uu;z }+b&oLJsK$qqŋ=z=zXY)Ni-} NGgo^>/+Cff'NܻWW7eʐ!ٵ)gdޝ>ܹ ._y˗׬1bثW? :-Ijʤ RR)T4SgbEgX0ޗ@MM=}C쉎>sƍ~R=JKzZ"??&СSJJ q[zbGE w_opf :!ĉcDz;)I .]g\+W޺|RPR"FFZYjuE7o6KxLfMMvvEN;yr|uuo=zLH=)wW׷^GW?`;%VŹgάYxΝ HmⴴÇ ;lٲe<[۷dՖ^6bL3{D?۷O(e. Ɔ-*y3'G,c^T{G2T]}N(/OOȐܢN:4*Om`ߋo|mt-={^4mI[//trz~0AG ۻeKk`[dg? Zpk]\F矒'&ݺYZZXXXXZzzΚ4vDŽ mm:jtir7MbSS+]]ί;Eii;v˰a'N~O?맫ۙm su>GXZ:8N*՛8{8ƫ vhl,eePUU_/)LddϞNN4A"QVֹs׮%'WW4Tg@͚}޸A UU ,TSPP^.X..in\(J"),LIuΝ†T"I{A9gũ"QN΃7nŕ464gkݧO޶::UUwի99B!xe[Z>y T>>үIb7ozzBaUU}D"3O^sTgf^rVjjYRIt:kcӷo>ݻذJۖm!Gܸ`XXDRZmhҷox1[lٳ@G}_ ^lёW>"I.>,g޽utΝ bbjjBC ˵ pqr p}ҥ˗㫪 05 gxy fgd\N=xFƥK/xy鍍w>x@Q^^ÇGE*Uuݻ.ݼhc3dHPAkQH;bC:;H$99g\rvvz$' >l%j3Z__<9;=BBZ.N(+KMMM]]##Mst F"y|me@$`鹻GD2Y~ /޽ak㣣CCTz>:]WTӗ BǦURS^%ťԈDJJPH$2Rټ!X4E KX,@RYSSTT^^QQYYUuPaZPPTBZ8L-)յ|kkWW>?117Al `x4gdݼ)XY0յ-=++72}9ss''{E" >xSyJmȑ( x5^lXLIH<=#"BCCBLL8:??&YK4s[1VVݻeeӿxt=<|h#GJJ n߾|93wo}AKRU*jm/hٿZ-K$*kllaadjLVSS^i+ CC++}ʦ9򆆒|/?BOO(/.LVEEYYbCt:ErH$H^;;$ ||||wՕjkۓEJ6JP(8:3^K@M/ 0ffݺgٓfx::FFc$|;^BCE۷\9w.3S.LNdzy=k::vv=zܸqDttbZM\f WWz1..NNt͛Oٓm`ٻw~&&*w!WW\|J%A$cb=pm/JUUz))r\NQ::}8:>ohRgx6lǏ''L0{`6^Bg=">4xL;q==)>>>>>Е rz0@@@@@`tdcT(P*Ho|wCgPO "@mmmmm-o+й0@@@@`kG9@kQе`Haa <}E{~8iR&&#ۄ &QTYfg7sٖ#,0pګWn %mki{:Ǯ,VgIGQ(**no?|դF LM==Ǝ]h //y:="zx<|rО{r+Z~~O:~vЁ! 0|`hد߄ QQVV*Uuuv[߻wĺu׮%'Xwʕtzӧ&'Ξϟ>A7vȐwߝ6C.l]Pp׮h0df AOmӚZtqFFce򬣨uu˗O6wnttM Ax{i`Pgd]~ީS..X0acGJ|Y[99/;vvFƙ3W/_ӷoa2l/Mޔ+nn??;M `A|1۶9xq@>|)SN|VSkjbc/…G)J:"4t#l …ի.\ʕr03 ?- 73HdG͛5I޼R*nܹs_=~<=AҤ#8xƌŋ-9RG̙?3gǎǿjmKn cbV~tP&h4Ǎe{gO^7g:5lؖ-}׳'bmGqGNܻ79{ʔ@Wxʕ+ w-''''SBP^^^^::ƍ3z(Wsp9>L{T~_=eʒ%.\߽$J*>q̙2 >Z&-**.͝9s\wȑ}4o"QLLz6$njfgɳ"߼yЅ 55A FϞ_|+W 2R✜a:^Z+yy dIb"A2RIQ/V^tf9A8-xx5+u7kgacs 'OΘ}I,E}m'X$)eem>mGmܘ0hК5AA\P~ Æmvĉ͞ǿbEHȋƻw.4isJJI^O,.>r//ON߿f{ ޽b$$թ$INhW_͘Rn6uΛw?$AT;N?kh4c@v E4k.=z&DF~5I4Uhץ*}}/IDAThd@o_ḶG2YHw<^˖8q…ڞb-'^ki9bĠAg0g̘(ׯoذd*fԔ-_~-c)%%gϮ^G7'&UV \nQ{V\*>^(;6*۶##MLhX 5ӄ xDzlh֬Yz -Ze lILLLLkaD:fM:u*5$a2 򆆦#G.]232'0P(OOܹӧ++/^<~<#O:7RSsځ{<<0ƌ =(::ݺ͜pqqgk׾fHhɷ䘘}##-,^fȏyrk[tNv7|ܸ//Ef+, 32B=T*ĉpF&R*kkE"p4?1 LbmWdee+V[;xy!!ھ2uuYYޮ˗&`7th@JRiWϞML,,Kl4?FjDNNNNN3Z$@k4vۻPgϞ=׼`BQ[ )fR$JLϯG?y?Yī: nftCݻO{ŋII w޻@"ѽ{6ZulTkn7/RǓcc׮}>;rԩ5ko5_o{mE-2Ӷ)V2HG h4wٳǏ߾}Μ[o0 {<''''<333_C*Ue?A hޘtׯ7Dn޼v449(ѫnת $-mރ8o \]CC'LДҍ_|1lؼy/76T*j5Aċ=th{{?61qw26&#G**jXc;<쀮ɴ;vΜݧLٰgifE-=`$IjCk&5eJ֭_\VMq+|1;9uX,s=73' d3 }}KK]]Q&==MR>BO凭IͶ 7oÆ55.}Je߾sJ,,`s8)it݈gϦM榩?3f޼G U*&fԨ_~9zt ll/HxsIuutɓ))Ekc[Ç;97bG;p )8˛b">+.]S*t(p|| 9ߟ˭~+JJ~qDss*UmmnevUW9{vq?k'+ ib/jummNN\ŋqqr9AdGxAQ:tDBab2iǎ}W5Ν%K 3yƍC}z|o5k\\Wirʔ~r TWwYƎu3_գϒ?'Ԭ##!>~Æ!C@wzիf8r$, /\Urs_{->eˬ˷l ""tu_4Euu;vzrφ 3gkiLVPw?;嗯bfC]]'NܸQXe|>#UU_eKyyϫA|NWd~wo~~a!..ۛ_V\jlCeennޏ~^yWz$^k\.'L&d۷o۶mGߣᥫTYuvvvFlbGb?[YFFFE-Zd̙C(jnNINIo92,LKkDڹs/IIѩBQctϷm6336{ب=x/>>SΛ"]uʷϟ;oh_dK|>.&SO9<|ɒ+?("!ꂂ[~_,(;˗=}Z9'ۃo}|abŔ)k[o; ih_~YUW_}!$A0::QQ_~aaw@<}IZZw#GvDr֭hQI9sĉ}EQ"QACk| EQ,Ype_~9k{o9ndL@6w$#:WWX1}߿D׿*9t3~CeHsZ rjj<᯼aoM榩`DWWA?tG9;^A+efn۶cGFFcvEņ aCoݻׯ6q=s|ԏ?=;9yժ͛uu-,JdzKիgμvg߸QKҲS}Ϝry{ b7{*C]A_?==6匌&wR)EٹA̹H{DnϜJ_311 w569rϞ,h̘ѣz^_iժ˗GڸqWWeo`0dر={YWT,VuTSU?KsA"/)aVJyyǏ gK,)g [ÆbdmmUUcN] 8:lVUU^.yyD[[RRUFHȤIʳbnkcbN,+̬::rU$) LmmcInڻQ$Snp^{MG|tS;y &DD:GD/}B\~#{A͛7zC=4%URr֭?֭ǎtu0C*-.޶mIN׋HH:; {ᇙ3++p? ]DMϦM"Qq۶wߥ븸FBhiIJ%K-zo'ׯ˗{ z;ϊ99t)56J$$IQ T*VzBcAAAAǎ w?+dg].orP(+卍))_}ᡭ=fLL̝o3vɰa7JoXaIDʽGpKIK[WOd,8x(“'ǎ54 nn^9::0pؓ'~?DRS6$&I$>[W7^N=#k7nv-+^W";:y׮<{RRR=RiMMn/_lفխw~UϽm۶m۶Aە:{ BioB\e_?%%~Ŋ+з/GI0CJ+*z/;/$2244N]cܦ>P \ 6l̔J9vw=-_[[qׯK$yyqq11b1AO>t>IQ-- Ap%%}3l:]tfFFN6knܸ[_rZaTZQq떖֤I66n{055wUO?}睹sܾ׷uqkjխ쳼Sw>t޾ Dޚ2ll6m|yLSS [6m7n„N9rew54||:5y_}5(hƌ<8!ᣏibm bɓ--AA!66[YѽFQZZ"QrriD2lX $\MAGGOá'2pkcmΝrCC.42z<Fj{+F :W_{SShkۿ~4;;;d2usss"###c/'xm41>}ݻq\/뇆nxxh(]mٳ;w#gqzzӦM2}zhV#a&M HJڹ3)iϞm˽\\llBBFϏq45%Kuu.\C.\8qbϞL`|gϦml|}`hh88DEmQQ6)'Sd]]LVTcDGۯ]6bD EwƄ dk֜?wܹVV`uu]26dʧsG55+i,-ygB9[[]]3\OŒjjD">F_EPW:ۻĉ7V{z7C[fҩS̔2ZZ&fg:to?kjJ$=cP.W_Q44~a۶sNPS3667zK*55;QF~ |tGMG{2}} Eo44 d.X~`hiFE[^]djk;;x񧟎tuǁ$ d2Ys~?}{޼C&O!3g>dƖ N5ޱ##΍ں2y$cΞ5jooK,.(޹Ɔ lIkkĉ--Ozի]\\=㢦fb'V^442e֬)SEuu33>z=-bYZɍ7nH$..#GZXc_N&{ϟ˗~{P_ŋ/] /]Zuذ;;\7LL,,,-?`Cbcׯ ~ᣏ.]:V?+Wjk+*^sq?ܹy9޾QTGGV֭f&&VVvY[oذ;89с%i7899z@`n>jΝ..6|]XJ Һ'[ZI՟f[G|t޽|ZtG|$I$Ix}ݑ䤫?kz1\B<,2Y]RӧϛFJajinWoyp%xxGoݚʲAA;w549 oﯿ˫r::Ső䣏_QxwC]{\Aڵ^ñ={ǎٳq=9\M`tu}阏rUY'嵵׮%'WWKmmjkI%&:VW'D]]lDDp HTQvҵkEE--]]r92ztX?Œ˛sr.^<>#V*%KKvԨGRWS= E[[aab ))坝$djiY[:b#O\Vv} lv[[cckHDQ^^\.hjq <#kk%cɱ JϞ=p 'cΜmlnabMMbWXEw33Wv__CG yFjrWrBa}}kX,r8=__zzv'%%66!\.y'N,g7n|EFN6mopxbamb?lYqqNGTTw!KihxpZtٳ AA&&,HTRAޓ'Ϟmj&74=zbf݌ÆUY&֦edtvzzFFΟ榥%66==LfGGaa|knn'ϝutܺu񅅦&x9vlxDR\|#yySzy訫EDmoߍ ;D]n>w~$1rp3f̘_ÃeD˗\7n//}}6[,.*G<}GEEEEEE|O3MMyr̮6nssffnnCKQRPxvM iffmM\(g 2555pjk}[$"IOtfY[궷VT;;kiut)+*ryyynnl÷Ʌ}콿7UύVb蘛xmm%%"L&65OGzzzzz:^NO4p//ܹÇww;C =Rѭqq+RE$khjjjm(J*~=6ҥ rTW75 51pʯ{1aĈP;;M;Ti{eۣ2r.:wի\T`X>2ܟ^f fF^_vNuE2IX`TZW6u;;;;;;<[/ 5} {=J\"(ҥcjk z|A553(h|OBB[G rml&N\}ag6{6>o۷oWeE-Z=rB\cCBf̠ !LJ~R(‚zIWz:}|+sI--GF/!55[ȷ^dɒ ǎ51Q>cb2v…K,YۑtPs<+\._Wߙ 96Ub>O_T(1c̙3++32Ο'I"IѣMS}?x!x 1|{yg|{{vv|>=|WWiѣ[omzhiiWA(+χMk,Yx_}ŋ'N',]xe_SZ*t6FgڵKo孷VҚ{YYbLVQqžffŇUVd$la55++$) 7W"{iN8t(>~ٲMΟ?~Cb􄄷>xN$mkX/]:q$ [pRRD'5!I*$\PL6mڴiUUYYEEEEEE l1!ILS NѡJ/^pL*%wcDz'L3gI._޽7;;GEQB }jUnk߾]WWYyҫ޼jG G%glmg47olĤ$c7a<L…˖M:d\uĞ=;vҢ#hi=[$9[[]]]9\VS#66t׷5ݝ&A?uС7nZν_uuKNjf{jiYXK\&ML@jn#s[ZrO~zȐQV*O CG}ϟ?.5/ꡤQ@QEy2_;*I67<9cQRT$RTWWa?im=sSʪ)Ujj Z[u|----=_ٶm۶mۮ^olLHkl믿/H$lkkkkkknniiiihhhhh9y̙'ӫ |_pGi1%`酇o7GN6zuR)ea1u?=R}I׬  ۷^}5&s E-Zh۷oww=z4sCCcbbbk-254*-ĉ*ˀAg~]-=====PRRRRRbgggggge"wT5m]o!=.b> {=OCT>I|/3}/ ypLt>PrBom>a`x :ZZZZZZЉ4\+?qy8]]t"ADK  ł6^a`@0@/[[g!C/"}/)~xq=Ѱ3>'LX2+~w޺5*Ԕ"k֯2O:: ||>ԩ> znq-r22Y}}Z?˾}11uu"Aahhg;};X1sLvwto(J*IH8th;Nz57 յpw1bܸ7x33W(*ڵf>p~zݻkkk> ѣ c\.FχM*-+;}eG+/?{…r}ti_~|$J++{Iƌy뭫W;;'7uKe. ko1b`tyC/̞mgۗ4g_9{w~>E==ŷn8qbK A=d{}5zz  Ivv^kkKW=zJK AFD)VVt-EO?iW1=,YS[drm_I")-=s 45ml,,Ҳ۷kjΝ;u|wup_]GG\ܗ_n6hЪU=EBaRw}EttK b s?m޼yϊIIII<ތII,g}-;Oޮݍ B"IL㏳gss\9pĉ:064 x<;u&L;wѢ˿_v:>+K(451"8¢vu_Ajm91{xDE͟jnw*9@!DD,_Fѷ՝={lqRn'Mlkŋ}eKZZkk1P(pϞc֬u.{ z~rKK1߽::옘{/YX3waA'%I$\ܹǛ=͝|ĉ[ bcnߟ:u+v_}BبX_JOq׺Dn)+B  uw7npmm Sz-`z閖'~۶c@_;ϝ||O? 11A,mϞ={V{}^6O1c2 [ $DW  tu#"f6L_g[o!%{G#IB(\ 22 ^vǎcw]p@__kk:"غ_bc?+ BqXGGn`N,~}u>KJ# GROoĈŋW $hn矒ޖ~=Riak~;{fK"װ0--'88&}tuee)'ⴵ-...Ri~~~>ze'1#*j`}}mmCCyܴzcML1SiiRRnnϛPI-7 {mmSS>W"uȑ|^Cq%K{oݺ~۾9RCC%S*Otfk;|ݶ#GHNnms$YP_/VWRT[[FɓIIS榥]zJzz[wOyj=cѣW\ϏnmtLP\\WؘysΘ1~]ёq/^|`YTBQ_3fl\_fOWOW5.zΜ?8xpΜG;;qݝ]\&N?ك++ϟ_j,{{''}}hl,)).(670a uuzII۶\vYXxx{{ "QIɕ+ b1A3yr`-޽ǏWTttw쉉x z ==%K,qwOKۼ3g׋F02˫]+)9p`#U::iitgh+?8;+NzL?vرvW26 yW_ۜ{Gaz o"ii G\x㝝~8k_EFʪag:;326n1U? W Ӥ6hРA"Q\\\\i*kq轗 yom7߼fD1U__\W\\[`oϛ6ko?nܬYnn/8qxt?\- >o?LHW2YEEl޽EGwGܜgܾV]~Ν|+#G:9q8.9s??dh8dȔ)Ӧ nlᨲcbRRSWwذPk;45† HRSk 6lʔM7*)``++f>u*,l;εdBςy=EǠ(4 鶶CR]]ϖ*W}|||||z{^"qgϮX&6?L11aZ[cbml6o~イ)Stt ~xTkر㧟-[l<^ddd$nݦMу~[Tf]Ҫ̊K}|.% ɎjuOϠիϜ9{7.0<G[K%d\dϜ{9!;VS|=+Q,H$Ælczռ$aLV^}duqd--7o>}tt}N9xSRSϞt^~zB!+E{ d::SŵyxC2Y]ݭ[yyRqski6{17;ɯ׋$Z^[P:::9ikXxȞhG=嚛mm]]2#x{;tI/8eĉffNz>N6X,@K˵I(*HHHIHԴ9r++32Ξ=p,8x̘a}zs8rykk~~\\R͛r9AXFki&5sr+*Z[e2b045MM}|Ǝ15ml|εH˗H$"MM/1c||z3]}>o%߸X^N\.dDr9>ގL.olHJjhJۅ'pxGi%%G<]b)_#GlSSm펎V$G su54rNO CGڵ33 l]]WS˻pԩ["4422";//6…fcc{->9<< @ PSꪨp!6655/"<ˋc0DԌ 4i8@MMhjJMz5+rx]]6Y,zÇ  5(<9ڵNI&Lpp⢢3g⒓ njqq1402vt4;{2}`2Ym͛7ovv:9;oL,3}oggeer+MMffǏ7f>)6774&am訡f3/76Z[(ۯEQG?n%ݾ}ZbbSEDĄ ,VWWqq\n\Rsz=63M&+/#Vuuk@?{?FQпK.Ԥ`| US*/?…Լ<+cx<&F6SX,mm@ i x:e0Ҏ.$ulކ. Dzz= !9c\]== .^AY>0 --\^, *-Ͽ~ǧbycRKñ 1anx~`@0@   GɄBt" WEx~bnx\)C'\^, n  }>a~:Jkj;MHɩ 45MM==G-7o.^dYo$W_ݿȑ˝\r9s"# k3gv4Ȉ>t„:s2/ΜqԮ\Y8(x6DGmh!I|K۶Dff` ,[ƃ['ڪ38-JOOOo3||||||#ԩs粲YnJCC7ZέAC[/&L&AI VMqqʏx߆I3ĉg_$xQQGLz&tuݼq) ̚}[[!WToU\retuChj>Y yO|VZZZ\[XSd9bȑCy9=Ѱڮ]۲pԟ>xp͚:x#|5kٲc~˖kׂf>6(KCȑKRd ؼ<ޛDPV5lɭ[׮UVϘ+X}/q^nUvʔK{?f::^=Ҙ_|_)-J{_$Җs电nn``rO7;^ IR,=xIoΜiz^ j#Ǐի/5kIf\fUVF*}ܹ[xڴǏ7n؈3̶6e 0׬Yhɓ#"BBBBM[ر 啱#:{6=}ǎW^ ˗[[?ґo>pWǍx=wb}} ._.-H$[[srRRɓ.4z8 0>ms77L57ߌ0aqnj th߾~)S.MO`_;r]],ӹs.&(8xBǣG33;;32^׌~]˵ :;?lq45{@"QCCg˜9ARiQѮ]kۧv?3YY7v_˗;;[(&("Ͽ(*/YbΛ7ǎsoI[[;bĴiryA?O[ۘ1_|ﯫ{M\Kˑ# RSkjd2[ڄ3.(8~ `''pZ?rs#퍍mlLKb2_'Ioի8z'q~~;w[n]mm@w@Vo?*8;{IAٳ7oL'vv 8uurySӵk;w~ÉUUBT*47 .."Qoy;ϕ+՗/_,x{_^_pƑ#d05k^]~ӧڲ.\3j}}?atunjque2_q2z]kJa2 CB>paÒ֭KNzAS.\8gܾw履GHȁ˗{zjjb>^ma1q%ǧ$&onfDa߳@Q$P^h G~С}O\qݘL&1P=dȼyǓs<_[k+W}mx!!UU.\|r[V@y<;xpWז-Wdk…H2ٓ9j6b_~9r?3ڵ7nLKۻwŊMv5kmϧxW T=( nk_n߲W7muʕhQ\Kׯ?ܹ7\]#'g۶֮.e9սKq<ɍ3|+VE G.[6c…7~ƌ˖=H<ȑ+V,[6t(Bj AmmՙL/1cܿjU0,qW7[16o}zV|b]H~۷ggD9 />&SG' ࣏~iʻ|AQ]]MM55o 4l#yWvւ˗/]mup>W/\Xo=z}}UUa2 ÇE"AԬGppسg׮b+`''pO⒒/]*/ 6mW^z۷{V >Z響YYVU;HVRг% EggCCW!I67eeg}`0jj{TU$cbM #88&_($^x{-8<<)ÇWUuvVUwASGFZzuuuggUUttQ?\y.mf6ew׏?Vx;[Tt‘#9rBQGx%7ƍ'1nn=tq vvkX,CCQ,,xl矣Ǐ>w37ΟC-^&&Λ>nɳgÕ}G}ECٹ~U95Ad%%}ݍOfk]q˫۳'(dLjeUPpXff{5KHΞv8];}32SK <<Ȱa&&t_wŋ E)g=1Kz(iiiiiiPEQG.9z7#"y̙}UrEzrBcAAFmOvvӧ=РPtt$&.[6x?uQAAǎ kijZXLaoom:kVttmÝʖt>f4OOssccSSSSssOiӾ&>Q.ކDRTҥF98XZZX 66Ç/_~L}mbqQ˖99YYYXLWW45)[af6a”)..VV::c|ERPH=Դ7o;;33}}=='?((Hb~hmdIRRg'E)55{XiӤI3f9CNkٳ3fGew\fؘZU{WPtu~qddLLcG)J*-*G??3cdǏƖζjjNϽKſ>~@`n}Gu~; KKTkڵÇ{rp` /HrrMMR^\mxtu:gOUU@Gb<,Yd'fe}\r^2bĚ5S)TӢE^TTV{ƏL$Ç_}C~-;*;\]{_?|uko|yժ߿z52颢ʪʄ~6ښˢ+;{ÆW^ٴ[>):/o'={ A ?]^^[wY\ܳh;RR*+ϟXRR:;wꎎ'P\[^.ge>0dԩSΜii{T~X,>e`0aˆ0EQLٜ9vTWZU{kjr,X=1q B,׭[gX*,АaE.MJ2銊+Wf6lڴ;jkjkkjwڴO>o~O>nO/CMLo3?.tw=ɔ Nڨ(Oݻ珖J[[#z2ZX lhHBcj:f+/_~@aXbrDi55[۰@ z|V;;33矛oZZ56nܘTQ1f̍{xXZFDhimWRRZb]LF Ks\VSS^XdrUbMjUXᄏcǭ[bEeM/:;+5rݕSq\cd[omvtZZIISSWHTW8dH{{oyIEbkckK;{9 acok_|1o^lSΟogDaEQ$yȎTi z&lNe.={ ,,1^MMx&O~-KLtir `ƌC 3Qz6rT`2l,}Jٟ _iKJ[Z>\C#$>:slj2dWWz4E$zkjr˽!]ѣ?`|bӫHL4裏ld$dr,&SRV4iժ>:zܹ={'ml6oLO9?(~*dSؽ{NHkXsB(,(,( {~(8"B Y9jm]Rrŋ55 A(--ii=Sڪbc}|͵HW++-rswZ/Ͽ_?<,xy { d׳O7oG45w_aJ$JO_& }֬Gz7>F 0335:ҥ~s:>cLMvڿŋLMMM-qcw튊25G?\` qG޲eӦѣ B]}Ȑ??u߇7WmYq%iPر~~[ѽDžñ3&$DG U 4iCsscIAXK6jʔ 45=xUuu[[N(l3;_99'32jkqSlmG?~++uu1bՂ%$(_VBCýyY+)))))CoI711-WV7ezI+*v3& :zⴴӧ15}Jo\nȐ-;{۶Q(``zk׹D{cdGGaa|knn'ϝutܺu񅅦&xFF$,zMMYYg'V\ ZZjkյjhXZpHCK!8x̘1cxsjk22:;==##wsҒZ[{zP{LUτ~;6ȑļ<{Stt0'߸uƍ۷O|A Em+/ji?'--ee--NN8>I[;5E.wqյJK[Z:;rcPsF¢LL|}盛xȊ7%KJ뫫H:J)D,y, z= L--##-˗쬬,-שׂS=$ٿ_uuzIMMss;;>?1M.,dL2e I2o **^6XVVo^?xx\KKu++KKrP(-,,+#XOޞ) znm\yPЌ(G7ۋ;ģu_X,==oɓM36.----//)IJή2e4__}}($I &qo`xEln{{aaEE{;IIZ[A Lv֍FFvv<^UUffYca\^unssffnnC2ң(T(|YT?b0l.jkS=˅ŠzC pt4hذ#<=uu%fm_)rrykkqq^^G>97O46a2y<'CbccbKI!I&SSjP''O9[XXVVt%88dKуoA7ztkk||\ܡC{JEQ$zxZ[?٣G rqaΝ;|xl}}wcºG|GMQRi}뱱.] 3LOh =교+1-ĉSRRF De@lA{@Nǜ9yx \aFSs˵8q@`@0@   }>a`@0@   }>a`@0@   }>a`@0@   }>a`@0@   }>a`` ^NaK06^a`@0@   GY"<pExg7<^ ΞщT }>a`@0@   }]}7oݺ'oڵ&=`@edc R~`APTw EEuMR.Ogz^Egfe=a$)(  eF (ݡI*܂/^@% IұIdgzֻ(4 J ~6 :\{<@*!IepuWuWu{Mtrkʭ(н%e ދgaD0eLF?} 응Yxwg VQǿ䋺g a$oyYvw8ֳҭg834뮬k/}l)"YB*Q(1h=+z zNlѳR~~Ω1zYA*{;;*gp\\OOSSMM6[EC")--,JN'g/ʥV9ޝSp<<>PB=fߝ](óoe0lwwook#z \55KKss]]33>&ɚ\YU^3I6˳TL0gdY[*H;;H27AC˥(Uv|ŤInnں2zJ;;;:rsoݪNJ(??Gq&&+۷e276;w(+WBCMdPKKOYi(wu\XT)3*g"5}T}ocuW)e0(J")- JO05utrt$ɺonlv-?(eҢzHrutFpp4H ()pK 55TWhm43y< Җɴ ⚚v04664jԨ˻of0裦RwYB*Q(1^ϱ*;GH23Hd O_!pԬmmͭݻ\ihhi(liho70wu51ry<55.˥EQXؘե奯oe!陙q8AׯWUuudL&fjb[75H== jjd2 ~YC*Q([w{wgdr8&&\DY\I,̈́ \.AZZU_H#Gak뫩)߼Y[A$IRB!DYYRA!VW74$P¤⮮;A&;{}㲲xJ*ުXF[[ki55ݼYS#LN&oio%IQS)55I2:"/-mllltr2332%˳ E"::;oܨQ(,,\OO;;vb uu۫ǬP9vu/>P]IGcݽDg r8/sѣMM<(T")--,};..'+Wn uu}C--MMffcADׯ75t H055?Ns __CCz]]7o޾-P ]vͻ4eE ,9mǝ]-\EaS[pwF]==D+Ss/^}gGQtݱWkιz^a;+otFQE ]{B*鞠*DZc0P+erٞKc0z C*sr;ovUVY{~u/>PIYYYYc#yD >۷vI*}>90<[ P0@   }>>H$ЉσG JJJJJJЉna`@0@   }~T"IENDB`django-constance-3.1.0/docs/screenshot3.png000066400000000000000000000620541447075242200206440ustar00rootroot00000000000000PNG  IHDRosRGBgAMA a pHYsodcIDATx^ |աJp k.B>@E!zW҂ K,EZT"mG@+(6PM0o@%x5{rO Mٳ̞3^k4:i{E(xYbgE(xYbgE(xYbgE(xYbgE(xV; \XEZ@MqP:ʷW>٥wn$ iSCG>,?W{587(1+wK75?#/8f>0]ӹ5甬X⫚7oczLqNj uI8r7ikqo VjE p]PHo (OBC?[JԻj4l*p'bpݧ7&*a4qNꈜ͋Qfy<հs]ܡjrm][.MݳY3__֔yS-sպ݇j_#SM~_QOS) VZN%U:QɡU_f>[]&1zcwMU̦$&T3*O.=z{-xb|gd 3>؟4Mz.Nq:2D3J0sMGZR4SϞڱ"%P^nAzkΓϧ-~fRsɀ]v@ܲOBޫҎ')U_Ě+?U/^ҚU3צzv-MU^Jrޱktۖy̚Vޢ}*EY/FJ{yMg~95R;)znf~Y9>^-:u54sw).<;Y[*vBS5s\ouxT]u=KxMi]Gh`qRsU'wz:qzƏQmeS5&.HR+ڒ<[ה*WgIÝ0LJ}7QIv3qc1khZBZoڭb|P9^M@z[4ojͦA,7<qP{?4G4wM7/dQ sֺC{jиqn6]?1˽Q)'TͱjWhB…˵YB{k CE֡FNe ޞ}n_;Z%j?A)W>4y4eTUs<ؾ@vRmP_>ʖD%,}/%$p3C:e8Sb]Ze*NC?]i ōF=t+ݵZo]y硇v; @(xݧE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(^ 7kc[u!&kɚ<3Yna(.UD=KMo/~ĝW;s֯b#۵굧K\& OUO0=b2Wtͽz^ pu;6gͿBq:|mRzTSR@iRxL ,5ΓɑmO^7qq Mvz5z y {Hi3> vm~91Myf-M4Fݶ5wiԮζm8H{)pG'RnUf*4}hBu㣳tkL6yToō+֪E[uqz]By%)/,NO<+]KI}Wʡ[; ^1f6|K|Yiy: j?PАW-&4b;LѼyez>ykJ~YsM?IW˞[ =-;o(Wּ5fp8ruX\r+R;L 6?&b[fn՘ 3]mK%@l'ҦE/Ό+\O/bc=FUpT l=b;'(SQY٦QP ݧ4/4)TO(y5zUJ<7{OtG)_^5м$Qں1|i|ߤMѧ3_sf&V\MZPrr+pMQna+W;e {c8m-ࠊPt;u)f?)kP}. ]w;P lkʞOI羗r?P!}9T>S04|N*; W|J@6}i[S_ppϛ)^|yr56x;5Z_^}W}hi*tOx7n}H|G::mF}ݍmWKڵ괠*^7Ǿ~[kh !&j],]wҒіB0@[4Cյg~*pٞX̯:Ngk:˄Rl8Y>\Ok{fW5u}.>]qW(1qٰڴ766>6uSl]fmG<0WN۲LG\c"n^y>jQNGflvy&>}3j[ݟk?e{ܗpg }Y s~6u<+~&{w쏵ڼ=R VKwq~]߯/Et\z'!۩*ԝ]W?. H?v.M}8]-s 5dkE,:SI׆C(5jc@sh{#Rc? yQֆyj6we?ߕT _h؟=NY׮ahcj( QNuZ_}>ڦ,wq@_ }Zg9AҥMŪyܹGePf p\ϾvT6|a"q._Yg=!$z:ϧmϘYV4 _(ȼS-_?UYCn.SdCJP6CGj{RPpqjPi8.z(1B]|QsЩ.jS5 hgޗ@`rVل%[.'Hx/l=򷛕>3tl&ng6x)$Z۬O/ȗ^,]g}Y ΡNY_I昨઺dbV)P6q_Z*}ZLY-a7g$m݃-kƴ;@ {L`ޯNm5 w MUݬwmu6a޷Mߣ ]7gisfmp2qծ֣Z.u<9vt=;sFDigUJ-p5U>ݐN_xbȸqtH?(;_i9yQ1-j`'K]L 4$*_lY>C]oNWB}̹hl|]gLU:md /6d}PW^ؼb h?A{"ƧAANp^^D]\W5n+V}ąNXꢸUF޳!t cn5&D=Yf785jgJ/ 2n(uv׆U_;kJm3>2o'=B{!6W>U՝9cE3ͺҊ8U=d8;cuxŻN1uX"

 r{!l)En6YZlD"iiڸH ZYyLNW+|˘d=S˥z$5&.QE+Wjԩ*#ǖz38"XKk閪I&ԅ0XÆI+gW^UlPăyX# UVDR7ggh@s+3xpW_ocK5`ug)|l-w}x/{jQ ?_l.DzkVNzZ\XN t6m+p-[v>-I?N%izi{]#lm5z4Y+jD!WTj 2[G -[#&i0Fyv8XHhFm޽BntS -:X:0JrVk_'WׯO՟Ffg?w0)ɹܹj9ޖ˂qrhfC?[z) MSaOb99BnRVۢ9R!gRA!#ZQ?Z1#ui3XUcLm-l[~9~=it3⭓4_ͼB"eSH=2iehkzjjnx^Rz˹e[o-oBUF?c1: %V=CUڪ5QiJU[d36>{Vޅ:PbF+bReifjkQe-5u/-MsN[*31nCti6rc&ֱ"Y 'f~+7ݒ-bx_⢔GYtH4L(gEhj Iz)'-M6+$W?7QwejshtV+1Z&Fa-"Q)YYJImjdyu])JKE#bg$67>1( 9rhTQZ떫]2JF-Q˞C^-r9ɞnbJ.M\ 4vD|0_Lp le,59"+GF1-ڶ k>t& ֐q1J^%ƌӐZE*fdW~U+o~`P q̹Y ְZY6Pޱ:.-k\FjWF5j(>Znje>7 8Xœvyso˶*[ tYI5G4-5R9M5iڰ.>"}5~jO:"t6l:U9Iъ7ߐG+2@ {3m=rf3Ï-gG:?yD˳KjN4hѲLw\Нp]vR'O[_sI-% }ֺ%p:{{ӝi{ovڶ-9:N7x嗕"F% Pч6RQ&We>'Ek%Ct>{2=+: qj}m:B1+twpGU9D}(%yRzƞ{/\5I9@]#W!9\3j+ּ!chɌ5QrtUWoxMNIԲn飹ZYţP|^S޶mhX͕0b"M`2WeDQkѐ׵٭+܄ڜ>'^K6|L_-6kE+Ur$tڦOW-F޲\3q?TwSU|;ZIH r/]rp⻯U|LP/Z;i}{}]*8!vPu8W5<oe"3-vYm@^^59c,\N_4M~զOVO6Gj˛j]C6s-K|㊸lUƷ-k:5C_ TT0Awl[$}~qw7ۖe;}TVی-:)ZM+cA52J-| %:C{SӤ~J9o㎨Stkg}#]iyeJ}}Sw.999j۶[>Fk/wԾ[ ع\KSܶڔiW1eVDj+[6Pt0 Uo|/4i/4J,`LWx&b:iU=7_fGXM#' V7x|LY+?Ց>c'鶫j?+FJ?wz4muvR`M-{oV~\Cao*Cӌ'pN'muP6ڷekȶY])TX`qں⏚?&hÃ=q[ B"_O7'g$dm(f8@'oV|J}SrXe $7QDhSeei_H+{L,n{l$%& y +tq3D]~|GUW!ҭךh}Zvp%ZuUqaFD:+2R99iAZ]A+_lr/g+ 3 diex"T}G [#ބ6k^H}([yՙΪ*iue[*nS^.6PlCp5SUem6JMC#|!Ym5dX/uo^2S{ˮ|_[[71h!1)knX'ziHӑѾH>?DnTn)q }?mnUj_~󪡷 uBX{M4N13 s *OK1YbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(ɓ'S2bx޼yn PP nb@ F(hb)p_~\j Ķl?P ѢE 8p-+[~|4i@-JJJ}v8AAAtb:(sP6;GukaL/ZjE JbgE(xYbgE(xYbg5:í)NEwABqII;SݧE(xYbgE(xY\꡴Tu1.M4QբE .u p6ܹS͚5s&9p(..v+,,Tǎ Ⱦ}@m۶ukҵw^8qBaaan o/ /aekʣ?;5137jIzhK=kJO3U켗n}@4CjhZ:fRzf} zjax@#cnn㜁;MwաΩ:tz>krL~==ڴlZzA%\<>}Z hnݪ+b m1oԵkWυ 1ֱ/{4w*5C3QL^JH˘1N͵Y+V&-Wc^]*8!T3Z|ؖ{IL_yDw,t?=6^3-պcz1~x@oV1i`K NwJ9w5i@ 4?Gog$.BUA8FL?wJ̿J"Fi=D/j\76&~YgrY0.[ b#bУZ|kn>R3Z@E>W'8p6f+)1 H:>3#}>Ck;Mѫgj XG:~ ߤ?~]?r2/طOQQ MޖmU6 Ŏ^{ b@3z'֥@[/~7*.wN5sSiԕZyڝuT}&4!&9}K <|l,"v M:?'2%9쫻՞LcSOߩ~hJrc[xg\|V:Owu>At\}{qo~|lcx_7&IeE>PMmWvg~>Mw uTɲ7o|Ln+xu_ٿ *pы9N;Q[.W  8Xi ;qso˶*[ t6ɥjBp{pXIS^]OHT=1)Tv %Wsmzewȷ7i AuEkUm@S7WkL9[]gjϿ&&(~\Bx^%$Gq-iZ\F' w))995]PΞc~%edd(::-dC\vYl nv]I/\㛧B1 B1P KG]m @CC(.d(K2pE(xY~$'*)[کOJT6(=wmKV{ikv ޙ, ~Ww߯NZPܮu3~+ߣ'vMZ]K_{k4FmSrb?uWlP@yrnT1 w9q¤SAZGwZNcji˛J  }bRgꫝ'ޭm6e]?9Q=M; VlU`diamxտ);< KhTҶul狀mvNSW;u"P}m۶n .}999 sK.2ws۩uwsD;s+d Cwf[iY#VkWG8byG`mj$MqfiB}VTl ҀmGʳ~?S{Zk#>.>M?QWjO`5.>]u:.Yl&~.|CynP|y7wiZz澝ݎYWeemOjv ݞ]ku.sru&`PnamK~Ko%:\<L",\}:7\›]2Ӕㄢ~v4.Qn{fz/cN~1vWյoM'8]ع;wVjZP\*0} TӖΆާ5Qn;UsS}iOBqM;WuGu45&Raqk -ejuC|Ӓkowhi v:|n;EZ]ᷦfs %%,v̮M-Y7vrh@`s$'Xjٮ 4s>:vT\n'yZ-;+<;mk-n:2w~oUֶ>H@_h}ԭ wDބև\'l8l[m}Z벝rvٷձo%_}҉ݧZбcknrZOOWRGݧVG(7+}2\Yy 4.(׌3K oB LN &k 7^͋>Rn)-jGo)imxnpzvi~O'LF2IRFaqZ~JJ׎բfx? 󾗨wtٙQ^u\{,^N݅n[L6`Z*鞝#P1ѾVpIPwtcʴGznVnF' w)))K8;vL5 [.,-/##Cn O`{R F($GJ YbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgb8͛n _]زg͚5sR;}ޔSp)--Ν;-Z(((ȝBlqaa:v쨀w@w#@=`~`رcn-\:li^˖- Y2sN1"

uo#ە`o:dicMUZ{)yoqմk֗c4n+mO^7IvL]93+)պтMRPzt@ (Un\=pS_OVcIyj8EmzWku0뼢lʄ)'~Bm|gۧО9qb_'>UO%iy5a^ES U;l)[6;ɂU>YͮRb>I//4VסMTnl= qWf'T]9b;NoR{EN u.H5~$>Ws/ߧMJz~6Fo@lk@:/U_ΟzK3 ү^y/WO[.تM~Kw<3qjykR+ ի7 5QjWv(ٷ2va~A|+SjT\#>TUZYo {&Fh]aM7El ~t:}f7&2f_+n{ƿ`/vwhVs{ndlZgb;xϥB̶^N+*5شPss\$U[)j(7tGu01WJ1i<ȷ2|?A&,C5iIΕYO@6+l:>kv׾mCrGolNO7V7Ә^Ы4Ja'3R+l25"*< 0Y+kuA/ko腇-=T}ں@נBqVSugl`ۅ՗ҫgڪ:V1خv}꥗^e$(|arkZ$[=էwl io:T*]#d*NyJe7Ru2pr5s=z0)OrmbԠB*֞]y@@R[=u@sLs~pz=zyD޸1 ں޽w.oAqnB[zc$ؤ|lk]j>޳~O{V(Vb=8|+}o9gLNCq=v}n^njPV>3Q:m^.M|F>bV s0lo(ęո}^AeSqM9(W:ևՠAOh8=T}?NqÔNzOѼ}Xk)Ϣ4h)xYbguA:p;h:.H(...v8 w4"

]>X!Nm 3>l s4l帴2i5}Gd̈́7ш'Ho}Qק/斃5}fٙvcMaV%ͼzdh}Jk۶]彍g/_*.g+s|n>e?&í +fX3}R*MX ۃEg0jt-q~}u-JߧjNk܄8=Ou\Zx2 e4g@)Ӱxjw<-Ԑ _9SӳTLU']ڣjoyc͙9@9s^##a޳Z͝aStl'g)}Sul}X#ژbħ.\8MYԀ?RXج g mSҼ8=iόSpuSfR}E9+L4zk޳V*򸴲zeE}2_OӪEWs.]Tǿ{^5oA_(\ϾvT6|a"q._Yg=DP'󹐵]9Kߪ} P˗eRV2@wpH~"[JWb:BWC>vU\DžNwCqj 勚]_uUBm4`@;~=xhE۷B&$(*u5@C`ŗ,+U[Y˙SA63 bG#.ho6?er _&v{tmSǗeg Pv*fz^O2v՝&[˦J:(}JǏUr\ZYmǢ5ӊgej=5:vP/8&lpmUr{=@a=ԣeV6}a ivrN{OnUpAu6a޷Mߣ 5Ξۅzs6n'W:= Za=zB]\nggNЈ l^^\Xݧ9 |ѧ#iҵ"wAEo 쫁̛/bw]?z31XE-H??];9 uҶ]ͽ,'T.ԕ(6?M-uԳ7hV$T"(i˫ƍ}Ūq陸Pi"K]׽({6d߲ᡵ hݵ',}'jgJ/ 2n(umvݼCkên)?S6YǷz.Y*.:Gl3qEC.$B.]̟Z.tHVgf'u7 ء=5j&k/ƃ !7HVB}y=KBmPlޜg. nzn})+ {LѪ]qZq&L׬E;aWthB¬:˔Yѥ"vɎ\|&$PmojYY+T}WPs>̍7v6ʻN;^؏OEA'WP{sq%Ϛnf-5"n]_zO%$w rKn\=Fhr\c1SDiL鳴&? ,B1"

EWuPeŶ@C`?A(nblmmZP<\XVe9ZV5oI[jjksTy=_V,iuᑿ^? =. yQ7wolύ>4M8[W`\S裏[v|xBqTZ5P11 4UOӖEj@S7$^?\ǫݢEw@(~Vl b3X?OkrQ;\FCg)wX5VZ\v?ኟL9-ijg;Ӎ۴™l{%D=sULarvRm!;1[?_>\)hDAV|2V2P0U1[i(1탭̷]}>k"P?Sp}RиMLh6'̾O~X8~\i{oel9x%cXr'UjJ{"Rp5Z1#uiM\XUcLmml`EܲKƔ?Zm)Xݢ# 4,&RMdRgD@'87Qtƕ8T\+}k[ ի3nx^Rz˹e[o-ojpݧ5Z TъغT)iZ1BcThFٺDko-Ouo/K*=W$D m-prLE&n}}"d"3gˆwK>\mhˤbتWE#Fdb4hM,Dzҗ%_ڿ.ۋ0aWTg^6nkHϭuK5REWue!mcxB9]>t&7tkA.RV%jEJq.ON}J"%XVf8s켁)cJmqKۛeOUF=\7Nθ- $-%(4fj ;{[VrSln\;BmٮuzN5Z8?y#jڏqȽZ7ԝS݆+5qZNnF4BϾ/];ݓ󄆛L^a9f_F3\9a{ӧ;ޖm-.[N\jISso222ԯ_]L-1RIb}:FinkQe-׳s*;.W'g:qϩyRRf~:U޷\-%cs]%cl;n^2C 3v׵d5wМs `C\vYl 2EÆ5"vS|w"LQ~>5)+HNf/ѨeYJܜ'%^}u2Y3Yg2s9grO+elDCA 45#knƮtV-K3A((m}@%yfMzx|g!L~^~u5$RC4jl6E5JNp\ct˴RA]6ҾF*viaZ3+Lm~kE}a͖m=@(ًa9EKgS7hrd˝.ѨFԨ6jB-We^Gu,q_69i=G2|<);ssAٸSs;֤f8.v:g:}*sm3^LJmSHdݖ%sxm++Z6%hRm!񓕒LGs*LOLY#hpk5E#5k/я ݺ:ݱʝ]iHb$ޭswMPw\ٷ9f_c(ef%ݧ'~64dj|eM^k>ևoT 4ҹ6}~oj,eUE+a]HIpK8ZWHdK2mp^_! NHy9*,To-Wh?-?"?uTe]*eMNp;}4A6V>RZmm֛],o[TDQZ9:o3Hhu6K>N[-( /( mO`'1rj)ϗ)kyFGM^ބM轏ӔꠦvS=O[xLNNڶmnHw\N_]ձl?SܶڔiW1eVDj+[6Pt0 Uo|/4i/4J,`LWx&b:iU1S] #_V,覑+H1ftUGiE=6U[ʺvǃ{m7(8;]v)ZMs ݧ!ju7}PO͹v4l+4a񓁶:CBQdd[u,VN.*Cɸhoam]G͟G:Kt`Aƞܸ]L{sL!X/S^Ƨ3z{r]ͶKbZ^t8zEZ֧6jdq{U|xVqq)9]77w;7m*Ojߦy)? MjBaVTiٙ:XF{VI-6a]+t&S.?CcŠѪʼnZu˜Z?kM\=K+3ijS8|~sIVBU\XThNrtmv{,/[zJ:GmYZ66Ud~zBy7aaڸW6R/VvD%dZ]VAʷǔw}8S~mnft;&JMC#|!Ym5dX/uo^2S{Vh }mUoǬI\Ŷbe!5OGG";`EQa{G4㻩I˕R+߷Ui;DzPf 2su 8:P s pE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbgE(xYbl#͛P p1\X@9_b-3/b~E:p[WW \F' wJIIo߮@2((ȝBlC@w5ʣGp]{Z""y~wR njwx0'D?_.R j)~:G;ڣ/KG׶Wx.#ݳ| ߠ )/׍mǵg?kηYpn}7U?o0ZbEKG=^2uWpH 'mo}Fz _on[J:]q .iռOiQnO_c52=Y-ު6#wNA(K?{O\O+mEvسҶAIǔ3߄Jc9tl6Eh@GiۦnZ=Q#nح4`_. .. code-block:: python from constance import config from constance.test import override_config from django.test import TestCase @override_config(YOUR_NAME="Arthur of Camelot") class ExampleTestCase(TestCase): def test_what_is_your_name(self): self.assertEqual(config.YOUR_NAME, "Arthur of Camelot") @override_config(YOUR_QUEST="To find the Holy Grail") def test_what_is_your_quest(self): self.assertEqual(config.YOUR_QUEST, "To find the Holy Grail") def test_what_is_your_favourite_color(self): with override_config(YOUR_FAVOURITE_COLOR="Blue?"): self.assertEqual(config.YOUR_FAVOURITE_COLOR, "Blue?") Pytest usage ~~~~~~~~~~~~ Django-constance provides pytest plugin that adds marker :class:`@pytest.mark.override_config()`. It handles config override for module/class/function, and automatically revert any changes made to the constance config values when test is completed. .. py:function:: pytest.mark.override_config(**kwargs) Specify different config values for the marked tests in kwargs. Module scope override .. code-block:: python pytestmark = pytest.mark.override_config(API_URL="/awesome/url/") def test_api_url_is_awesome(): ... Class/function scope .. code-block:: python from constance import config @pytest.mark.override_config(API_URL="/awesome/url/") class SomeClassTest: def test_is_awesome_url(self): assert config.API_URL == "/awesome/url/" @pytest.mark.override_config(API_URL="/another/awesome/url/") def test_another_awesome_url(self): assert config.API_URL == "/another/awesome/url/" If you want to use override as a context manager or decorator, consider using .. code-block:: python from constance.test.pytest import override_config def test_override_context_manager(): with override_config(BOOL_VALUE=False): ... # or @override_config(BOOL_VALUE=False) def test_override_context_manager(): ... Pytest fixture as function or method parameter ( NOTE: no import needed as fixture is available globally) .. code-block:: python def test_api_url_is_awesome(override_config): with override_config(API_URL="/awesome/url/"): ... Any scope, auto-used fixture alternative can also be implemented like this .. code-block:: python @pytest.fixture(scope='module', autouse=True) # e.g. module scope def api_url(override_config): with override_config(API_URL="/awesome/url/"): yield Memory backend ~~~~~~~~~~~~~~ If you don't want to rely on any external services such as Redis or database when running your unittests you can select :class:`MemoryBackend` for a test Django settings file .. code-block:: python CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend' It will provide simple thread-safe backend which will reset to default values after each test run. django-constance-3.1.0/example/000077500000000000000000000000001447075242200163725ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/000077500000000000000000000000001447075242200205205ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/__init__.py000066400000000000000000000000001447075242200226170ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/000077500000000000000000000000001447075242200214635ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/__init__.py000066400000000000000000000000001447075242200235620ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/catalog/000077500000000000000000000000001447075242200230755ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/catalog/__init__.py000066400000000000000000000000001447075242200251740ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/catalog/admin.py000066400000000000000000000001561447075242200245410ustar00rootroot00000000000000from django.contrib import admin from cheeseshop.apps.catalog.models import Brand admin.site.register(Brand) django-constance-3.1.0/example/cheeseshop/apps/catalog/migrations/000077500000000000000000000000001447075242200252515ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/catalog/migrations/0001_initial.py000066400000000000000000000006371447075242200277220ustar00rootroot00000000000000from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Brand', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=75)), ], ), ] django-constance-3.1.0/example/cheeseshop/apps/catalog/migrations/__init__.py000066400000000000000000000000001447075242200273500ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/catalog/models.py000066400000000000000000000001451447075242200247320ustar00rootroot00000000000000from django.db import models class Brand(models.Model): name = models.CharField(max_length=75) django-constance-3.1.0/example/cheeseshop/apps/storage/000077500000000000000000000000001447075242200231275ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/storage/__init__.py000066400000000000000000000000001447075242200252260ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/storage/admin.py000066400000000000000000000002221447075242200245650ustar00rootroot00000000000000from django.contrib import admin from cheeseshop.apps.storage.models import Shelf, Supply admin.site.register(Shelf) admin.site.register(Supply) django-constance-3.1.0/example/cheeseshop/apps/storage/migrations/000077500000000000000000000000001447075242200253035ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/storage/migrations/0001_initial.py000066400000000000000000000015461447075242200277540ustar00rootroot00000000000000from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Shelf', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=75)), ], options={ 'verbose_name_plural': 'shelves', }, ), migrations.CreateModel( name='Supply', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=75)), ], options={ 'verbose_name_plural': 'supplies', }, ), ] django-constance-3.1.0/example/cheeseshop/apps/storage/migrations/__init__.py000066400000000000000000000000001447075242200274020ustar00rootroot00000000000000django-constance-3.1.0/example/cheeseshop/apps/storage/models.py000066400000000000000000000004401447075242200247620ustar00rootroot00000000000000from django.db import models class Shelf(models.Model): name = models.CharField(max_length=75) class Meta: verbose_name_plural = 'shelves' class Supply(models.Model): name = models.CharField(max_length=75) class Meta: verbose_name_plural = 'supplies' django-constance-3.1.0/example/cheeseshop/fields.py000066400000000000000000000011011447075242200223310ustar00rootroot00000000000000import json from django.forms import fields, widgets class JsonField(fields.CharField): widget = widgets.Textarea def __init__(self, rows: int = 5, **kwargs): self.rows = rows super().__init__(**kwargs) def widget_attrs(self, widget: widgets.Widget): attrs = super().widget_attrs(widget) attrs['rows'] = self.rows return attrs def to_python(self, value): if value: return json.loads(value) else: return {} def prepare_value(self, value): return json.dumps(value) django-constance-3.1.0/example/cheeseshop/settings.py000066400000000000000000000106151447075242200227350ustar00rootroot00000000000000""" Django settings for cheeseshop project. For more information on this file, see https://docs.djangoproject.com/en/4.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.1/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os from datetime import date BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ SITE_ID = 1 # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'hdx64#m+lnc_0ffoyehbk&7gk1&*9uar$pcfcm-%$km#p0$k=6' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'cheeseshop.apps.catalog', 'cheeseshop.apps.storage', 'constance', ) MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) ROOT_URLCONF = 'cheeseshop.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'cheeseshop.wsgi.application' # Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', #'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'NAME': '/tmp/cheeseshop.db', } } CONSTANCE_REDIS_CONNECTION = { 'host': 'localhost', 'port': 6379, 'db': 0, } CONSTANCE_ADDITIONAL_FIELDS = { 'yes_no_null_select': [ 'django.forms.fields.ChoiceField', { 'widget': 'django.forms.Select', 'choices': ((None, "-----"), ("yes", "Yes"), ("no", "No")) } ], 'email': ('django.forms.fields.EmailField',), 'json_field': ['cheeseshop.fields.JsonField'], 'image_field': ['django.forms.ImageField', {}], } CONSTANCE_CONFIG = { 'BANNER': ('The National Cheese Emporium', 'name of the shop'), 'OWNER': ('Mr. Henry Wensleydale', 'owner of the shop'), 'OWNER_EMAIL': ('henry@example.com', 'contact email for owner', 'email'), 'MUSICIANS': (4, 'number of musicians inside the shop'), 'DATE_ESTABLISHED': (date(1972, 11, 30), "the shop's first opening"), 'MY_SELECT_KEY': ('yes', 'select yes or no', 'yes_no_null_select'), 'MULTILINE': ('Line one\nLine two', 'multiline string'), 'JSON_DATA': ( {'a': 1_000, 'b': 'test', 'max': 30_000_000}, 'Some test data for json', 'json_field', ), 'LOGO': ( '', 'Logo image file', 'image_field', ), } CONSTANCE_CONFIG_FIELDSETS = { 'Cheese shop general info': [ 'BANNER', 'OWNER', 'OWNER_EMAIL', 'MUSICIANS', 'DATE_ESTABLISHED', 'LOGO', ], 'Awkward test settings': ['MY_SELECT_KEY', 'MULTILINE', 'JSON_DATA'], } CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', 'LOCATION': '127.0.0.1:11211', } } CONSTANCE_DATABASE_CACHE_BACKEND = 'default' # Internationalization # https://docs.djangoproject.com/en/4.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'America/Chicago' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.1/howto/static-files/ STATIC_URL = '/static/' MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') CONSTANCE_FILE_ROOT = 'constance' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' django-constance-3.1.0/example/cheeseshop/urls.py000066400000000000000000000004711447075242200220610ustar00rootroot00000000000000from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib import admin from django.conf import settings from django.urls import re_path admin.autodiscover() urlpatterns = [ re_path('admin/', admin.site.urls), ] if settings.DEBUG: urlpatterns += staticfiles_urlpatterns() django-constance-3.1.0/example/cheeseshop/wsgi.py000066400000000000000000000002531447075242200220430ustar00rootroot00000000000000import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cheeseshop.settings") application = get_wsgi_application() django-constance-3.1.0/example/manage.py000077500000000000000000000003751447075242200202040ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cheeseshop.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-constance-3.1.0/example/requirements.txt000066400000000000000000000000361447075242200216550ustar00rootroot00000000000000Django>=3.2 Pillow pymemcache django-constance-3.1.0/setup.py000066400000000000000000000045451447075242200164610ustar00rootroot00000000000000import os import re import codecs from setuptools import setup, find_packages def read(*parts): filename = os.path.join(os.path.dirname(__file__), *parts) with codecs.open(filename, encoding='utf-8') as fp: return fp.read() def find_version(*file_paths): version_file = read(*file_paths) version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") setup( name='django-constance', version=find_version("constance", "__init__.py"), url="https://github.com/jazzband/django-constance", description='Django live settings with pluggable backends, including Redis.', long_description=read('README.rst'), author='Jannis Leidel', author_email='jannis@leidel.info', license='BSD', keywords='django libraries settings redis'.split(), platforms=['any'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', 'Framework :: Django :: 4.1', 'Framework :: Django :: 4.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', ], packages=find_packages(exclude=['tests', 'tests.*']), include_package_data=True, zip_safe=False, python_requires='>=3.7', install_requires=[ 'django-picklefield', ], extras_require={ 'redis': ['redis'], }, entry_points={ 'pytest11': [ 'pytest-django-constance = constance.test.pytest', ], }, ) django-constance-3.1.0/tests/000077500000000000000000000000001447075242200161015ustar00rootroot00000000000000django-constance-3.1.0/tests/__init__.py000066400000000000000000000000001447075242200202000ustar00rootroot00000000000000django-constance-3.1.0/tests/backends/000077500000000000000000000000001447075242200176535ustar00rootroot00000000000000django-constance-3.1.0/tests/backends/__init__.py000066400000000000000000000000001447075242200217520ustar00rootroot00000000000000django-constance-3.1.0/tests/backends/test_database.py000066400000000000000000000013411447075242200230270ustar00rootroot00000000000000from django.test import TestCase from constance import settings from tests.storage import StorageTestsMixin class TestDatabase(StorageTestsMixin, TestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = 'constance.backends.database.DatabaseBackend' super().setUp() def test_database_queries(self): # Read and set to default value with self.assertNumQueries(5): self.config.INT_VALUE # Read again with self.assertNumQueries(1): self.config.INT_VALUE # Set value with self.assertNumQueries(2): self.config.INT_VALUE = 15 def tearDown(self): settings.BACKEND = self.old_backend django-constance-3.1.0/tests/backends/test_memory.py000066400000000000000000000007311447075242200225750ustar00rootroot00000000000000from django.test import TestCase from constance import settings from tests.storage import StorageTestsMixin class TestMemory(StorageTestsMixin, TestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = 'constance.backends.memory.MemoryBackend' super().setUp() self.config._backend._storage = {} def tearDown(self): self.config._backend._storage = {} settings.BACKEND = self.old_backend django-constance-3.1.0/tests/backends/test_redis.py000066400000000000000000000011261447075242200223720ustar00rootroot00000000000000from django.test import TestCase from constance import settings from tests.storage import StorageTestsMixin class TestRedis(StorageTestsMixin, TestCase): _BACKEND = 'constance.backends.redisd.RedisBackend' def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = self._BACKEND super().setUp() self.config._backend._rd.clear() def tearDown(self): self.config._backend._rd.clear() settings.BACKEND = self.old_backend class TestCachingRedis(TestRedis): _BACKEND = 'constance.backends.redisd.CachingRedisBackend' django-constance-3.1.0/tests/redis_mockup.py000066400000000000000000000002331447075242200211350ustar00rootroot00000000000000class Connection(dict): def set(self, key, value): self[key] = value def mget(self, keys): return [self.get(key) for key in keys] django-constance-3.1.0/tests/settings.py000066400000000000000000000056731447075242200203260ustar00rootroot00000000000000from datetime import datetime, date, time, timedelta from decimal import Decimal SECRET_KEY = 'cheese' MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) DATABASE_ENGINE = 'sqlite3' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', }, 'secondary': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.staticfiles', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'constance', 'constance.backends.database', ) ROOT_URLCONF = 'tests.urls' CONSTANCE_REDIS_CONNECTION_CLASS = 'tests.redis_mockup.Connection' CONSTANCE_ADDITIONAL_FIELDS = { 'yes_no_null_select': [ 'django.forms.fields.ChoiceField', { 'widget': 'django.forms.Select', 'choices': ((None, "-----"), ("yes", "Yes"), ("no", "No")) } ], # note this intentionally uses a tuple so that we can test immutable 'email': ('django.forms.fields.EmailField',), } USE_TZ = True CONSTANCE_CONFIG = { 'INT_VALUE': (1, 'some int'), 'BOOL_VALUE': (True, 'true or false'), 'STRING_VALUE': ('Hello world', 'greetings'), 'DECIMAL_VALUE': (Decimal('0.1'), 'the first release version'), 'DATETIME_VALUE': (datetime(2010, 8, 23, 11, 29, 24), 'time of the first commit'), 'FLOAT_VALUE': (3.1415926536, 'PI'), 'DATE_VALUE': (date(2010, 12, 24), 'Merry Chrismas'), 'TIME_VALUE': (time(23, 59, 59), 'And happy New Year'), 'TIMEDELTA_VALUE': (timedelta(days=1, hours=2, minutes=3), 'Interval'), 'CHOICE_VALUE': ('yes', 'select yes or no', 'yes_no_null_select'), 'LINEBREAK_VALUE': ('Spam spam', 'eggs\neggs'), 'EMAIL_VALUE': ('test@example.com', 'An email', 'email'), } DEBUG = True STATIC_ROOT = './static/' STATIC_URL = '/static/' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.request', 'django.template.context_processors.static', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'constance.context_processors.config', ], }, }, ] django-constance-3.1.0/tests/storage.py000066400000000000000000000103561447075242200201240ustar00rootroot00000000000000from datetime import datetime, date, time, timedelta from decimal import Decimal from constance import settings from constance.base import Config class StorageTestsMixin: def setUp(self): self.config = Config() super().setUp() def test_store(self): self.assertEqual(self.config.INT_VALUE, 1) self.assertEqual(self.config.BOOL_VALUE, True) self.assertEqual(self.config.STRING_VALUE, 'Hello world') self.assertEqual(self.config.DECIMAL_VALUE, Decimal('0.1')) self.assertEqual(self.config.DATETIME_VALUE, datetime(2010, 8, 23, 11, 29, 24)) self.assertEqual(self.config.FLOAT_VALUE, 3.1415926536) self.assertEqual(self.config.DATE_VALUE, date(2010, 12, 24)) self.assertEqual(self.config.TIME_VALUE, time(23, 59, 59)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=1, hours=2, minutes=3)) self.assertEqual(self.config.CHOICE_VALUE, 'yes') self.assertEqual(self.config.EMAIL_VALUE, 'test@example.com') # set values self.config.INT_VALUE = 100 self.config.BOOL_VALUE = False self.config.STRING_VALUE = 'Beware the weeping angel' self.config.DECIMAL_VALUE = Decimal('1.2') self.config.DATETIME_VALUE = datetime(1977, 10, 2) self.config.FLOAT_VALUE = 2.718281845905 self.config.DATE_VALUE = date(2001, 12, 20) self.config.TIME_VALUE = time(1, 59, 0) self.config.TIMEDELTA_VALUE = timedelta(days=2, hours=3, minutes=4) self.config.CHOICE_VALUE = 'no' self.config.EMAIL_VALUE = 'foo@bar.com' # read again self.assertEqual(self.config.INT_VALUE, 100) self.assertEqual(self.config.BOOL_VALUE, False) self.assertEqual(self.config.STRING_VALUE, 'Beware the weeping angel') self.assertEqual(self.config.DECIMAL_VALUE, Decimal('1.2')) self.assertEqual(self.config.DATETIME_VALUE, datetime(1977, 10, 2)) self.assertEqual(self.config.FLOAT_VALUE, 2.718281845905) self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20)) self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=2, hours=3, minutes=4)) self.assertEqual(self.config.CHOICE_VALUE, 'no') self.assertEqual(self.config.EMAIL_VALUE, 'foo@bar.com') def test_nonexistent(self): try: self.config.NON_EXISTENT except Exception as e: self.assertEqual(type(e), AttributeError) try: self.config.NON_EXISTENT = 1 except Exception as e: self.assertEqual(type(e), AttributeError) def test_missing_values(self): # set some values and leave out others self.config.BOOL_VALUE = False self.config.DECIMAL_VALUE = Decimal('1.2') self.config.DATETIME_VALUE = datetime(1977, 10, 2) self.config.DATE_VALUE = date(2001, 12, 20) self.config.TIME_VALUE = time(1, 59, 0) self.assertEqual(self.config.INT_VALUE, 1) # this should be the default value self.assertEqual(self.config.BOOL_VALUE, False) self.assertEqual(self.config.STRING_VALUE, 'Hello world') # this should be the default value self.assertEqual(self.config.DECIMAL_VALUE, Decimal('1.2')) self.assertEqual(self.config.DATETIME_VALUE, datetime(1977, 10, 2)) self.assertEqual(self.config.FLOAT_VALUE, 3.1415926536) # this should be the default value self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20)) self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=1, hours=2, minutes=3)) def test_backend_retrieves_multiple_values(self): # Check corner cases such as falsy values self.config.INT_VALUE = 0 self.config.BOOL_VALUE = False self.config.STRING_VALUE = '' values = dict(self.config._backend.mget(settings.CONFIG)) self.assertEqual(values['INT_VALUE'], 0) self.assertEqual(values['BOOL_VALUE'], False) self.assertEqual(values['STRING_VALUE'], '') def test_backend_does_not_return_none_values(self): result = dict(self.config._backend.mget(settings.CONFIG)) self.assertEqual(result, {}) django-constance-3.1.0/tests/test_admin.py000066400000000000000000000232251447075242200206060ustar00rootroot00000000000000from datetime import datetime from unittest import mock from django.contrib import admin from django.contrib.auth.models import User, Permission from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.template.defaultfilters import linebreaksbr from django.test import TestCase, RequestFactory from django.utils.translation import gettext_lazy as _ from constance import settings from constance.admin import Config from constance.utils import get_values from constance.forms import ConstanceForm from unittest import mock class TestAdmin(TestCase): model = Config def setUp(self): super().setUp() self.rf = RequestFactory() self.superuser = User.objects.create_superuser('admin', 'nimda', 'a@a.cz') self.normaluser = User.objects.create_user('normal', 'nimda', 'b@b.cz') self.normaluser.is_staff = True self.normaluser.save() self.options = admin.site._registry[self.model] def test_changelist(self): self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertEqual(response.status_code, 200) def test_custom_auth(self): settings.SUPERUSER_ONLY = False self.client.login(username='normal', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.normaluser self.assertRaises(PermissionDenied, self.options.changelist_view, request, {}) self.assertFalse(request.user.has_perm('constance.change_config')) # reload user to reset permission cache request = self.rf.get('/admin/constance/config/') request.user = User.objects.get(pk=self.normaluser.pk) request.user.user_permissions.add(Permission.objects.get(codename='change_config')) self.assertTrue(request.user.has_perm('constance.change_config')) response = self.options.changelist_view(request, {}) self.assertEqual(response.status_code, 200) def test_linebreaks(self): self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, 'LINEBREAK_VALUE') self.assertContains(response, linebreaksbr('eggs\neggs')) @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'Numbers': ('INT_VALUE',), 'Text': ('STRING_VALUE',), }) def test_fieldset_headers(self): self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, '

Numbers

') self.assertContains(response, '

Text

') @mock.patch('constance.settings.CONFIG_FIELDSETS', ( ('Numbers', ('INT_VALUE',)), ('Text', ('STRING_VALUE',)), )) def test_fieldset_tuple(self): self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, '

Numbers

') self.assertContains(response, '

Text

') @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'Numbers': { 'fields': ('INT_VALUE', 'DECIMAL_VALUE',), 'collapse': True, }, 'Text': { 'fields': ('STRING_VALUE', 'LINEBREAK_VALUE',), 'collapse': True, }, }) def test_collapsed_fieldsets(self): self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, 'module collapse') @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'FieldSetOne': ('INT_VALUE',) }) @mock.patch('constance.settings.CONFIG', { 'INT_VALUE': (1, 'some int'), }) @mock.patch('constance.settings.IGNORE_ADMIN_VERSION_CHECK', True) @mock.patch("constance.forms.ConstanceForm.save", lambda _: None) @mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True) def test_submit(self): """ Test that submitting the admin page results in an http redirect when everything is in order. """ initial_value = {"INT_VALUE": settings.CONFIG['INT_VALUE'][0]} self.client.login(username='admin', password='nimda') request = self.rf.post('/admin/constance/config/', data={ **initial_value, "version": "123", }) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message") as mock_message: with mock.patch.object(ConstanceForm, "__init__", **initial_value, return_value=None ) as mock_form: response = self.options.changelist_view(request, {}) mock_form.assert_called_with( data=request.POST, files=request.FILES, initial=initial_value, request=request ) mock_message.assert_called_with( request, 25, _('Live settings updated successfully.'), ) self.assertIsInstance(response, HttpResponseRedirect) @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'FieldSetOne': ('MULTILINE',) }) @mock.patch('constance.settings.CONFIG', { 'MULTILINE': ('Hello\nWorld', 'multiline value'), }) @mock.patch('constance.settings.IGNORE_ADMIN_VERSION_CHECK', True) def test_newlines_normalization(self): self.client.login(username='admin', password='nimda') request = self.rf.post('/admin/constance/config/', data={ "MULTILINE": "Hello\r\nWorld", "version": "123", }) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) self.assertEqual(get_values()['MULTILINE'], 'Hello\nWorld') @mock.patch('constance.settings.CONFIG', { 'DATETIME_VALUE': (datetime(2019, 8, 7, 18, 40, 0), 'some naive datetime'), }) @mock.patch('constance.settings.IGNORE_ADMIN_VERSION_CHECK', True) @mock.patch('tests.redis_mockup.Connection.set', mock.MagicMock()) def test_submit_aware_datetime(self): """ Test that submitting the admin page results in an http redirect when everything is in order. """ request = self.rf.post('/admin/constance/config/', data={ "DATETIME_VALUE_0": "2019-08-07", "DATETIME_VALUE_1": "19:17:01", "version": "123", }) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'Numbers': ('INT_VALUE',), 'Text': ('STRING_VALUE',), }) def test_inconsistent_fieldset_submit(self): """ Test that the admin page warns users if the CONFIG_FIELDSETS setting doesn't account for every field in CONFIG. """ self.client.login(username='admin', password='nimda') request = self.rf.post('/admin/constance/config/', data=None) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertContains(response, 'is missing field(s)') @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'Fieldsets': ('STRING_VALUE', 'INT_VALUE',), }) def test_fieldset_ordering_1(self): """Ordering of inner list should be preserved""" self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content_str = response.content.decode() self.assertGreater( content_str.find('INT_VALUE'), content_str.find('STRING_VALUE') ) @mock.patch('constance.settings.CONFIG_FIELDSETS', { 'Fieldsets': ('INT_VALUE', 'STRING_VALUE',), }) def test_fieldset_ordering_2(self): """Ordering of inner list should be preserved""" self.client.login(username='admin', password='nimda') request = self.rf.get('/admin/constance/config/') request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content_str = response.content.decode() self.assertGreater( content_str.find('STRING_VALUE'), content_str.find('INT_VALUE') ) def test_labels(self): self.assertEqual(type(self.model._meta.label), str) self.assertEqual(type(self.model._meta.label_lower), str) django-constance-3.1.0/tests/test_checks.py000066400000000000000000000041041447075242200207510ustar00rootroot00000000000000from constance import settings from unittest import mock from constance.checks import check_fieldsets, get_inconsistent_fieldnames from django.test import TestCase class ChecksTestCase(TestCase): @mock.patch("constance.settings.CONFIG_FIELDSETS", {"Set1": settings.CONFIG.keys()}) def test_get_inconsistent_fieldnames_none(self): """ Test that get_inconsistent_fieldnames returns an empty data and no checks fail if CONFIG_FIELDSETS accounts for every key in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertFalse(missing_keys) self.assertFalse(extra_keys) @mock.patch( "constance.settings.CONFIG_FIELDSETS", {"Set1": list(settings.CONFIG.keys())[:-1]}, ) def test_get_inconsistent_fieldnames_for_missing_keys(self): """ Test that get_inconsistent_fieldnames returns data and the check fails if CONFIG_FIELDSETS does not account for every key in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertTrue(missing_keys) self.assertFalse(extra_keys) self.assertEqual(1, len(check_fieldsets())) @mock.patch( "constance.settings.CONFIG_FIELDSETS", {"Set1": list(settings.CONFIG.keys()) + ['FORGOTTEN_KEY']}, ) def test_get_inconsistent_fieldnames_for_extra_keys(self): """ Test that get_inconsistent_fieldnames returns data and the check fails if CONFIG_FIELDSETS contains extra key that is absent in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertFalse(missing_keys) self.assertTrue(extra_keys) self.assertEqual(1, len(check_fieldsets())) @mock.patch( "constance.settings.CONFIG_FIELDSETS", {} ) def test_check_fieldsets(self): """ check_fieldsets should not output warning if CONFIG_FIELDSETS is not defined. """ del settings.CONFIG_FIELDSETS self.assertEqual(0, len(check_fieldsets())) django-constance-3.1.0/tests/test_cli.py000066400000000000000000000057771447075242200203010ustar00rootroot00000000000000from datetime import datetime from textwrap import dedent from django.conf import settings from django.core.management import call_command, CommandError from django.test import TransactionTestCase from django.utils import timezone from django.utils.encoding import smart_str from io import StringIO from constance import config from constance.models import Constance class CliTestCase(TransactionTestCase): def setUp(self): self.out = StringIO() def test_help(self): try: call_command('constance', '--help') except SystemExit: pass def test_list(self): call_command('constance', 'list', stdout=self.out) self.assertEqual(set(self.out.getvalue().splitlines()), set(dedent(smart_str( """ BOOL_VALUE True EMAIL_VALUE test@example.com INT_VALUE 1 LINEBREAK_VALUE Spam spam DATE_VALUE 2010-12-24 TIME_VALUE 23:59:59 TIMEDELTA_VALUE 1 day, 2:03:00 STRING_VALUE Hello world CHOICE_VALUE yes DECIMAL_VALUE 0.1 DATETIME_VALUE 2010-08-23 11:29:24 FLOAT_VALUE 3.1415926536 """)).splitlines())) def test_get(self): call_command('constance', *('get EMAIL_VALUE'.split()), stdout=self.out) self.assertEqual(self.out.getvalue().strip(), "test@example.com") def test_set(self): call_command('constance', *('set EMAIL_VALUE blah@example.com'.split()), stdout=self.out) self.assertEqual(config.EMAIL_VALUE, "blah@example.com") call_command('constance', *('set', 'DATETIME_VALUE', '2011-09-24', '12:30:25'), stdout=self.out) expected = datetime(2011, 9, 24, 12, 30, 25) if settings.USE_TZ: expected = timezone.make_aware(expected) self.assertEqual(config.DATETIME_VALUE, expected) def test_get_invalid_name(self): self.assertRaisesMessage(CommandError, "NOT_A_REAL_CONFIG is not defined in settings.CONSTANCE_CONFIG", call_command, 'constance', 'get', 'NOT_A_REAL_CONFIG') def test_set_invalid_name(self): self.assertRaisesMessage(CommandError, "NOT_A_REAL_CONFIG is not defined in settings.CONSTANCE_CONFIG", call_command, 'constance', 'set', 'NOT_A_REAL_CONFIG', 'foo') def test_set_invalid_value(self): self.assertRaisesMessage(CommandError, "Enter a valid email address.", call_command, 'constance', 'set', 'EMAIL_VALUE', 'not a valid email') def test_set_invalid_multi_value(self): self.assertRaisesMessage(CommandError, "Enter a list of values.", call_command, 'constance', 'set', 'DATETIME_VALUE', '2011-09-24 12:30:25') def test_delete_stale_records(self): initial_count = Constance.objects.count() Constance.objects.create(key='STALE_KEY', value=None) call_command('constance', 'remove_stale_keys', stdout=self.out) self.assertEqual(Constance.objects.count(), initial_count, msg=self.out) django-constance-3.1.0/tests/test_form.py000066400000000000000000000020741447075242200204600ustar00rootroot00000000000000from constance.forms import ConstanceForm from django.forms import fields from django.test import TestCase class TestForm(TestCase): def test_form_field_types(self): f = ConstanceForm({}) self.assertIsInstance(f.fields['INT_VALUE'], fields.IntegerField) self.assertIsInstance(f.fields['BOOL_VALUE'], fields.BooleanField) self.assertIsInstance(f.fields['STRING_VALUE'], fields.CharField) self.assertIsInstance(f.fields['DECIMAL_VALUE'], fields.DecimalField) self.assertIsInstance(f.fields['DATETIME_VALUE'], fields.SplitDateTimeField) self.assertIsInstance(f.fields['TIMEDELTA_VALUE'], fields.DurationField) self.assertIsInstance(f.fields['FLOAT_VALUE'], fields.FloatField) self.assertIsInstance(f.fields['DATE_VALUE'], fields.DateField) self.assertIsInstance(f.fields['TIME_VALUE'], fields.TimeField) # from CONSTANCE_ADDITIONAL_FIELDS self.assertIsInstance(f.fields['CHOICE_VALUE'], fields.ChoiceField) self.assertIsInstance(f.fields['EMAIL_VALUE'], fields.EmailField) django-constance-3.1.0/tests/test_pytest_overrides.py000066400000000000000000000051471447075242200231330ustar00rootroot00000000000000import unittest try: import pytest from constance import config from constance.test.pytest import override_config class TestPytestOverrideConfigFunctionDecorator: """Test that the override_config decorator works correctly for Pytest classes. Test usage of override_config on test method and as context manager. """ def test_default_value_is_true(self): """Assert that the default value of config.BOOL_VALUE is True.""" assert config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE=False) def test_override_config_on_method_changes_config_value(self): """Assert that the pytest mark decorator changes config.BOOL_VALUE.""" assert not config.BOOL_VALUE def test_override_config_as_context_manager_changes_config_value(self): """Assert that the context manager changes config.BOOL_VALUE.""" with override_config(BOOL_VALUE=False): assert not config.BOOL_VALUE assert config.BOOL_VALUE @override_config(BOOL_VALUE=False) def test_method_decorator(self): """Ensure `override_config` can be used as test method decorator.""" assert not config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE=False) class TestPytestOverrideConfigDecorator: """Test that the override_config decorator works on classes.""" def test_override_config_on_class_changes_config_value(self): """Asser that the class decorator changes config.BOOL_VALUE.""" assert not config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE='True') def test_override_config_on_overrided_value(self): """Ensure that method mark decorator changes already overrided value for class.""" assert config.BOOL_VALUE == 'True' def test_fixture_override_config(override_config): """ Ensure `override_config` fixture is available globally and can be used in test functions. """ with override_config(BOOL_VALUE=False): assert not config.BOOL_VALUE @override_config(BOOL_VALUE=False) def test_func_decorator(): """Ensure `override_config` can be used as test function decorator.""" assert not config.BOOL_VALUE except ImportError: pass class PytestTests(unittest.TestCase): def setUp(self): self.skipTest('Skip all pytest tests when using unittest') def test_do_not_skip_silently(self): """ If no at least one test present, unittest silently skips module. """ pass django-constance-3.1.0/tests/test_test_overrides.py000066400000000000000000000024561447075242200225620ustar00rootroot00000000000000from django.test import TestCase from constance import config from constance.test import override_config class OverrideConfigFunctionDecoratorTestCase(TestCase): """Test that the override_config decorator works correctly. Test usage of override_config on test method and as context manager. """ def test_default_value_is_true(self): """Assert that the default value of config.BOOL_VALUE is True.""" self.assertTrue(config.BOOL_VALUE) @override_config(BOOL_VALUE=False) def test_override_config_on_method_changes_config_value(self): """Assert that the method decorator changes config.BOOL_VALUE.""" self.assertFalse(config.BOOL_VALUE) def test_override_config_as_context_manager_changes_config_value(self): """Assert that the context manager changes config.BOOL_VALUE.""" with override_config(BOOL_VALUE=False): self.assertFalse(config.BOOL_VALUE) self.assertTrue(config.BOOL_VALUE) @override_config(BOOL_VALUE=False) class OverrideConfigClassDecoratorTestCase(TestCase): """Test that the override_config decorator works on classes.""" def test_override_config_on_class_changes_config_value(self): """Asser that the class decorator changes config.BOOL_VALUE.""" self.assertFalse(config.BOOL_VALUE) django-constance-3.1.0/tests/test_utils.py000066400000000000000000000027561447075242200206640ustar00rootroot00000000000000import datetime from decimal import Decimal from constance.utils import get_values from constance.management.commands.constance import _set_constance_value from django.core.exceptions import ValidationError from django.test import TestCase class UtilsTestCase(TestCase): def test_set_value_validation(self): self.assertRaisesMessage(ValidationError, 'Enter a whole number.', _set_constance_value, 'INT_VALUE', 'foo') self.assertRaisesMessage(ValidationError, 'Enter a valid email address.', _set_constance_value, 'EMAIL_VALUE', 'not a valid email') self.assertRaisesMessage(ValidationError, 'Enter a valid date.', _set_constance_value, 'DATETIME_VALUE', ('2000-00-00', '99:99:99',)) self.assertRaisesMessage(ValidationError, 'Enter a valid time.', _set_constance_value, 'DATETIME_VALUE', ('2016-01-01', '99:99:99',)) def test_get_values(self): self.assertEqual(get_values(), { 'FLOAT_VALUE': 3.1415926536, 'BOOL_VALUE': True, 'EMAIL_VALUE': 'test@example.com', 'INT_VALUE': 1, 'CHOICE_VALUE': 'yes', 'TIME_VALUE': datetime.time(23, 59, 59), 'DATE_VALUE': datetime.date(2010, 12, 24), 'TIMEDELTA_VALUE': datetime.timedelta(days=1, hours=2, minutes=3), 'LINEBREAK_VALUE': 'Spam spam', 'DECIMAL_VALUE': Decimal('0.1'), 'STRING_VALUE': 'Hello world', 'DATETIME_VALUE': datetime.datetime(2010, 8, 23, 11, 29, 24), }) django-constance-3.1.0/tests/urls.py000066400000000000000000000001701447075242200174360ustar00rootroot00000000000000from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ] django-constance-3.1.0/tox.ini000066400000000000000000000017371447075242200162620ustar00rootroot00000000000000[tox] requires = tox<4 envlist = py{37,38,39,310}-dj{32}-{unittest,pytest} py{38,39,310}-dj{40}-{unittest,pytest} py{38,39,310,311}-dj{41,42}-{unittest,pytest} py{310,311}-dj{main}-{unittest,pytest} [testenv] deps = redis coverage django-picklefield dj32: Django>=3.2,<4; dj40: Django>=4.0,<4.1 dj41: Django>=4.1,<4.2 dj42: Django>=4.2,<5.0 djmain: https://github.com/django/django/archive/main.tar.gz pytest: pytest pytest: pytest-cov pytest: pytest-django usedevelop = True ignore_outcome = djmain: True commands = unittest: coverage run {envbindir}/django-admin test -v2 unittest: coverage report unittest: coverage xml pytest: pytest --cov=. --ignore=.tox --disable-pytest-warnings --cov-report=xml --cov-append {toxinidir} setenv = PYTHONDONTWRITEBYTECODE=1 DJANGO_SETTINGS_MODULE=tests.settings [gh-actions] python = 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311