From f0f1762753fcb5ef821e46dad0273f7ae81a1840 Mon Sep 17 00:00:00 2001 From: mark sevelj <31756570+imAsparky@users.noreply.github.com> Date: Sat, 19 Mar 2022 08:21:49 +0800 Subject: [PATCH] feat(django): Add django-constance dynamic settings #299 (#301) Add django-constance to the cookiecutter options. Update README. Add django-constance how-to. Update django-cookiecutter project inputs reference. Add example setting in base.py. Add tests for the option. closes #299 --- README.rst | 14 +++ cookiecutter.json | 4 + .../imgs/how-tos/admin-constance-setting.png | Bin 0 -> 26749 bytes .../_static/imgs/how-tos/admin-constance.png | Bin 0 -> 5967 bytes docs/source/how-tos/how-to-constance.rst | 97 ++++++++++++++++++ docs/source/how-tos/index-how-to.rst | 3 +- .../reference/reference-project-inputs.rst | 21 +++- tests/test_bake_django.py | 53 ++++++++++ .../config/requirements/base.txt | 3 +- .../config/settings/base.py | 19 +++- 10 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 docs/source/_static/imgs/how-tos/admin-constance-setting.png create mode 100644 docs/source/_static/imgs/how-tos/admin-constance.png create mode 100644 docs/source/how-tos/how-to-constance.rst diff --git a/README.rst b/README.rst index 0c1fa04c..d334f1c1 100644 --- a/README.rst +++ b/README.rst @@ -145,6 +145,20 @@ Communication .. _Read the Docs: https://readthedocs.org/ .. _Repository Status Badges: https://www.repostatus.org/#concept +Dynamic Settings +~~~~~~~~~~~~~~~~ + +Choose to add `django-constance`_ for dynamic Django settings, which allows +you to: + +#. Easily migrate your static settings to dynamic settings. +#. Edit dynamic settings in the Django Admin interface. + +See our `django-constance How-to`_ for more information. + +.. _django-constance: https://django-constance.readthedocs.io/en/latest/index.html +.. _django-constance How-to: https://django-cookiecutter.readthedocs.io/en/latest/how-tos/how-to-constance.html + Contributing ------------ diff --git a/cookiecutter.json b/cookiecutter.json index 497e5911..ffe65fd0 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -18,6 +18,10 @@ "False" ], "SITE_ID": "1", + "use_constance": [ + "n", + "y" + ], "deploy_with_docker": [ "n", "y", diff --git a/docs/source/_static/imgs/how-tos/admin-constance-setting.png b/docs/source/_static/imgs/how-tos/admin-constance-setting.png new file mode 100644 index 0000000000000000000000000000000000000000..09bc71b1291dc183c903b4c0825ab9755aca3dbc GIT binary patch literal 26749 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 Q35E2ujs74!c=-H303E#fzyJUM literal 0 HcmV?d00001 diff --git a/docs/source/_static/imgs/how-tos/admin-constance.png b/docs/source/_static/imgs/how-tos/admin-constance.png new file mode 100644 index 0000000000000000000000000000000000000000..f58b4cb77da05dd1df1815f73d0e9c356c1c4e56 GIT binary patch literal 5967 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 literal 0 HcmV?d00001 diff --git a/docs/source/how-tos/how-to-constance.rst b/docs/source/how-tos/how-to-constance.rst new file mode 100644 index 00000000..64acd1f9 --- /dev/null +++ b/docs/source/how-tos/how-to-constance.rst @@ -0,0 +1,97 @@ +.. include:: /extras.rst.txt +.. highlight:: rst +.. index:: how-to-constance ; Index + +.. _how-to-constance: + +========= +Constance +========= + + +django-constance provides the convenience of dynamic site settings, +accessible in the Admin panel. + +There are many configuration options available; for more, see further +reading below. + +django-cookiecutter employs the django-constance database configuration option. + +Adding dynamic settings is done in `config/settings/base.py`. + + +.. code-block:: python + :linenos: + + CONSTANCE_CONFIG = { + 'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' + 'The Universe, and Everything', int), + } + +In the example above, provided in `config/settings/base.py`, + +`THE_ANSWER`: is the dynamic settings key. + +`42`: is the default value if `THE_ANSWER` is not found in the backend. + +`Help` text displayed in the Admin panel. + 'Answer to the Ultimate Question of Life, ' + 'The Universe, and Everything' + +`int`: optional indicates the value type. + +The supported types are: + + - bool + - int + - float + - Decimal + - str + - datetime + - date + - time + + +Changing Settings +_________________ + +Settings you have provided in `base.py`, shown above, can be changed in the +Admin panel. + +**Select** `Config` in the Constance section. + +.. image:: ../_static/imgs/how-tos/admin-constance.png + :alt: Django Admin Constance + + +| + +**Change** the setting. + +The example dynamic setting provided with django-cookiecutter is below. + +.. image:: ../_static/imgs/how-tos/admin-constance-setting.png + :alt: Django Admin Constance + +| + +Further Reading +--------------- + +For additional Constance and custom configurations. + +`Constance config quickstart`_. + +Constance provides several backend options to store configuration values. +By default, it uses the `Redis` backend. django-cookiecutter uses `database`. + +`Constance backends`_. + +Use `override_config` class for testing how your app behaves with different config values. + +`Constance config for testing`_. + + +.. _Constance config quickstart: https://django-constance.readthedocs.io/en/latest/index.html +.. _Constance backends: +.. _Constance config for testing: https://django-constance.readthedocs.io/en/latest/testing.html diff --git a/docs/source/how-tos/index-how-to.rst b/docs/source/how-tos/index-how-to.rst index 622169e7..14f3471b 100644 --- a/docs/source/how-tos/index-how-to.rst +++ b/docs/source/how-tos/index-how-to.rst @@ -15,10 +15,11 @@ See below for a list of How-To for Django Cookiecutter. :titlesonly: how-to-quickstart + how-to-constance how-to-custom-user + how-to-docker-linux-cheatsheet how-to-htmx how-to-logging how-to-tailwind how-to-test-env-settings - how-to-docker-linux-cheatsheet how-to-contribute diff --git a/docs/source/reference/reference-project-inputs.rst b/docs/source/reference/reference-project-inputs.rst index 32a6a99f..768c3827 100644 --- a/docs/source/reference/reference-project-inputs.rst +++ b/docs/source/reference/reference-project-inputs.rst @@ -66,10 +66,10 @@ version The first version number. The version number appears in the documentation and semantic version release. -Django Settings ---------------- +Django Static Settings +---------------------- -"ALLOWED_HOSTS": "www.example.com", +"ALLOWED_HOSTS": "", "INTERNAL_IPS": "127.0.0.1", @@ -89,6 +89,17 @@ See `Django Settings`_ for more information. Options ------- +Django Dynamic Settings +----------------------- + +"use_constance": ["n", "y"] +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use django-constance for Dynamic Django settings. + +#. Easily migrate your static settings to dynamic settings. +#. Edit dynamic settings in the Django Admin interface. + The following Django Cookiecutter configuration options are grouped logically. Where options are in a list, the first item is the default setting. @@ -235,6 +246,8 @@ Requires a `Pyup.io`_ account linked to your GitHub project repository. "open_source_license": ~~~~~~~~~~~~~~~~~~~~~~ +Let people know about this project license arrangements. + [ 1. MIT License, 2. BSD license, @@ -244,8 +257,6 @@ Requires a `Pyup.io`_ account linked to your GitHub project repository. 6. Not open source ] -Let people know about this project license arrangements. - .. _Pyup.io: https://github.com/pyupio/pyup .. _Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0/ .. _Django Settings: https://docs.djangoproject.com/en/4.0/ref/settings/ diff --git a/tests/test_bake_django.py b/tests/test_bake_django.py index 45dc6cb9..834b0733 100644 --- a/tests/test_bake_django.py +++ b/tests/test_bake_django.py @@ -189,6 +189,59 @@ def test_baked_django_without_commit_message_file(cookies): non_default_django.project_path / ".github" ) +def test_baked_django_without_constance_config_in_base_py(cookies): + """Test django-constance not installed.""" + default_django = cookies.bake() + + list_path = default_django.project_path / "config/settings/base.py" + list_file = str(list_path.read_text().splitlines()) + + assert "constance" not in list_file + assert "constance.backends.database" not in list_file + assert "# Constance Configuration" not in list_file + + + +def test_baked_django_with_constance_config_in_base_py(cookies): + """Test django-constance installed correctly.""" + non_default_django = cookies.bake( + extra_context={ + "use_constance": "y", + } + ) + + list_path = non_default_django.project_path / "config/settings/base.py" + list_file = str(list_path.read_text().splitlines()) + + assert "constance" in list_file + assert "constance.backends.database" in list_file + assert "# Constance Configuration" in list_file + + + + +def test_baked_django_without_constance_config_in_base_txt(cookies): + """Test django-constance not installed.""" + default_django = cookies.bake() + + list_path = default_django.project_path / "config/requirements/base.txt" + list_file = str(list_path.read_text().splitlines()) + + assert 'django-constance[database]==' not in list_file + + +def test_baked_django_with_constance_config_in_base_txt(cookies): + """Test django-constance installed correctly.""" + non_default_django = cookies.bake( + extra_context={ + "use_constance": "y", + } + ) + + list_path = non_default_django.project_path / "config/requirements/base.txt" + list_file = str(list_path.read_text().splitlines()) + + assert 'django-constance[database]==' in list_file def test_baked_django_with_custom_issue_template_files(cookies): """Test Django project has custom ISSUE templates generated correctly. diff --git a/{{cookiecutter.git_project_name}}/config/requirements/base.txt b/{{cookiecutter.git_project_name}}/config/requirements/base.txt index 606d7ef3..487bf095 100644 --- a/{{cookiecutter.git_project_name}}/config/requirements/base.txt +++ b/{{cookiecutter.git_project_name}}/config/requirements/base.txt @@ -1,5 +1,6 @@ Django==4.0.3 -django-allauth==0.49.0 +django-allauth==0.49.0{% if cookiecutter.use_constance != "n" %} +django-constance[database]==2.8.0{% endif %} django-environ==0.8.1 django-htmx==1.9.0 django-tailwind==3.1.1 diff --git a/{{cookiecutter.git_project_name}}/config/settings/base.py b/{{cookiecutter.git_project_name}}/config/settings/base.py index 9e361f22..4660a536 100644 --- a/{{cookiecutter.git_project_name}}/config/settings/base.py +++ b/{{cookiecutter.git_project_name}}/config/settings/base.py @@ -62,7 +62,9 @@ # Application definition -INSTALLED_APPS = [ +INSTALLED_APPS = [{% if cookiecutter.use_constance != "n" %} + "constance", + "constance.backends.database",{% endif %} "users", "django.contrib.admin", "django.contrib.auth", @@ -142,7 +144,22 @@ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] +{% if cookiecutter.use_constance != "n" %} +# Constance Configuration +CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" + +CONSTANCE_CONFIG = { + "THE_ANSWER": ( + 42, + "Answer to the Ultimate Question of Life, The Universe, and Everything", + int, + ), +} +CONSTANCE_CONFIG_FIELDSETS = { + "The Answer": ("THE_ANSWER",), +} +{% endif %} # Internationalization # https://docs.djangoproject.com/en/4.0/topics/i18n/ {%- set language_labels = ({