django-setup-configuration


Namedjango-setup-configuration JSON
Version 0.5.0 PyPI version JSON
download
home_pageNone
SummaryPluggable configuration setup used with the django management command
upload_time2024-12-13 15:10:41
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseCopyright 2024 Maykin Media Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords django configuration
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            

Welcome to django_setup_configuration's documentation!
======================================================

:Version: 0.5.0
:Source: https://github.com/maykinmedia/django-setup-configuration
:Keywords: Configuration
:PythonVersion: 3.10

|build-status| |code-quality| |black| |coverage| |docs|

|python-versions| |django-versions| |pypi-version|

Manage your configuration via django command.

.. contents::

.. section-numbering::

Features
========

This library will allow you to define one or more "configuration steps" which declare a set of
expected configuration values and hooks which can be used to configure some part of your
project based on those settings. The steps can be run programmatically or via an
included Django management command.

It's intended usage is larger Django projects that require a significant amount of
configuration to be provisioned, as well as a way to store configuration in an
agnostic format (such as yaml) that is not tightly coupled to your Django models.

Installation
============

Requirements
------------

* Python 3.10 or above
* Django 3.2 or above
* Pydantic 2 or above
* Pydantic-settings 2.2 or above


Install
-------

1. Install from PyPI

.. code-block:: bash

    pip install django-setup-configuration

2. Add ``django_setup_configuration`` to the ``INSTALLED_APPS`` setting.


Usage
=====

Key Concepts
------------

- **Configuration Model**: A `Pydantic <https://docs.pydantic.dev/>`_ model defining the structure and validation rules for your configuration.
- **Configuration Step**: A class that implements the actual configuration logic using the validated configuration model.


Define a Configuration Model
----------------------------

.. code-block:: python

    from pydantic import Field
    from django_setup_configuration import ConfigurationModel, DjangoModelRef

    class UserConfigurationModel(ConfigurationModel):
        # A regular Pydantic field
        add_to_groups: list[str] = Field(
            default_factory=list,
            description="Groups to add the user to"
        )

        # Reference Django model fields with automatic type inference
        username = DjangoModelRef("auth.User", "username", default="admin")

        # You can optionally override the inferred type (overriding the type is
        # required for fields that can not be unambiguously mapped to a Python type,
        # such as relational fields or third-party fields).
        is_staff: Optional[int] = DjangoModelRef("auth.User", "username", default=0)
        
        # If you have no need for overriding any of the inferred attributes, you can reference model fields in a Meta class
        class Meta:
            django_model_refs = {
                User: ["password"]
            }


Field Defaults
^^^^^^^^^^^^^^

For regular Pydantic fields, you must explicitly configure defaults using  `Field
(default=...)` or `Field(default_factory=lambda: ...)` as specified in  the  `Pydantic
documentation <https://docs.pydantic.dev/2.10/concepts/fields/#default-values>`_.

**NOTE:** Marking a field as ``Optional`` or using ``... | None`` does *not* automatically 
set the field's default to `None`. You must set this explicitly if you want the field to
be optional:

.. code-block:: python

    from pydantic import Field

    class ConfigModel(ConfigurationModel):
        optional_field: int | None = DjangoModelRef(SomeModel, "some_field", default=None)

For ``DjangoModelRef``, the default value handling follows these rules:

You can provide explicit defaults using the ``default`` or ``default_factory`` kwargs,
similar to regular Pydantic fields:

.. code-block:: python

    class ConfigModel(ConfigurationModel):
        # Explicit string default
        field_with_explicit_default = DjangoModelRef(SomeModel, "some_field", default="foobar")
        
        # Explicit default factory for a list
        field_with_explicit_default_factory: list[str] = DjangoModelRef(
            SomeModel, "some_other_field", default_factory=list
        )

When no explicit default is provided, the default is derived from the referenced Django field:

1. If the Django field has an explicit default, that default will be used.

2. If no explicit default is set but the field has ``null=True`` set:
        
        a. The default will be set to ``None``
        b. The field will be optional

3. If no explicit default is provided and the field is not nullable, but has ``blank=True`` **and** it is a string-type field:

        a. The default will be an empty string
        b. The field will be optional


Create a Configuration Step
---------------------------

.. code-block:: python

    from django_setup_configuration import BaseConfigurationStep
    from django.contrib.auth.models import Group, User

    class UserConfigurationStep(BaseConfigurationStep[UserConfigurationModel]):
        """Configure initial user accounts"""

        config_model = UserConfigurationModel
        enable_setting = "user_configuration_enabled"
        namespace = "user_configuration"
        verbose_name = "User Configuration"

        def execute(self, model: UserConfigurationModel) -> None:
            # Idempotent user creation and configuration
            user_qs = User.objects.filter(username=model.username)
            if user_qs.exists():
                user = user_qs.get()
                if not user.check_password(model.password):
                    user.set_password(model.password)
                    user.save()
            else:
                user = User.objects.create_user(
                    username=model.username,
                    password=model.password,
                    is_superuser=True,
                )
            
            for group_name in model.add_to_groups:
                group = Group.objects.get(name=group_name)
                group.user_set.add(user)

Configuration Source
--------------------

Create a YAML configuration file with your settings:

.. code-block:: yaml

    user_configuration_enabled: true 
    user_configuration:
        username: alice
        password: supersecret
        add_to_groups:
            - moderators
            - editors

    some_other_step_enabled_flag: true
    some_other_step:
        foo: bar
        bar: baz

Note that you can combine settings for multiple steps in a single file. The root level
keys are exclusively used for the steps' ``enable_setting`` key, and the ``namespace``
key which encapsulates the configuration model's attributes.

Step Registration
-----------------

Register your configuration steps in Django settings:

.. code-block:: python

    SETUP_CONFIGURATION_STEPS = [
        "myapp.configuration_steps.user_configuration.UserConfigurationStep",
    ]

Note that steps will be executed in the order in which they are defined.

Execution
---------

Command Line
^^^^^^^^^^^^

.. code-block:: bash

    python manage.py setup_configuration --yaml-file /path/to/config.yaml

Programmatically
^^^^^^^^^^^^^^^^

.. code-block:: python

    from django_setup_configuration.runner import SetupConfigurationRunner

    runner = SetupConfigurationRunner(
        steps=["myapp.configuration_steps.user_configuration.UserConfigurationStep"],
        yaml_source="/path/to/config.yaml"
    )
    # Validate that the configuration settings can be loaded from the source
    runner.validate_all_requirements() 

    # Execute all steps
    runner.execute_all()

Note that regardless of the execution method, only *enabled* steps will be executed. By
default, steps are **not enabled**, so you will have to explicitly set the ``enable_setting``
flag to true for each step you intend to run.

Testing
-------

Direct Model Instantiation
^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

    def test_execute_step():
        config_model = UserConfigurationModel(
            username="alice", 
            password="supersecret", 
            add_to_groups=["moderators", "editors"]
        )
        step = UserConfigurationStep()
        step.execute(config_model)

        # Add assertions

Model Instantiation from an object or YAML
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python
    
    from django_setup_configuration.test_utils import build_step_config_from_sources

    def test_execute_step():
        config =  {
            'user_configuration_enabled': True,
            'user_configuration': {
                'username': 'alice',
                'password': 'supersecret',
                'groups': ['moderators', 'editors']
            }
        }
        config_model = build_step_config_from_sources(UserConfigurationStep, 
            object_source=config,
            # or yaml_source="/path/to/file.yaml"
            )   
        step = UserConfigurationStep()
        step.execute(config_model_instance)

        # Add assertions

Using Test Helpers
^^^^^^^^^^^^^^^^^^

.. code-block:: python

    from django_setup_configuration.test_utils import execute_single_step

    def test_execute_step():
        execute_single_step(
            UserConfigurationStep, 
            yaml_source="/path/to/test_config.yaml"
        )

        # Add assertions

Note that when using ``execute_single_step``, the enabled flag in your setting source
will be ignored and the step will be executed regardless of its presence or value.

Best Practices
==============

- **Idempotency**: Design steps that can be run multiple times without unintended side effects.
- **Validation**: You can use the full range of Pydantic's validation capabilities.
- **Modularity**: Break complex configurations into focused, manageable steps based on your domain in a way that will make sense to your users.


Local development
=================

To install and develop the library locally, use:

.. code-block:: bash

    pip install -e .[tests,coverage,docs,release]

When running management commands via ``django-admin``, make sure to add the root
directory to the python path (or use ``python -m django <command>``):

.. code-block:: bash

    export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings
    django-admin check
    # or other commands like:
    # django-admin makemessages -l nl


.. |build-status| image:: https://github.com/maykinmedia/django_setup_configuration/workflows/Run%20CI/badge.svg
    :alt: Build status
    :target: https://github.com/maykinmedia/django_setup_configuration/actions?query=workflow%3A%22Run+CI%22

.. |code-quality| image:: https://github.com/maykinmedia/django_setup_configuration/workflows/Code%20quality%20checks/badge.svg
     :alt: Code quality checks
     :target: https://github.com/maykinmedia/django_setup_configuration/actions?query=workflow%3A%22Code+quality+checks%22

.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :target: https://github.com/psf/black

.. |coverage| image:: https://codecov.io/gh/maykinmedia/django_setup_configuration/branch/main/graph/badge.svg
    :target: https://codecov.io/gh/maykinmedia/django_setup_configuration
    :alt: Coverage status

.. |docs| image:: https://readthedocs.org/projects/django_setup_configuration/badge/?version=latest
    :target: https://django_setup_configuration.readthedocs.io/en/latest/?badge=latest
    :alt: Documentation Status

.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django_setup_configuration.svg

.. |django-versions| image:: https://img.shields.io/pypi/djversions/django_setup_configuration.svg

.. |pypi-version| image:: https://img.shields.io/pypi/v/django_setup_configuration.svg
    :target: https://pypi.org/project/django_setup_configuration/

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-setup-configuration",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "Django, Configuration",
    "author": null,
    "author_email": "Maykin Media <support@maykinmedia.nl>",
    "download_url": "https://files.pythonhosted.org/packages/4b/73/61e340217bb5d15f21ed85c7297a3af84016bbe8738e503e08def3553373/django_setup_configuration-0.5.0.tar.gz",
    "platform": null,
    "description": "\n\nWelcome to django_setup_configuration's documentation!\n======================================================\n\n:Version: 0.5.0\n:Source: https://github.com/maykinmedia/django-setup-configuration\n:Keywords: Configuration\n:PythonVersion: 3.10\n\n|build-status| |code-quality| |black| |coverage| |docs|\n\n|python-versions| |django-versions| |pypi-version|\n\nManage your configuration via django command.\n\n.. contents::\n\n.. section-numbering::\n\nFeatures\n========\n\nThis library will allow you to define one or more \"configuration steps\" which declare a set of\nexpected configuration values and hooks which can be used to configure some part of your\nproject based on those settings. The steps can be run programmatically or via an\nincluded Django management command.\n\nIt's intended usage is larger Django projects that require a significant amount of\nconfiguration to be provisioned, as well as a way to store configuration in an\nagnostic format (such as yaml) that is not tightly coupled to your Django models.\n\nInstallation\n============\n\nRequirements\n------------\n\n* Python 3.10 or above\n* Django 3.2 or above\n* Pydantic 2 or above\n* Pydantic-settings 2.2 or above\n\n\nInstall\n-------\n\n1. Install from PyPI\n\n.. code-block:: bash\n\n    pip install django-setup-configuration\n\n2. Add ``django_setup_configuration`` to the ``INSTALLED_APPS`` setting.\n\n\nUsage\n=====\n\nKey Concepts\n------------\n\n- **Configuration Model**: A `Pydantic <https://docs.pydantic.dev/>`_ model defining the structure and validation rules for your configuration.\n- **Configuration Step**: A class that implements the actual configuration logic using the validated configuration model.\n\n\nDefine a Configuration Model\n----------------------------\n\n.. code-block:: python\n\n    from pydantic import Field\n    from django_setup_configuration import ConfigurationModel, DjangoModelRef\n\n    class UserConfigurationModel(ConfigurationModel):\n        # A regular Pydantic field\n        add_to_groups: list[str] = Field(\n            default_factory=list,\n            description=\"Groups to add the user to\"\n        )\n\n        # Reference Django model fields with automatic type inference\n        username = DjangoModelRef(\"auth.User\", \"username\", default=\"admin\")\n\n        # You can optionally override the inferred type (overriding the type is\n        # required for fields that can not be unambiguously mapped to a Python type,\n        # such as relational fields or third-party fields).\n        is_staff: Optional[int] = DjangoModelRef(\"auth.User\", \"username\", default=0)\n        \n        # If you have no need for overriding any of the inferred attributes, you can reference model fields in a Meta class\n        class Meta:\n            django_model_refs = {\n                User: [\"password\"]\n            }\n\n\nField Defaults\n^^^^^^^^^^^^^^\n\nFor regular Pydantic fields, you must explicitly configure defaults using  `Field\n(default=...)` or `Field(default_factory=lambda: ...)` as specified in  the  `Pydantic\ndocumentation <https://docs.pydantic.dev/2.10/concepts/fields/#default-values>`_.\n\n**NOTE:** Marking a field as ``Optional`` or using ``... | None`` does *not* automatically \nset the field's default to `None`. You must set this explicitly if you want the field to\nbe optional:\n\n.. code-block:: python\n\n    from pydantic import Field\n\n    class ConfigModel(ConfigurationModel):\n        optional_field: int | None = DjangoModelRef(SomeModel, \"some_field\", default=None)\n\nFor ``DjangoModelRef``, the default value handling follows these rules:\n\nYou can provide explicit defaults using the ``default`` or ``default_factory`` kwargs,\nsimilar to regular Pydantic fields:\n\n.. code-block:: python\n\n    class ConfigModel(ConfigurationModel):\n        # Explicit string default\n        field_with_explicit_default = DjangoModelRef(SomeModel, \"some_field\", default=\"foobar\")\n        \n        # Explicit default factory for a list\n        field_with_explicit_default_factory: list[str] = DjangoModelRef(\n            SomeModel, \"some_other_field\", default_factory=list\n        )\n\nWhen no explicit default is provided, the default is derived from the referenced Django field:\n\n1. If the Django field has an explicit default, that default will be used.\n\n2. If no explicit default is set but the field has ``null=True`` set:\n        \n        a. The default will be set to ``None``\n        b. The field will be optional\n\n3. If no explicit default is provided and the field is not nullable, but has ``blank=True`` **and** it is a string-type field:\n\n        a. The default will be an empty string\n        b. The field will be optional\n\n\nCreate a Configuration Step\n---------------------------\n\n.. code-block:: python\n\n    from django_setup_configuration import BaseConfigurationStep\n    from django.contrib.auth.models import Group, User\n\n    class UserConfigurationStep(BaseConfigurationStep[UserConfigurationModel]):\n        \"\"\"Configure initial user accounts\"\"\"\n\n        config_model = UserConfigurationModel\n        enable_setting = \"user_configuration_enabled\"\n        namespace = \"user_configuration\"\n        verbose_name = \"User Configuration\"\n\n        def execute(self, model: UserConfigurationModel) -> None:\n            # Idempotent user creation and configuration\n            user_qs = User.objects.filter(username=model.username)\n            if user_qs.exists():\n                user = user_qs.get()\n                if not user.check_password(model.password):\n                    user.set_password(model.password)\n                    user.save()\n            else:\n                user = User.objects.create_user(\n                    username=model.username,\n                    password=model.password,\n                    is_superuser=True,\n                )\n            \n            for group_name in model.add_to_groups:\n                group = Group.objects.get(name=group_name)\n                group.user_set.add(user)\n\nConfiguration Source\n--------------------\n\nCreate a YAML configuration file with your settings:\n\n.. code-block:: yaml\n\n    user_configuration_enabled: true \n    user_configuration:\n        username: alice\n        password: supersecret\n        add_to_groups:\n            - moderators\n            - editors\n\n    some_other_step_enabled_flag: true\n    some_other_step:\n        foo: bar\n        bar: baz\n\nNote that you can combine settings for multiple steps in a single file. The root level\nkeys are exclusively used for the steps' ``enable_setting`` key, and the ``namespace``\nkey which encapsulates the configuration model's attributes.\n\nStep Registration\n-----------------\n\nRegister your configuration steps in Django settings:\n\n.. code-block:: python\n\n    SETUP_CONFIGURATION_STEPS = [\n        \"myapp.configuration_steps.user_configuration.UserConfigurationStep\",\n    ]\n\nNote that steps will be executed in the order in which they are defined.\n\nExecution\n---------\n\nCommand Line\n^^^^^^^^^^^^\n\n.. code-block:: bash\n\n    python manage.py setup_configuration --yaml-file /path/to/config.yaml\n\nProgrammatically\n^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n\n    from django_setup_configuration.runner import SetupConfigurationRunner\n\n    runner = SetupConfigurationRunner(\n        steps=[\"myapp.configuration_steps.user_configuration.UserConfigurationStep\"],\n        yaml_source=\"/path/to/config.yaml\"\n    )\n    # Validate that the configuration settings can be loaded from the source\n    runner.validate_all_requirements() \n\n    # Execute all steps\n    runner.execute_all()\n\nNote that regardless of the execution method, only *enabled* steps will be executed. By\ndefault, steps are **not enabled**, so you will have to explicitly set the ``enable_setting``\nflag to true for each step you intend to run.\n\nTesting\n-------\n\nDirect Model Instantiation\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n\n    def test_execute_step():\n        config_model = UserConfigurationModel(\n            username=\"alice\", \n            password=\"supersecret\", \n            add_to_groups=[\"moderators\", \"editors\"]\n        )\n        step = UserConfigurationStep()\n        step.execute(config_model)\n\n        # Add assertions\n\nModel Instantiation from an object or YAML\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n    \n    from django_setup_configuration.test_utils import build_step_config_from_sources\n\n    def test_execute_step():\n        config =  {\n            'user_configuration_enabled': True,\n            'user_configuration': {\n                'username': 'alice',\n                'password': 'supersecret',\n                'groups': ['moderators', 'editors']\n            }\n        }\n        config_model = build_step_config_from_sources(UserConfigurationStep, \n            object_source=config,\n            # or yaml_source=\"/path/to/file.yaml\"\n            )   \n        step = UserConfigurationStep()\n        step.execute(config_model_instance)\n\n        # Add assertions\n\nUsing Test Helpers\n^^^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n\n    from django_setup_configuration.test_utils import execute_single_step\n\n    def test_execute_step():\n        execute_single_step(\n            UserConfigurationStep, \n            yaml_source=\"/path/to/test_config.yaml\"\n        )\n\n        # Add assertions\n\nNote that when using ``execute_single_step``, the enabled flag in your setting source\nwill be ignored and the step will be executed regardless of its presence or value.\n\nBest Practices\n==============\n\n- **Idempotency**: Design steps that can be run multiple times without unintended side effects.\n- **Validation**: You can use the full range of Pydantic's validation capabilities.\n- **Modularity**: Break complex configurations into focused, manageable steps based on your domain in a way that will make sense to your users.\n\n\nLocal development\n=================\n\nTo install and develop the library locally, use:\n\n.. code-block:: bash\n\n    pip install -e .[tests,coverage,docs,release]\n\nWhen running management commands via ``django-admin``, make sure to add the root\ndirectory to the python path (or use ``python -m django <command>``):\n\n.. code-block:: bash\n\n    export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings\n    django-admin check\n    # or other commands like:\n    # django-admin makemessages -l nl\n\n\n.. |build-status| image:: https://github.com/maykinmedia/django_setup_configuration/workflows/Run%20CI/badge.svg\n    :alt: Build status\n    :target: https://github.com/maykinmedia/django_setup_configuration/actions?query=workflow%3A%22Run+CI%22\n\n.. |code-quality| image:: https://github.com/maykinmedia/django_setup_configuration/workflows/Code%20quality%20checks/badge.svg\n     :alt: Code quality checks\n     :target: https://github.com/maykinmedia/django_setup_configuration/actions?query=workflow%3A%22Code+quality+checks%22\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n    :target: https://github.com/psf/black\n\n.. |coverage| image:: https://codecov.io/gh/maykinmedia/django_setup_configuration/branch/main/graph/badge.svg\n    :target: https://codecov.io/gh/maykinmedia/django_setup_configuration\n    :alt: Coverage status\n\n.. |docs| image:: https://readthedocs.org/projects/django_setup_configuration/badge/?version=latest\n    :target: https://django_setup_configuration.readthedocs.io/en/latest/?badge=latest\n    :alt: Documentation Status\n\n.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django_setup_configuration.svg\n\n.. |django-versions| image:: https://img.shields.io/pypi/djversions/django_setup_configuration.svg\n\n.. |pypi-version| image:: https://img.shields.io/pypi/v/django_setup_configuration.svg\n    :target: https://pypi.org/project/django_setup_configuration/\n",
    "bugtrack_url": null,
    "license": "Copyright 2024 Maykin Media  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
    "summary": "Pluggable configuration setup used with the django management command",
    "version": "0.5.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/maykinmedia/django-setup-configuration/issues",
        "Documentation": "http://django-setup-configuration.readthedocs.io/en/latest/",
        "Homepage": "https://github.com/maykinmedia/django-setup-configuration",
        "Source Code": "https://github.com/maykinmedia/django-setup-configuration"
    },
    "split_keywords": [
        "django",
        " configuration"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b31a9e4818228482807e3c51d46fcad86bd7d2ebcd20c9e61a360f990105349f",
                "md5": "e7720a089322b0e92818bedf1f8a0189",
                "sha256": "e756f2dd0495413123ce35faad4a4cd8ccff9c09af39fc5a4c1c5b16025b8826"
            },
            "downloads": -1,
            "filename": "django_setup_configuration-0.5.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e7720a089322b0e92818bedf1f8a0189",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 20819,
            "upload_time": "2024-12-13T15:10:38",
            "upload_time_iso_8601": "2024-12-13T15:10:38.693783Z",
            "url": "https://files.pythonhosted.org/packages/b3/1a/9e4818228482807e3c51d46fcad86bd7d2ebcd20c9e61a360f990105349f/django_setup_configuration-0.5.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4b7361e340217bb5d15f21ed85c7297a3af84016bbe8738e503e08def3553373",
                "md5": "1f67969a6ec42fd4632c54936aad3f71",
                "sha256": "0e3a95287ea4b577c34cdd8227fc823f56c40241d188abb161714c70aafa0583"
            },
            "downloads": -1,
            "filename": "django_setup_configuration-0.5.0.tar.gz",
            "has_sig": false,
            "md5_digest": "1f67969a6ec42fd4632c54936aad3f71",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 26455,
            "upload_time": "2024-12-13T15:10:41",
            "upload_time_iso_8601": "2024-12-13T15:10:41.497413Z",
            "url": "https://files.pythonhosted.org/packages/4b/73/61e340217bb5d15f21ed85c7297a3af84016bbe8738e503e08def3553373/django_setup_configuration-0.5.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-13 15:10:41",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "maykinmedia",
    "github_project": "django-setup-configuration",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "django-setup-configuration"
}
        
Elapsed time: 0.54246s