From ed71c2afe387e7dddbdd1c5563fa92dfad16d7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 11:00:48 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 115 --------------------------------------------------- README.en.md | 36 ---------------- 2 files changed, 151 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.en.md diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 11614af..0000000 --- a/.gitignore +++ /dev/null @@ -1,115 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 4b7fca0..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# django-kelove-setting - -#### Description -快速定制django配置,统一管理配置信息 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) -- Gitee From 5ae241c8c88e8d8cf34892820bc902dcb385127e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 11:13:35 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E4=BE=9D=E8=B5=96=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9648395 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "django-kelove-setting" +version = "0.1.0" +description = "" +authors = ["IT小强xqitw.cn "] + +[tool.poetry.dependencies] +python = "^3.6" +django = "^3.1" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "tencent" +default = true +url = "https://mirrors.cloud.tencent.com/pypi/simple/" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" -- Gitee From facd205b71f8000b9deee722132bc79098c7a8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 11:13:58 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/__init__.py | 7 +++++++ django_kelove_setting/apps.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 django_kelove_setting/__init__.py create mode 100644 django_kelove_setting/apps.py diff --git a/django_kelove_setting/__init__.py b/django_kelove_setting/__init__.py new file mode 100644 index 0000000..2fdc354 --- /dev/null +++ b/django_kelove_setting/__init__.py @@ -0,0 +1,7 @@ +""" +__init__.py +By IT小强xqitw.cn +At 1/24/21 11:10 AM +""" + +default_app_config = "django_kelove_setting.apps.DjangoKeloveSettingConfig" diff --git a/django_kelove_setting/apps.py b/django_kelove_setting/apps.py new file mode 100644 index 0000000..e4c34ba --- /dev/null +++ b/django_kelove_setting/apps.py @@ -0,0 +1,18 @@ +""" +apps.py +By IT小强xqitw.cn +At 1/24/21 11:11 AM +""" + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class DjangoKeloveSettingConfig(AppConfig): + """ + DjangoKeloveSettingConfig + """ + + label = 'django_kelove_setting' + name = 'django_kelove_setting' + verbose_name = _('Kelove Settings') -- Gitee From fcc58eee9d5bc938c4722125b1ff1a07f02bbac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 11:27:35 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=8F=8A=E8=BF=81=E7=A7=BB=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0001_initial.py | 34 +++++ django_kelove_setting/migrations/__init__.py | 5 + django_kelove_setting/models.py | 124 ++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 django_kelove_setting/migrations/0001_initial.py create mode 100644 django_kelove_setting/migrations/__init__.py create mode 100644 django_kelove_setting/models.py diff --git a/django_kelove_setting/migrations/0001_initial.py b/django_kelove_setting/migrations/0001_initial.py new file mode 100644 index 0000000..de2a5c0 --- /dev/null +++ b/django_kelove_setting/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# Generated by Django 3.1.5 on 2021-01-24 03:25 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Settings', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('settings_key', models.CharField(db_index=True, max_length=191, unique=True, verbose_name='配置标识')), + ('settings_title', models.CharField(blank=True, default='', max_length=191, verbose_name='配置名称')), + ('settings_val', models.JSONField(blank=True, default=dict, verbose_name='配置内容')), + ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('created_user', models.ForeignKey(blank=True, db_constraint=False, default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='django_kelove_setting_settings_created_user_set', to=settings.AUTH_USER_MODEL, verbose_name='创建用户')), + ('updated_user', models.ForeignKey(blank=True, db_constraint=False, default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='django_kelove_setting_settings_updated_user_set', to=settings.AUTH_USER_MODEL, verbose_name='更新用户')), + ], + options={ + 'verbose_name': '应用配置', + 'verbose_name_plural': '应用配置', + }, + ), + ] diff --git a/django_kelove_setting/migrations/__init__.py b/django_kelove_setting/migrations/__init__.py new file mode 100644 index 0000000..c3893fb --- /dev/null +++ b/django_kelove_setting/migrations/__init__.py @@ -0,0 +1,5 @@ +""" +__init__.py +By IT小强xqitw.cn +At 1/24/21 11:20 AM +""" diff --git a/django_kelove_setting/models.py b/django_kelove_setting/models.py new file mode 100644 index 0000000..dc36f4c --- /dev/null +++ b/django_kelove_setting/models.py @@ -0,0 +1,124 @@ +""" +models.py +By IT小强xqitw.cn +At 1/24/21 11:16 AM +""" +from django.conf import settings +from django.contrib.auth.models import Permission +from django.db import models +from django.contrib.auth import get_permission_codename +from django.contrib.admin.options import get_content_type_for_model +from django.utils.translation import gettext_lazy as _ + + +class Settings(models.Model): + """ + 配置表 + """ + + settings_key = models.CharField( + verbose_name=_('配置标识'), + unique=True, + db_index=True, + max_length=191, + blank=False, + null=False + ) + + settings_title = models.CharField( + verbose_name=_('配置名称'), + max_length=191, + default='', + blank=True, + null=False + ) + + settings_val = models.JSONField( + verbose_name=_('配置内容'), + default=dict, + null=False, + blank=True + ) + + # 创建用户 + created_user = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_('创建用户'), + related_name="%(app_label)s_%(class)s_created_user_set", + on_delete=models.SET_NULL, + db_constraint=False, + null=True, + blank=True, + editable=False, + default=None + ) + + # 更新用户 + updated_user = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_('更新用户'), + related_name="%(app_label)s_%(class)s_updated_user_set", + on_delete=models.SET_NULL, + db_constraint=False, + null=True, + blank=True, + editable=False, + default=None + ) + + # 创建时间 + created_time = models.DateTimeField(verbose_name=_('创建时间'), auto_now_add=True, editable=True) + + # 更新时间 + updated_time = models.DateTimeField(verbose_name=_('更新时间'), auto_now=True) + + def get_settings_permission_codename(self, action): + """ + 获取配置权限代码 + :param action: + :return: + """ + + return get_permission_codename(action, self._meta) + '_{key}'.format(key=self.settings_key) + + def get_settings_permission_code(self, action): + """ + 获取配置权限代码(包括app label) + :param action: + :return: + """ + + return "%s.%s" % (self._meta.app_label, self.get_settings_permission_codename(action)) + + def create_admin_settings_auth(self): + """ + 初始化配置权限 + :return: + """ + + content_type = get_content_type_for_model(self) + + Permission.objects.get_or_create( + codename=self.get_settings_permission_codename('change'), + name='Can change {value}'.format(value=self.settings_title), + content_type=content_type, + ) + + Permission.objects.get_or_create( + codename=self.get_settings_permission_codename('delete'), + name='Can delete {value}'.format(value=self.settings_title), + content_type=content_type, + ) + + Permission.objects.get_or_create( + codename=self.get_settings_permission_codename('view'), + name='Can view {value}'.format(value=self.settings_title), + content_type=content_type, + ) + + def __str__(self): + return '%s | %s' % (self.settings_title, self.settings_key) + + class Meta: + verbose_name = _('应用配置') + verbose_name_plural = _('应用配置') -- Gitee From 66a376620f4bd8fad5b24053e4d6c41b7470304d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 13:34:29 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E5=9F=BA=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/setting_forms.py | 246 +++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 django_kelove_setting/setting_forms.py diff --git a/django_kelove_setting/setting_forms.py b/django_kelove_setting/setting_forms.py new file mode 100644 index 0000000..3c4e84a --- /dev/null +++ b/django_kelove_setting/setting_forms.py @@ -0,0 +1,246 @@ +""" +settings.py +By IT小强xqitw.cn +At 1/24/21 11:44 AM +""" + +import json +from hashlib import md5 + +from django.db import ProgrammingError, OperationalError +from django.forms.models import ModelForm, model_to_dict +from django.forms.fields import CharField +from django.forms.widgets import HiddenInput +from django.conf import settings +from django.core.cache import cache +from django.utils.translation import gettext_lazy as _ + +from .models import Settings as SettingsModel + + +class Settings(ModelForm): + """ + 配置表单基类 + """ + + settings_title: str = _('未命名') + + settings_val = CharField(widget=HiddenInput(), required=False) + + def __init__(self, data=None, files=None, **kwargs): + initial = kwargs.get('initial', {}) + instance = kwargs.get('instance', None) + kwargs['initial'] = { + **initial, + **self.init_form_initial(instance=instance) + } + super().__init__(data=data, files=files, **kwargs) + + def clean(self): + settings_val = self.get() + for key, val in self.cleaned_data.items(): + if key != 'settings_val' and key in self.changed_data: + settings_val[key] = val + + self.cleaned_data['settings_val'] = settings_val + return super().clean() + + def init_form_data(self, data=None, instance=None): + """ + 初始化表单数据 + :param data: + :param instance: + :return: + """ + + # 表单数据已存在时,不做处理 + if data is not None: + return data + return self.init_form_initial(instance=instance) + + def init_form_initial(self, instance=None): + """ + 初始化表单初始值 + :param instance: + :return: + """ + + data = {} + + if not instance: + return data + + # 查询结果转为字典 + instance_data = model_to_dict(instance) + + # 循环处理配置值 + settings_val = instance_data.get('settings_val', {}) + if not settings_val: + settings_val = {} + if isinstance(settings_val, str): + try: + settings_val = json.loads(settings_val) + except json.JSONDecodeError: + settings_val = {} + + for field_name, field_info in self.base_fields.items(): + if field_name == 'settings_val': + continue + try: + data[field_name] = settings_val.get(field_name, self.get_initial(field_info)) + except AttributeError: + pass + + data['settings_key'] = instance_data['settings_key'] + data['settings_title'] = instance_data['settings_title'] + return data + + @classmethod + def get_settings_key(cls): + """ + 获取当前配置类标识 + :return: + """ + + return '{module}.{name}'.format( + module=cls.__module__, + name=cls.__name__ + ) + + @classmethod + def get_settings_title(cls, is_full=True): + """ + 获取当前配置类名称 + :param is_full: + :return: + """ + + if not is_full: + return cls.settings_title + return '{base_name}【{module}.{name}】'.format( + base_name=cls.settings_title, + module=cls.__module__, + name=cls.__name__ + ) + + @classmethod + def get_cache_key(cls): + """ + 获取缓存标识 + :return: + """ + + return 'settings_{file}_{key}_cache'.format( + file=md5(__file__.encode()).hexdigest(), + key=md5(cls.get_settings_key().encode()).hexdigest() + ) + + @classmethod + def delete_cache(cls): + """ + 删除缓存 + :return: + """ + + cache_key = cls.get_cache_key() + cache.delete(cache_key) + + @classmethod + def get(cls) -> dict: + """ + 获取配置 + :return: + """ + + cache_key = cls.get_cache_key() + cache_data = cache.get(cache_key) + if cache_data is None: + try: + data = SettingsModel.objects.get(settings_key=cls.get_settings_key()) + data = data.settings_val + except (SettingsModel.DoesNotExist, ProgrammingError, OperationalError): + data = {} + initial_data = {} + for key, val in cls.base_fields.items(): + if key == 'settings_val': + continue + try: + initial_data[key] = cls.get_initial(val) + except AttributeError: + pass + cache_data = {**initial_data, **data} + cache.set(cache_key, cache_data) + + return cache_data + + @classmethod + def get_item(cls, key: str, default=None): + """ + 获取单个配置 + :param key: + :param default: + :return: + """ + + return cls.get().get(key, default) + + @classmethod + def get_initial(cls, field): + """ + 获取初始值 + :param field: + :return: + """ + + value = getattr(field, 'initial') + if callable(value): + value = value() + return value + + @classmethod + def get_initial_with_default(cls, field, default): + """ + 获取初始值,不存在时返回 default + :param field: + :param default: + :return: + """ + + value = getattr(field, 'initial', default) + if callable(value): + value = value() + return value + + @classmethod + def change_django_settings(cls): + """ + 刷新django设置 + :return: + """ + + for key, val in cls.get().items(): + setattr(settings, key, val) + + def update_choices(self, field, value): + """ + 动态更新可选项 + :param field: + :param value: + :return: + """ + self.fields[field].choices = value + self.fields[field].widget.choices = value + self.base_fields[field].choices = value + self.base_fields[field].widget.choices = value + + def save(self, commit=True): + self.delete_cache() + return super().save(commit) + + class Meta: + """ + Meta + """ + + model = SettingsModel + fields = ['settings_val'] -- Gitee From 3eac3e72d39f7e64a4b2c70bd03bb34afb9f7ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 13:35:51 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/kelove_settings.py | 85 ++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 django_kelove_setting/kelove_settings.py diff --git a/django_kelove_setting/kelove_settings.py b/django_kelove_setting/kelove_settings.py new file mode 100644 index 0000000..44d052e --- /dev/null +++ b/django_kelove_setting/kelove_settings.py @@ -0,0 +1,85 @@ +""" +kelove_settings.py +By IT小强xqitw.cn +At 1/24/21 11:47 AM +""" + +from django.utils.translation import gettext_lazy as _ +from django.conf import settings +from django import forms + +from .setting_forms import Settings + + +class EmailSettings(Settings): + """ + 邮件配置 + """ + + settings_title: str = _('邮件配置') + + EMAIL_HOST = forms.CharField( + initial=getattr(settings, 'EMAIL_HOST', 'smtp.qq.com'), + empty_value=getattr(settings, 'EMAIL_HOST', 'smtp.qq.com'), + required=False, + label=_('邮件服务器域名'), + help_text=_('例如:smtp.qq.com') + ) + + EMAIL_PORT = forms.IntegerField( + initial=getattr(settings, 'EMAIL_PORT', 465), + required=False, + label=_('邮件服务器端口号,为数字'), + help_text=_('例如:465') + ) + + EMAIL_HOST_USER = forms.CharField( + initial=getattr(settings, 'EMAIL_HOST_USER', ''), + required=False, + label=_('发件人邮箱'), + ) + + DEFAULT_FROM_EMAIL = forms.CharField( + initial=getattr(settings, 'DEFAULT_FROM_EMAIL', ''), + required=False, + label=_('发件人地址'), + help_text=_('fred@example.com 和 Fred <fred@example.com> 形式都是合法的') + ) + + OTP_EMAIL_SENDER = forms.CharField( + initial=getattr(settings, 'OTP_EMAIL_SENDER', ''), + required=False, + label=_('一次性验证码发件人地址'), + help_text=_('留空自动使用发件人地址。fred@example.com 和 Fred <fred@example.com> 形式都是合法的') + ) + + EMAIL_HOST_PASSWORD = forms.CharField( + widget=forms.PasswordInput(render_value=True), + initial=getattr(settings, 'EMAIL_HOST_PASSWORD', ''), + required=False, + label=_('发件人授权码'), + help_text=_('发件人授权码不一定是邮箱密码') + ) + + EMAIL_USE_TLS = forms.BooleanField( + initial=getattr(settings, 'EMAIL_USE_TLS', False), + required=False, + label=_('是否启用安全链接TLS'), + help_text=_('通常端口为587 TLS/SSL是相互排斥的,因此仅将其中一个设置设置为启用即可') + ) + + EMAIL_USE_SSL = forms.BooleanField( + initial=getattr(settings, 'EMAIL_USE_SSL', True), + required=False, + label=_('是否启用安全链接SSL'), + help_text=_('通常端口为465 TLS/SSL是相互排斥的,因此仅将其中一个设置设置为启用即可') + ) + + @classmethod + def get(cls) -> dict: + data = super().get() + otp_email_sender_value = data.get('OTP_EMAIL_SENDER', '') + if not otp_email_sender_value: + otp_email_sender_value = data.get('DEFAULT_FROM_EMAIL', '') + data['OTP_EMAIL_SENDER'] = otp_email_sender_value + return data -- Gitee From c9565e788538ab3d5f02fda16c4637603d163d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 13:37:16 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/apps.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django_kelove_setting/apps.py b/django_kelove_setting/apps.py index e4c34ba..e7acf1e 100644 --- a/django_kelove_setting/apps.py +++ b/django_kelove_setting/apps.py @@ -15,4 +15,8 @@ class DjangoKeloveSettingConfig(AppConfig): label = 'django_kelove_setting' name = 'django_kelove_setting' - verbose_name = _('Kelove Settings') + verbose_name = _('应用配置') + + kelove_settings = [ + 'django_kelove_setting.kelove_settings.EmailSettings' + ] -- Gitee From 22d1505eb90d01c958f3400c15a82b9434c73fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 13:59:57 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=88=B0admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/admin.py | 132 +++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 django_kelove_setting/admin.py diff --git a/django_kelove_setting/admin.py b/django_kelove_setting/admin.py new file mode 100644 index 0000000..f11629f --- /dev/null +++ b/django_kelove_setting/admin.py @@ -0,0 +1,132 @@ +""" +admin.py +By IT小强xqitw.cn +At 1/24/21 11:29 AM +""" + +from collections import Iterable +from importlib import import_module + +from django.contrib.admin import ModelAdmin, site +from django.http import HttpResponseRedirect +from django.urls import reverse +from django.apps import apps + +from .models import Settings as SettingsModel +from .setting_forms import Settings as SettingsForm + + +def load_object(path: str): + """ + Load an object given its absolute object path, and return it. + object can be a class, function, variable or an instance. + path ie: 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware' + """ + + dot = path.rindex('.') + module, name = path[:dot], path[dot + 1:] + mod = import_module(module) + return getattr(mod, name) + + +class SettingsModelAdmin(ModelAdmin): + """ + 配置管理 + """ + + list_display = ( + 'id', + 'settings_title', + 'settings_key', + 'created_user', + 'created_time', + 'updated_user', + 'updated_time', + ) + + list_display_links = ('settings_title', 'settings_key') + + list_filter = ('created_time', 'updated_time') + + search_fields = ('settings_title', 'settings_key') + + readonly_fields = ('settings_title', 'settings_key') + + def has_change_permission(self, request, obj=None): + if not obj: + return super().has_change_permission(request, obj) + return request.user.has_perm(obj.get_settings_permission_code('change')) + + def has_delete_permission(self, request, obj=None): + if not obj: + return super().has_delete_permission(request, obj) + return request.user.has_perm(obj.get_settings_permission_code('delete')) + + def has_view_permission(self, request, obj=None): + if not obj: + return super().has_view_permission(request, obj) + return ( + request.user.has_perm(obj.get_settings_permission_code('change')) or + request.user.has_perm(obj.get_settings_permission_code('view')) + ) + + def add_view(self, request, form_url='', extra_context=None): + """ + 初始化 + :param request: + :param form_url: + :param extra_context: + :return: + """ + kelove_settings_app_key = 'kelove_settings' + app_configs = apps.get_app_configs() + kelove_settings = [ + j + for i in app_configs + for j in getattr(i, kelove_settings_app_key, []) + if + hasattr(i, kelove_settings_app_key) + and isinstance(getattr(i, kelove_settings_app_key, []), Iterable) + and not isinstance(getattr(i, kelove_settings_app_key, []), str) + ] + + for form in kelove_settings: + if isinstance(form, str): + try: + form = load_object(form) + except ModuleNotFoundError: + continue + + if not issubclass(form, SettingsForm): + continue + + obj, is_create = SettingsModel.objects.get_or_create( + settings_key=form.get_settings_key(), + settings_title=form.get_settings_title(is_full=False), + settings_val=form.get(), + created_user=request.user, + updated_user=request.user + ) + obj.create_admin_settings_auth() + form.delete_cache() + + opts = self.model._meta + obj_url = reverse( + 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name), + current_app=self.admin_site.name, + ) + return HttpResponseRedirect(obj_url) + + def get_form(self, request, obj=None, change=False, **kwargs): + if obj: + try: + form = load_object(obj.settings_key) + if issubclass(form, SettingsForm): + return form + except AttributeError: + pass + return super().get_form(request, obj, change, **kwargs) + + +if not site.is_registered(SettingsModel): + site.register(SettingsModel, SettingsModelAdmin) -- Gitee From 6011e3a6d3bbea16b6120af224c76d17e25d3830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 14:27:19 +0800 Subject: [PATCH 09/13] =?UTF-8?q?fieldsets=20=E5=8F=AF=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/admin.py | 15 +++++++++++++ django_kelove_setting/kelove_settings.py | 28 ++++++++++++++++++++++++ django_kelove_setting/setting_forms.py | 6 +++++ 3 files changed, 49 insertions(+) diff --git a/django_kelove_setting/admin.py b/django_kelove_setting/admin.py index f11629f..f8aa422 100644 --- a/django_kelove_setting/admin.py +++ b/django_kelove_setting/admin.py @@ -52,6 +52,21 @@ class SettingsModelAdmin(ModelAdmin): readonly_fields = ('settings_title', 'settings_key') + def get_fieldsets(self, request, obj=None): + """ + Hook for specifying fieldsets. + :param request: + :param obj: + :return: + """ + form = self.get_form(request=request, obj=obj) + if form.fieldsets: + return form.fieldsets + fieldsets = form.get_fieldsets(model_admin=self, request=request, obj=obj) + if fieldsets: + return fieldsets + return super().get_fieldsets(request=request, obj=obj) + def has_change_permission(self, request, obj=None): if not obj: return super().has_change_permission(request, obj) diff --git a/django_kelove_setting/kelove_settings.py b/django_kelove_setting/kelove_settings.py index 44d052e..6b1594c 100644 --- a/django_kelove_setting/kelove_settings.py +++ b/django_kelove_setting/kelove_settings.py @@ -18,6 +18,34 @@ class EmailSettings(Settings): settings_title: str = _('邮件配置') + fieldsets = ( + (_('基础配置'), { + 'fields': ( + 'EMAIL_HOST', + 'EMAIL_PORT', + 'EMAIL_HOST_USER', + 'DEFAULT_FROM_EMAIL', + 'OTP_EMAIL_SENDER', + 'EMAIL_HOST_PASSWORD', + ), + 'classes': ('extrapretty', 'wide') + }), + (_('安全链接'), { + 'fields': ( + 'EMAIL_USE_TLS', + 'EMAIL_USE_SSL', + ), + 'classes': ('extrapretty', 'wide') + }), + (_('配置信息'), { + 'fields': ( + 'settings_title', + 'settings_key', + ), + 'classes': ('collapse',) + }) + ) + EMAIL_HOST = forms.CharField( initial=getattr(settings, 'EMAIL_HOST', 'smtp.qq.com'), empty_value=getattr(settings, 'EMAIL_HOST', 'smtp.qq.com'), diff --git a/django_kelove_setting/setting_forms.py b/django_kelove_setting/setting_forms.py index 3c4e84a..7c0944f 100644 --- a/django_kelove_setting/setting_forms.py +++ b/django_kelove_setting/setting_forms.py @@ -27,6 +27,8 @@ class Settings(ModelForm): settings_val = CharField(widget=HiddenInput(), required=False) + fieldsets = () + def __init__(self, data=None, files=None, **kwargs): initial = kwargs.get('initial', {}) instance = kwargs.get('instance', None) @@ -237,6 +239,10 @@ class Settings(ModelForm): self.delete_cache() return super().save(commit) + @classmethod + def get_fieldsets(cls, model_admin, request, obj=None): + return cls.fieldsets + class Meta: """ Meta -- Gitee From eafa56f94dff1920649a5ff18f8c40c4dbf25e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 14:33:01 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=86=99=E5=85=A5=E4=BF=AE=E6=94=B9=E4=BA=BAID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/admin.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/django_kelove_setting/admin.py b/django_kelove_setting/admin.py index f8aa422..9e98149 100644 --- a/django_kelove_setting/admin.py +++ b/django_kelove_setting/admin.py @@ -142,6 +142,24 @@ class SettingsModelAdmin(ModelAdmin): pass return super().get_form(request, obj, change, **kwargs) + def save_model(self, request, obj, form, change): + """ + Given a model instance save it to the database. + 自动写入创建用户ID和更新用户ID + :param request: + :param obj: + :param form: + :param change: + :return: + """ + + if request.user: + user = request.user + obj.updated_user = user + if not change: + obj.created_user = user + super().save_model(request, obj, form, change) + if not site.is_registered(SettingsModel): site.register(SettingsModel, SettingsModelAdmin) -- Gitee From 5d290e8f7c32b04bf4e6b9b00209ded57b83a519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Sun, 24 Jan 2021 21:19:59 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/kelove_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_kelove_setting/kelove_settings.py b/django_kelove_setting/kelove_settings.py index 6b1594c..752d409 100644 --- a/django_kelove_setting/kelove_settings.py +++ b/django_kelove_setting/kelove_settings.py @@ -42,7 +42,7 @@ class EmailSettings(Settings): 'settings_title', 'settings_key', ), - 'classes': ('collapse',) + 'classes': ('extrapretty', 'wide') }) ) -- Gitee From 94a4dd74c47229956d7643b477adb3c1aac6327f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Mon, 25 Jan 2021 11:42:10 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E9=A1=B5=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- django_kelove_setting/admin.py | 2 + .../templates/kelove_setting/change_form.html | 6 +++ .../templates/kelove_setting/form_css.html | 49 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 django_kelove_setting/templates/kelove_setting/change_form.html create mode 100644 django_kelove_setting/templates/kelove_setting/form_css.html diff --git a/django_kelove_setting/admin.py b/django_kelove_setting/admin.py index 9e98149..c93663b 100644 --- a/django_kelove_setting/admin.py +++ b/django_kelove_setting/admin.py @@ -34,6 +34,8 @@ class SettingsModelAdmin(ModelAdmin): 配置管理 """ + change_form_template = 'kelove_setting/change_form.html' + list_display = ( 'id', 'settings_title', diff --git a/django_kelove_setting/templates/kelove_setting/change_form.html b/django_kelove_setting/templates/kelove_setting/change_form.html new file mode 100644 index 0000000..7755704 --- /dev/null +++ b/django_kelove_setting/templates/kelove_setting/change_form.html @@ -0,0 +1,6 @@ +{% extends 'admin/change_form.html' %} + +{% block extrastyle %} + {{ block.super }} + {% include 'kelove_setting/form_css.html' %} +{% endblock %} \ No newline at end of file diff --git a/django_kelove_setting/templates/kelove_setting/form_css.html b/django_kelove_setting/templates/kelove_setting/form_css.html new file mode 100644 index 0000000..18ccc9c --- /dev/null +++ b/django_kelove_setting/templates/kelove_setting/form_css.html @@ -0,0 +1,49 @@ + \ No newline at end of file -- Gitee From 701a4827e6349755ddba009151555c6efdc5d3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IT=E5=B0=8F=E5=BC=BAxqitw=2Ecn?= Date: Mon, 25 Jan 2021 11:57:19 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E4=BE=9D=E8=B5=96=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9648395..f56a46e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,46 @@ [tool.poetry] name = "django-kelove-setting" + version = "0.1.0" -description = "" + +description = "DJANGO 配置管理" + +license = "Apache-2.0" + authors = ["IT小强xqitw.cn "] +maintainers = ["IT小强xqitw.cn "] + +readme = "README.md" + +homepage = "https://gitee.com/itxq/django-kelove-setting" + +repository = "https://gitee.com/itxq/django-kelove-setting.git" + +documentation = "https://gitee.com/itxq/django-kelove-setting/blob/master/README.md" + +keywords = [ + 'django', + 'django-kelove', + 'django-kelove-setting', +] + +classifiers = [ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + "Natural Language :: Chinese (Simplified)", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Framework :: Django :: 3.1", +] + +packages = [ + { include = "django_kelove_setting" }, +] + [tool.poetry.dependencies] python = "^3.6" django = "^3.1" -- Gitee