Start me up

From scratch

An extremely short guide to start using Concrete Setting in your application.

I. Define Settings

As a developer of an application, define the settings:

For example, my_app/app_settings.py file:

from concrete_settings import Settings

class ApplicationSettings(Settings):
    #: Turns on / off debug mode
    DEBUG: bool = False

II. Think of an end-user

What would be a convenient way to enter the application settings for an end-user? Perhaps via a JSON or YAML file or environmental variables?

# my_app/main.py

import os
import sys

from concrete_settings.contrib.sources import EnvVarSource

from .app_settings import ApplicationSettings


def main():
    """Usage: python main.py /path/to/settings.[yml|json]"""

    settings = ApplicationSettings()
    if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
        print(f"Reading settings from {sys.argv[1]}")
        settings.update(sys.argv[1])

    print("Reading settings from environmental variables")
    settings.update(EnvVarSource())

    # validate the settings,
    # raise exception if something is invalid
    settings.is_valid(raise_exception=True)

    # start application with settings
    ...
    if settings.DEBUG:
        ...
# settings.yml

DEBUG: true

III. Remember to test settings object definition

# we love pytest!

from my_app.app_settings import ApplicationSettings

def test_settings_definiton():
    ApplicationSettings()

That’s it! You are ready to start using Concrete Settings in your programs!

Django

Concrete Settings is shipped with batteries which help bootstrapping settings in a legacy or a brand new Django project. Django30Settings class reflects Django 3.0 global_settings definitions and allows quick integration with new and legacy projects.

New projects

Here is an example of starting up a new Django application with Concrete Settings. Let’s consider that a project was created by the traditional djago-admin.py startproject mysite. The project settings are defined in the good old settings.py. Why not have all Django project settings in a YAML file instead? (full source)

# mysite/django.yml

SECRET_KEY: 'xnhdv!(nm6f+y^izff1^e#kdy^v3gdgme87j*p)ahs6)t5-(32'

DEBUG: true

ALLOWED_HOSTS: []

INSTALLED_APPS:
  - django.contrib.admin
  - django.contrib.auth
  - ...

...

ROOT_URLCONF: mysite.urls

...

STATIC_URL: '/static/'

To read this file, settings.py can be modified as follows:

import os

from concrete_settings import Settings
from concrete_settings.contrib.frameworks.django30 import Django30Settings

SETTINGS_DIR = os.path.dirname(os.path.abspath(__file__))

settings = Django30Settings()

# Read settings from djano.yml
settings.update(SETTINGS_DIR + '/django.yml')

# Validate settings
settings.is_valid(raise_exception=True)

# extract settings to module's global scope
# so that Django can read them
settings.extract_to(globals())

Easy, isn’t it?

Separate application settings

Developers often put application settings to a site’s settings.py which leads to mixing up Django and Application settings. Let’s put application settings definiton to a separate file application_settings.py:

# mysite/application_settings.py

from concrete_settings import Settings

class ApplicationSettings(Settings):
    GREETING_MESSAGE: str = 'Welcome'

A corresponding application.yml would be:

# mysite/application.yml

GREETING_MESSAGE: Welcome, Concrete Settings User!

Finally we can combine Django and application settings in settings.py and load the settings from django.yml and application.yml:

# settings.py

import os

from concrete_settings.contrib.frameworks.django30 import Django30Settings

from .application_settings import AppliactionSettings


SETTINGS_DIR = os.path.dirname(os.path.abspath(__file__))


class SiteSettings(ApplicationSettings, Django30Settings):
    def validate(self):
        super().validate()
        ApplicationSettings.validate(self)
        Django30Settings.validate(self)

settings = SiteSettings()

settings.update(SETTINGS_DIR + '/django.yml')
settings.update(SETTINGS_DIR + '/application.yml')

settings.is_valid(raise_exception=True)
settings.extract_to(globals())

Legacy projects

Existing Django projects’ settings can be gradually migrated to Concrete Settings without modifying the existing configuration files at all!

The basic idea is to import the original settings attributes via from settings import *, then process the globals() with Concrete Settings:

# mysite/new_settings.py
# remember to update DJANGO_SETTINGS_MODULE

import os

from concrete_settings.contrib.frameworks.django30 import Django30Settings
from .settings import *  # import all existing application settings

SETTINGS_DIR = os.path.dirname(os.path.abspath(__file__))

settings = Django30Settings()

# load variables imported from settings.py
settings.update(globals())

settings.is_valid(raise_exception=True)

settings.extract_to(globals())

Start migrating application settings by defining an empty Settings class in application_settings.py:

from concrete_settings import Settings

class ApplicationSettings(Settings):
    ...

Update new_settings.py to separate Django and application settings:

# new_settings.py

import os

from concrete_settings.contrib.frameworks.django30 import Django30Settings

from .application_settings import ApplicationSettings

from .settings import *

SETTINGS_DIR = os.path.dirname(os.path.abspath(__file__))


class SiteSettings(ApplicationSettings, Django30Settings):
    def validate(self):
        super().validate()
        ApplicationSettings.validate(self)
        Django30Settings.validate(self)


settings = SiteSettings()

settings.update(globals())
settings.update(SETTINGS_DIR + '/application.yml')  # optional

settings.is_valid(raise_exception=True)
settings.extract_to(globals())