django-liveconfigs


Namedjango-liveconfigs JSON
Version 1.0.6.1 PyPI version JSON
download
home_pageNone
SummarySimple powerful and convenient configs for Django
upload_time2024-09-05 10:00:17
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseCopyright (c) 2019-2024 F5Devs <https://f5devs.ru/> Copyright (c) 2024 SigmaDevs <https://sigmadevs.ru/> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of copyright holders 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 COPYRIGHT HOLDERS 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.
keywords django config easy config simple config liveconfigs
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # LiveConfigs

LiveConfigs помогает настраивать приложение "на лету" через django-админку без перевыкатки и перезапуска. 
Сама конфигурация хранится в БД, но для разработчика скрыта за удобным программным интерфейсом.

## Быстрый пример

Создаем настройки

```python
class MyConfig(liveconfigs.BaseConfig):
   IS_FEATURE_ENABLED: bool = False
   NEW_FEATURE_VALUE: float = 20.4
```

Используем их

```python
if MyConfig.IS_FEATURE_ENABLED:
   print('feature is enabled and feature value is', MyConfig.NEW_FEATURE_VALUE)
```

Меняем через админку!

![Экран редактирования конфига](https://github.com/liveconfigs/django-liveconfigs/blob/main/images/change_config.jpg?raw=true)


## Термины
+ __настройка__, __конфиг__ - одно типизированное именованное значение
+ __класс конфига__, __группа настроек__ - настройки, объединенные в один класс python
+ __топик__ - общая "тема" для группы настроек, по которой можно производить поиск настроек в админке.
У разных групп настроек в принципе может быть один и тот же топик
+ __теги__ - ключевые слова для быстрого поиска настроек. Одна настройка может иметь несколько тегов
## Со стороны разработчика

Для разработчика настройки выглядят как статические члены-константы классов python.
Их можно быстро добавить, убрать, их просто использовать (пара строк кода). Их понимает mytype и среды разработки.

За создание новых конфигов отвечает разработчик.
Значения по-умолчанию хранятся в коде.
В БД новые конфиги попадают при выкатке или при первом использовании.


## Со стороны администратора 
Значения задаются через админку django. 
Для удобного администрирования конфиги поддерживают
+ __теги__ и __топики__ для группировки и поиска
+ документацию
+ автоматическую загрузку новых конфигов в бд
+ валидацию при изменении (типы и значения)
+ сохранение даты последних изменений и чтений конфигов
+ импорт и экспорт (удобно для тестирования системы)

![Экран поиска и фильтрации конфигов](https://github.com/liveconfigs/django-liveconfigs/blob/main/images/filter_config.jpg?raw=true)

## Как начать пользоваться
1. Установите пакет `django-liveconfigs` через pip, poetry или еще как-нибудь.
```bash
pip install django-liveconfigs
```

2. Добавьте "liveconfigs" в INSTALLED_APPS в settings:
```python
    INSTALLED_APPS = [
        ...,
        "import_export",
        "liveconfigs",
    ]
```

3. Добавьте в settings еще несколько строк.
```python
    # liveconfigs settings
    # Максимальная длина текста в значении конфига при которой отображать поле редактирования конфига как textinput
    # При длине текста в значении конфига большей этого значения - отображать поле редактирования конфига как textarea
    LC_MAX_STR_LENGTH_DISPLAYED_AS_TEXTINPUT = 50
    LC_ENABLE_PRETTY_INPUT = True
    LIVECONFIGS_SYNCWRITE = True    # sync write mode
    LC_CACHE_TTL = 1    # cache TTL in seconds (default = 1)
    # Максимальная длина значения конфига (в текстовом представлении) при которой значение в списке выводится целиком
    # При бОльшей длине визуал значения будет усечен ("Длинная строка" -> "Длин ... рока")
    LC_MAX_VISUAL_VALUE_LENGTH = 50
```

4. Заведите себе файл собственно с конфигами, например `config/config.py`
```python
from liveconfigs import models
from liveconfigs.validators import greater_than
from enum import Enum

# isort: off

# config_row_update_signal_handler begin
from django.conf import settings
from django.dispatch import receiver
from liveconfigs.signals import config_row_update_signal
from liveconfigs.tasks import config_row_update_or_create

# FIXME: Импорт приложения Celery из вашего проекта (если используете Celery)
# FIXME: Вам нужно изменить этот код, если вы используете не Celery
from celery_app import app

# isort: on

# Пример для Celery
# Реальное сохранение данных выполняет функция config_row_update_or_create
# Реализуйте отложенное сохранение удобным для вас методом
# Для Celery зарегистрируйте эту задачу в CELERY_TASK_ROUTES
# FIXME: Вам нужно изменить этот код, если вы используете не Celery
@app.task(max_retries=1, soft_time_limit=1200, time_limit=1500)
def config_row_update_or_create_proxy(config_name: str, update_fields: dict):
    config_row_update_or_create(config_name, update_fields)


@receiver(config_row_update_signal, dispatch_uid="config_row_update_signal")
def config_row_update_signal_handler(sender, config_name, update_fields, **kwargs):
    # Пример для Celery
    # При настройках для синхронного сохранения функция будет вызвана напрямую
    if settings.LIVECONFIGS_SYNCWRITE:
        config_row_update_or_create_proxy_func = config_row_update_or_create_proxy
    # При настройках для асинхронного сохранения функция будет вызвана через delay
    # FIXME: Вам нужно изменить этот код, если вы используете не Celery
    else:
        config_row_update_or_create_proxy_func = config_row_update_or_create_proxy.delay

    config_row_update_or_create_proxy_func(config_name, update_fields)


# config_row_update_signal_handler end


# тут перечислены возможные теги для настроек из вашей предметной области
class ConfigTags(str, Enum):
    front = "Настройки для фронта"
    features = "Фичи"
    basic = "Основные"
    other = "Прочее"

# тут описана сама настройка и ее мета-данные
class FirstExample(models.BaseConfig):
    __topic__ = 'Основные настройки'  # короткое описание группы настроек
    MY_FIRST_CONFIG: int = 40
    # следующие строчки необязательны
    MY_FIRST_CONFIG_DESCRIPTION = "Какая-то моя настройка"
    MY_FIRST_CONFIG_TAGS = [ConfigTags.basic, ConfigTags.other]
    MY_FIRST_CONFIG_VALIDATORS = [greater_than(5)]
    
    # вторая настройка, без метаданных
    SECOND_ONE: bool = False  
```

5. Используете где-нибудь `FirstExample.MY_FIRST_CONFIG` как обычный int:
```python
from config.config import FirstExample
...
if FirstExample.MY_FIRST_CONFIG > 20:
    print("Hello there!")
```

## Просмотр и редактирование конфигов в админке django
Редактировать значения конфигов можно по адресу
 http://YOUR_HOST/admin/liveconfigs/configrow/

При установке значений проверяется тип нового значения, а также вызываются
дополнительные валидаторы

## Автоматическая загрузка новых конфигов в БД
При первом обращении к настройке приложение проверяет,
есть ли запись о них в БД. Если ее нет, то конфиг записывается
в БД со значением по-умолчанию.

Если по какой-то причине вы не хотите ждать, то залить все новые конфиги 
в БД можно и на старте сервиса, добавив
в ваш скрипт запуска вызов команды load_config:

```sh
    # какие-то старые команды
    python /app/manage.py migrate --noinput
    python /app/manage.py collectstatic --noinput

    #  НОВАЯ СТРОЧКА - загрузка конфигов в бд
    python /app/manage.py load_config

    #  сам запуск сервиса - может быть и так
    python /app/manage.py runserver_plus 0.0.0.0:8080 --insecure
```

## Даты последнего изменения и чтения
В БД у каждой настройки есть два дополнительных поля - даты последнего чтения
и записи. Они помогают определить в живой системе, нужны ли все еще какие-то настройки
или пора уже от них избавиться.
### Асинхронная запись
Чтобы запись последней даты чтения не тормозила вам всю систему (если,например, конфиги часто читаются разными частями кода),
можно вынести ее в задачу celery. Для этого:
 1. Установите переменную LIVECONFIGS_SYNCWRITE в `settings.py` в False:
 ```
     LIVECONFIGS_SYNCWRITE = False   # async write mode
 ```
 
 2. Если используете Celery, то настройте запуск задачи `config.config.config_row_update_or_create_proxy`:
 ```python
     CELERY_TASK_ROUTES = {
         'config.config.config_row_update_or_create_proxy': {
             'queue': 'quick', 'routing_key': 'quick'
         },
     }
 ```

 3. Если используете не Celery, то адаптируйте этот код под ваш случай

## Остались вопросы?
+ Посмотрите примеры использования конфигов: https://github.com/liveconfigs/django-liveconfigs-example/

+ Примеры использования валидаторов : https://github.com/liveconfigs/django-liveconfigs-example/

+ Примеры валидаторов : `validators.py` в https://github.com/liveconfigs/django-liveconfigs/

+ Напишите нам! 

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-liveconfigs",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "Denis Dudnik <Denis.Dudnik@gmail.com>",
    "keywords": "django, config, easy config, simple config, liveconfigs",
    "author": null,
    "author_email": "Evgenii Katsevman <eugene.katsevman@gmail.com>, Mihail Mayorov <mihail1-m1@yandex.ru>, Alexander Ignatov <goodwinsis@yandex.ru>, Alexander Mironov <mironov.miet@gmail.com>, Timur Timerkhanov <timurgrunge@gmail.com>, Ivan Shevyakov <iashevyakov@mail.ru>, Vladilav Nechaev <vladislavtv1@gmail.com>, Denis Dudnik <Denis.Dudnik@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/73/91/694e861fd144881fe7c41cbd75025ee4391446ecddbd6e00d9cd6d63d5bb/django_liveconfigs-1.0.6.1.tar.gz",
    "platform": null,
    "description": "# LiveConfigs\n\nLiveConfigs \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \"\u043d\u0430 \u043b\u0435\u0442\u0443\" \u0447\u0435\u0440\u0435\u0437 django-\u0430\u0434\u043c\u0438\u043d\u043a\u0443 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0432\u044b\u043a\u0430\u0442\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430. \n\u0421\u0430\u043c\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0411\u0414, \u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043a\u0440\u044b\u0442\u0430 \u0437\u0430 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c.\n\n## \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440\n\n\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n\n```python\nclass MyConfig(liveconfigs.BaseConfig):\n   IS_FEATURE_ENABLED: bool = False\n   NEW_FEATURE_VALUE: float = 20.4\n```\n\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0438\u0445\n\n```python\nif MyConfig.IS_FEATURE_ENABLED:\n   print('feature is enabled and feature value is', MyConfig.NEW_FEATURE_VALUE)\n```\n\n\u041c\u0435\u043d\u044f\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u043c\u0438\u043d\u043a\u0443!\n\n![\u042d\u043a\u0440\u0430\u043d \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430](https://github.com/liveconfigs/django-liveconfigs/blob/main/images/change_config.jpg?raw=true)\n\n\n## \u0422\u0435\u0440\u043c\u0438\u043d\u044b\n+ __\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430__, __\u043a\u043e\u043d\u0444\u0438\u0433__ - \u043e\u0434\u043d\u043e \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n+ __\u043a\u043b\u0430\u0441\u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0430__, __\u0433\u0440\u0443\u043f\u043f\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a__ - \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u043d\u044b\u0435 \u0432 \u043e\u0434\u0438\u043d \u043a\u043b\u0430\u0441\u0441 python\n+ __\u0442\u043e\u043f\u0438\u043a__ - \u043e\u0431\u0449\u0430\u044f \"\u0442\u0435\u043c\u0430\" \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435.\n\u0423 \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u0442\u043e\u043f\u0438\u043a\n+ __\u0442\u0435\u0433\u0438__ - \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a. \u041e\u0434\u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0433\u043e\u0432\n## \u0421\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\n\n\u0414\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u0430\u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0447\u043b\u0435\u043d\u044b-\u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u043a\u043b\u0430\u0441\u0441\u043e\u0432 python.\n\u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c, \u0443\u0431\u0440\u0430\u0442\u044c, \u0438\u0445 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c (\u043f\u0430\u0440\u0430 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430). \u0418\u0445 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442 mytype \u0438 \u0441\u0440\u0435\u0434\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.\n\n\u0417\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a.\n\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e-\u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u043a\u043e\u0434\u0435.\n\u0412 \u0411\u0414 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442 \u043f\u0440\u0438 \u0432\u044b\u043a\u0430\u0442\u043a\u0435 \u0438\u043b\u0438 \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438.\n\n\n## \u0421\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 \n\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u043c\u0438\u043d\u043a\u0443 django. \n\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\n+ __\u0442\u0435\u0433\u0438__ \u0438 __\u0442\u043e\u043f\u0438\u043a\u0438__ \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u0438 \u043f\u043e\u0438\u0441\u043a\u0430\n+ \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e\n+ \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u0432 \u0431\u0434\n+ \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 (\u0442\u0438\u043f\u044b \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f)\n+ \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u044b \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0438 \u0447\u0442\u0435\u043d\u0438\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432\n+ \u0438\u043c\u043f\u043e\u0440\u0442 \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442 (\u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u044b)\n\n![\u042d\u043a\u0440\u0430\u043d \u043f\u043e\u0438\u0441\u043a\u0430 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432](https://github.com/liveconfigs/django-liveconfigs/blob/main/images/filter_config.jpg?raw=true)\n\n## \u041a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f\n1. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u0430\u043a\u0435\u0442 `django-liveconfigs` \u0447\u0435\u0440\u0435\u0437 pip, poetry \u0438\u043b\u0438 \u0435\u0449\u0435 \u043a\u0430\u043a-\u043d\u0438\u0431\u0443\u0434\u044c.\n```bash\npip install django-liveconfigs\n```\n\n2. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \"liveconfigs\" \u0432 INSTALLED_APPS \u0432 settings:\n```python\n    INSTALLED_APPS = [\n        ...,\n        \"import_export\",\n        \"liveconfigs\",\n    ]\n```\n\n3. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 settings \u0435\u0449\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a.\n```python\n    # liveconfigs settings\n    # \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043a\u0430\u043a textinput\n    # \u041f\u0440\u0438 \u0434\u043b\u0438\u043d\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u044d\u0442\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f - \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043a\u0430\u043a textarea\n    LC_MAX_STR_LENGTH_DISPLAYED_AS_TEXTINPUT = 50\n    LC_ENABLE_PRETTY_INPUT = True\n    LIVECONFIGS_SYNCWRITE = True    # sync write mode\n    LC_CACHE_TTL = 1    # cache TTL in seconds (default = 1)\n    # \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430 (\u0432 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438) \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0446\u0435\u043b\u0438\u043a\u043e\u043c\n    # \u041f\u0440\u0438 \u0431\u041e\u043b\u044c\u0448\u0435\u0439 \u0434\u043b\u0438\u043d\u0435 \u0432\u0438\u0437\u0443\u0430\u043b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0435\u0447\u0435\u043d (\"\u0414\u043b\u0438\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430\" -> \"\u0414\u043b\u0438\u043d ... \u0440\u043e\u043a\u0430\")\n    LC_MAX_VISUAL_VALUE_LENGTH = 50\n```\n\n4. \u0417\u0430\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0435\u0431\u0435 \u0444\u0430\u0439\u043b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u043c\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 `config/config.py`\n```python\nfrom liveconfigs import models\nfrom liveconfigs.validators import greater_than\nfrom enum import Enum\n\n# isort: off\n\n# config_row_update_signal_handler begin\nfrom django.conf import settings\nfrom django.dispatch import receiver\nfrom liveconfigs.signals import config_row_update_signal\nfrom liveconfigs.tasks import config_row_update_or_create\n\n# FIXME: \u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Celery \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Celery)\n# FIXME: \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043d\u0435 Celery\nfrom celery_app import app\n\n# isort: on\n\n# \u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f Celery\n# \u0420\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f config_row_update_or_create\n# \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0434\u043b\u044f \u0432\u0430\u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u043c\n# \u0414\u043b\u044f Celery \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 CELERY_TASK_ROUTES\n# FIXME: \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043d\u0435 Celery\n@app.task(max_retries=1, soft_time_limit=1200, time_limit=1500)\ndef config_row_update_or_create_proxy(config_name: str, update_fields: dict):\n    config_row_update_or_create(config_name, update_fields)\n\n\n@receiver(config_row_update_signal, dispatch_uid=\"config_row_update_signal\")\ndef config_row_update_signal_handler(sender, config_name, update_fields, **kwargs):\n    # \u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f Celery\n    # \u041f\u0440\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0434\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e\n    if settings.LIVECONFIGS_SYNCWRITE:\n        config_row_update_or_create_proxy_func = config_row_update_or_create_proxy\n    # \u041f\u0440\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0434\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 delay\n    # FIXME: \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043d\u0435 Celery\n    else:\n        config_row_update_or_create_proxy_func = config_row_update_or_create_proxy.delay\n\n    config_row_update_or_create_proxy_func(config_name, update_fields)\n\n\n# config_row_update_signal_handler end\n\n\n# \u0442\u0443\u0442 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0442\u0435\u0433\u0438 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0438\u0437 \u0432\u0430\u0448\u0435\u0439 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438\nclass ConfigTags(str, Enum):\n    front = \"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0430\"\n    features = \"\u0424\u0438\u0447\u0438\"\n    basic = \"\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435\"\n    other = \"\u041f\u0440\u043e\u0447\u0435\u0435\"\n\n# \u0442\u0443\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0430 \u0441\u0430\u043c\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438 \u0435\u0435 \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u0435\nclass FirstExample(models.BaseConfig):\n    __topic__ = '\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438'  # \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\n    MY_FIRST_CONFIG: int = 40\n    # \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\n    MY_FIRST_CONFIG_DESCRIPTION = \"\u041a\u0430\u043a\u0430\u044f-\u0442\u043e \u043c\u043e\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\"\n    MY_FIRST_CONFIG_TAGS = [ConfigTags.basic, ConfigTags.other]\n    MY_FIRST_CONFIG_VALIDATORS = [greater_than(5)]\n    \n    # \u0432\u0442\u043e\u0440\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430, \u0431\u0435\u0437 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445\n    SECOND_ONE: bool = False  \n```\n\n5. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0433\u0434\u0435-\u043d\u0438\u0431\u0443\u0434\u044c `FirstExample.MY_FIRST_CONFIG` \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0439 int:\n```python\nfrom config.config import FirstExample\n...\nif FirstExample.MY_FIRST_CONFIG > 20:\n    print(\"Hello there!\")\n```\n\n## \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435 django\n\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443\n http://YOUR_HOST/admin/liveconfigs/configrow/\n\n\u041f\u0440\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0442\u0438\u043f \u043d\u043e\u0432\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f\n\u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b\n\n## \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u0432 \u0411\u0414\n\u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442,\n\u0435\u0441\u0442\u044c \u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u044c \u043e \u043d\u0438\u0445 \u0432 \u0411\u0414. \u0415\u0441\u043b\u0438 \u0435\u0435 \u043d\u0435\u0442, \u0442\u043e \u043a\u043e\u043d\u0444\u0438\u0433 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f\n\u0432 \u0411\u0414 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u043e-\u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.\n\n\u0415\u0441\u043b\u0438 \u043f\u043e \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u0432\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u0436\u0434\u0430\u0442\u044c, \u0442\u043e \u0437\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \n\u0432 \u0411\u0414 \u043c\u043e\u0436\u043d\u043e \u0438 \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u0432\n\u0432 \u0432\u0430\u0448 \u0441\u043a\u0440\u0438\u043f\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u044b\u0437\u043e\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u044b load_config:\n\n```sh\n    # \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0441\u0442\u0430\u0440\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b\n    python /app/manage.py migrate --noinput\n    python /app/manage.py collectstatic --noinput\n\n    #  \u041d\u041e\u0412\u0410\u042f \u0421\u0422\u0420\u041e\u0427\u041a\u0410 - \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u0432 \u0431\u0434\n    python /app/manage.py load_config\n\n    #  \u0441\u0430\u043c \u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0430 - \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438 \u0442\u0430\u043a\n    python /app/manage.py runserver_plus 0.0.0.0:8080 --insecure\n```\n\n## \u0414\u0430\u0442\u044b \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0447\u0442\u0435\u043d\u0438\u044f\n\u0412 \u0411\u0414 \u0443 \u043a\u0430\u0436\u0434\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u044f - \u0434\u0430\u0442\u044b \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0447\u0442\u0435\u043d\u0438\u044f\n\u0438 \u0437\u0430\u043f\u0438\u0441\u0438. \u041e\u043d\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432 \u0436\u0438\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043d\u0443\u0436\u043d\u044b \u043b\u0438 \u0432\u0441\u0435 \u0435\u0449\u0435 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n\u0438\u043b\u0438 \u043f\u043e\u0440\u0430 \u0443\u0436\u0435 \u043e\u0442 \u043d\u0438\u0445 \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f.\n### \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c\n\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0434\u0430\u0442\u044b \u0447\u0442\u0435\u043d\u0438\u044f \u043d\u0435 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u043b\u0430 \u0432\u0430\u043c \u0432\u0441\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 (\u0435\u0441\u043b\u0438,\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0447\u0430\u0441\u0442\u043e \u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0447\u0430\u0441\u0442\u044f\u043c\u0438 \u043a\u043e\u0434\u0430),\n\u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0435\u0435 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 celery. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e:\n 1. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e LIVECONFIGS_SYNCWRITE \u0432 `settings.py` \u0432 False:\n ```\n     LIVECONFIGS_SYNCWRITE = False   # async write mode\n ```\n \n 2. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Celery, \u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438 `config.config.config_row_update_or_create_proxy`:\n ```python\n     CELERY_TASK_ROUTES = {\n         'config.config.config_row_update_or_create_proxy': {\n             'queue': 'quick', 'routing_key': 'quick'\n         },\n     }\n ```\n\n 3. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043d\u0435 Celery, \u0442\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434 \u043f\u043e\u0434 \u0432\u0430\u0448 \u0441\u043b\u0443\u0447\u0430\u0439\n\n## \u041e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b?\n+ \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432: https://github.com/liveconfigs/django-liveconfigs-example/\n\n+ \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u0432 : https://github.com/liveconfigs/django-liveconfigs-example/\n\n+ \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u0432 : `validators.py` \u0432 https://github.com/liveconfigs/django-liveconfigs/\n\n+ \u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043d\u0430\u043c! \n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2019-2024 F5Devs <https://f5devs.ru/> Copyright (c) 2024 SigmaDevs <https://sigmadevs.ru/>  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of copyright holders 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 COPYRIGHT HOLDERS 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. ",
    "summary": "Simple powerful and convenient configs for Django",
    "version": "1.0.6.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/liveconfigs/django-liveconfigs/issues",
        "Changelog": "https://github.com/liveconfigs/django-liveconfigs/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/liveconfigs/django-liveconfigs/blob/main/README.md",
        "Homepage": "https://github.com/liveconfigs/django-liveconfigs",
        "Repository": "https://github.com/liveconfigs/django-liveconfigs"
    },
    "split_keywords": [
        "django",
        " config",
        " easy config",
        " simple config",
        " liveconfigs"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b8d8b307c3ef98295f3696907060d5a2e2d5220e0dfe476f4c425dae71f36ea8",
                "md5": "35c67cd35bbc16bfe8b75655d19f4f61",
                "sha256": "05fc2dcd7dc59c791ef7f1d2a8d8b27d0819bac565c51c885904b6cc76023c1a"
            },
            "downloads": -1,
            "filename": "django_liveconfigs-1.0.6.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "35c67cd35bbc16bfe8b75655d19f4f61",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 23299,
            "upload_time": "2024-09-05T10:00:15",
            "upload_time_iso_8601": "2024-09-05T10:00:15.844784Z",
            "url": "https://files.pythonhosted.org/packages/b8/d8/b307c3ef98295f3696907060d5a2e2d5220e0dfe476f4c425dae71f36ea8/django_liveconfigs-1.0.6.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7391694e861fd144881fe7c41cbd75025ee4391446ecddbd6e00d9cd6d63d5bb",
                "md5": "a3e8653b07d85b8d24af2c801acb5602",
                "sha256": "5e61050462ff4062e81d8f9501ed5bf20c33bfca1eba868d9afff3c2aac76931"
            },
            "downloads": -1,
            "filename": "django_liveconfigs-1.0.6.1.tar.gz",
            "has_sig": false,
            "md5_digest": "a3e8653b07d85b8d24af2c801acb5602",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 17947,
            "upload_time": "2024-09-05T10:00:17",
            "upload_time_iso_8601": "2024-09-05T10:00:17.530755Z",
            "url": "https://files.pythonhosted.org/packages/73/91/694e861fd144881fe7c41cbd75025ee4391446ecddbd6e00d9cd6d63d5bb/django_liveconfigs-1.0.6.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-05 10:00:17",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "liveconfigs",
    "github_project": "django-liveconfigs",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "django-liveconfigs"
}
        
Elapsed time: 0.30872s