跳转至

Django-constance实现动态设置

本文基于 django-constance==2.2.0

安装

django-constance分为redis存储设置的版本和使用database存储设置的版本,对应的下载方式分别为:

pip install "django-constance[redis]"
pip install "django-constance[database]"

如果确认相关依赖已经安装,直接使用下列命令安装:

pip install django-constance

配置

settings.py文件

# 已安装的Apps
INSTALLED_APPS = [
    'constance',
    ...
]

若使用database存储设置的版本,请使用:

# 已安装的Apps
INSTALLED_APPS = [
    'constance',
    'constance.backends.database',
    ...
]

声明待配置选项:

CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True


CONSTANCE_ADDITIONAL_FIELDS = {
    'coupon_select': ['django.forms.fields.ChoiceField', {
        'widget': 'django.forms.Select',
        'choices': ((0, "时+分+秒"), (1, "时+分"), (2, "时"), (3, "忽略"))
    }],
}

CONSTANCE_CONFIG = {
    'site_name': ('南京诗远启', '网站标题'),
    'site_description': ('南京诗远启', '站点描述'),

    'coupon_fix_code': ('DreamGo', '电子券固定代码'),
    'coupon_time_format': (0, '电子券时间格式', 'coupon_select'),
    'sms_reset_date': (1, '分配短信清零时间'),
}

CONSTANCE_CONFIG_FIELDSETS = {
    '站点设置': ('site_name', 'site_description'),
    '电子券': ('coupon_fix_code', 'coupon_time_format', ),
    '短信': ('sms_reset_date', ),
}

'site_name': ('南京诗远启', '网站标题'), 分别代表 设置变量的名称 默认值 设置变量用途描述(help_text)

'coupon_time_format': (0, '电子券时间格式', 'coupon_select'),前3个变量意义同上,最后的'coupon_select'代表使用自定义选择控件来选择值。

Admin样式

img

使用代码获取当前设置值

from constance import config
def set_value(key, value):
    """
    更新数据库设置项
    :param key: 键名
    :param value: 值
    :return: 无返回
    """
    setattr(config, key, value)


def get_value(key):
    """
    获取数据库设置项值
    :param key: 键名
    :return: 键名对应的设置项值
    """
    return getattr(config, key)

用法:

set_value('site_name', '南京诗远启(修改后)')
get_value('site_name') # 应为'南京诗远启(修改后)'

DjangoRestFramework中使用

utils.py:

from constance import config
from constance.settings import CONFIG

def get_settings(allow_settings):
    """
    获取对应settings组成的list
    :param allow_settings: 待转义列表
    :return: 对应settings组成的list
    """
    setting_list = []
    for key, options in CONFIG.items():
        if key in allow_settings:
            default, help_text = options[0], options[1]
            data = {'key': key, 'default': default, 'help_text': help_text, 'value': get_value(key)}
            setting_list.append(data)
    return setting_list

views.py:

from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from .utils import set_value, get_value, CONFIG 

class SettingViewSet(ViewSet):
    permission_classes = (IsAuthenticated,)

    def setting(self, request, allow_settings):
        if request.method == 'GET':
            return Response(data=get_settings(allow_settings), status=status.HTTP_200_OK)
        else:
            all_settings = CONFIG.keys()
            for key in request.data:
                if key in allow_settings and key in all_settings:
                    value = request.data[key]
                    set_value(key, '' if value is None else value)
            return Response(data=get_settings(allow_settings), status=status.HTTP_200_OK)

    def create(self, request):
        """
        <p>更新设置POST:<code>{'设置Key': 新值}</code>
        """
        return self.setting(request, CONFIG.keys())

    def list(self, request):
        """
        返回所有待选设置项
        """
        return self.setting(request, CONFIG.keys())

    @action(methods=['GET', 'POST'], detail=False)
    def site(self, request):
        """仅允许设置 网站标题、站点描述的接口"""
        allow_settings = ['site_name', 'site_description']
        return self.setting(request, allow_settings)

img

固定设置项在Admin中的顺序

为了防止每次打开设置界面设置选项错误,需要使用OrderedDict来保证顺序:

from collections import OrderedDict

SITE_NAME = 'site_name'
SITE_DESCRIPTION = 'site_description'

CONSTANCE_CONFIG = OrderedDict([
    (SITE_NAME, ('南京诗远启', '站点名称')),
    (SITE_DESCRIPTION, ('南京诗远启', '站点描述')),
])

CONSTANCE_CONFIG_FIELDSETS = OrderedDict([
    ('站点设置', (SITE_NAME, SITE_DESCRIPTION)),
])

QA

出现没有表的情况

执行:

python manage.py makemigrations constance
python manage.py migrate constance

在makemigrations时就提示表不存在

INSTALLED_APPS中加constance.backends.database,删除使用到constance的地方,makemigrations之后再迁移。

修改权限'constance.change_config'的名称后报错

constance默认提供了一个权限'constance.change_config',但是当修改了该权限的默认权限名称后,会出现一下错误:

django.db.utils.IntegrityError: (1062, "Duplicate entry '2-change_config' for key 'auth_permission_content_type_id_codename_01ab37
5a_uniq'")

重新浏览代码后发现是在constance/apps.py 29行,写了:

permission, created = Permission.objects.using(using).get_or_create(
    name='Can change config',
    content_type=content_type,
    codename='change_config')

将其修改为:

permission, created = Permission.objects.using(using).get_or_create(
    content_type=content_type,
    codename='change_config',
    defaults={'name': 'Can change config'})

即可解决这个错误,详见issue。 该PR已提交,代码合并成功,在一个正式版本该问题将被解决。

makemigrations constance一直不创建makemigrations文件

Backends->Database

pip install django-constance[database]

CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
INSTALLED_APPS = (
    # other apps
    'constance.backends.database',
)

python manage.py migrate database

频繁查询、更新的设置项 使用Cache加快访问速度

Cache

# cache配置
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
        'KEY_PREFIX': 'protal',
        'options': {
            'MAX_ENTRIES': 1024,
        }
    },
    'memcache': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
        'KEY_PREFIX': 'protal',
        'options': {
            'MAX_ENTRIES': 1024,
        }
    },
}

# 设置缓存
CONSTANCE_DATABASE_CACHE_BACKEND = 'memcache'

Illegal mix of collations for operation ' IN '

若在升级项目中遇到mysql报错

Illegal mix of collations for operation ' IN '

需要删除数据库,再次创建 create database database_name character set utf8; 再次导入数据库即可。

文档