57

this is somewhat related to this question
Why is django's settings object a LazyObject?

In my django project i have several applications. Each application can have its own non-trivial settings file.

proj/
    proj/
         settings.py
    app/
         settings.py
         views.py

What is the general best practice here?
should app/settings.py do

from django.conf import settings
APP_SETTING= lambda: settings.getattr('APP_SETTING', 'custom_value')
PROJ_SETTING= lambda: settings.PROJ_SETTING

and then in app/views.py do

import .settings 
X = settings.APP_SETTING
Y = settings.PROJ_SETTING

or should I be modifying the django lazy settings object in app/settings.py as per the django coding style?

from django.conf import settings
# not even sure how I would check for a default value that was specified in proj/settings.py
settings.configure(APP_SETTING='custom_value')

and then each app/views.py just consumes proj/settings.py via django.conf settings?

from django.conf import settings
X = settings.APP_SETTING
Y = settings.PROJ_SETTING

There are obviously quite a few other permutations but I think my intent is clear.
Thanks in advance.

Community
  • 1
  • 1
w--
  • 6,427
  • 12
  • 54
  • 92

5 Answers5

21

The simplest solution is to use the getattr(settings, 'MY_SETTING', 'my_default') trick that you mention youself. It can become a bit tedious to have to do this in multiple places, though.

Extra recommendation: use a per-app prefix like MYAPP_MY_SETTING.

There is a django app, however, that gets rid of the getattr and that handles the prefix for you. See http://django-appconf.readthedocs.org/en/latest/

Normally you create a conf.py per app with contents like this:

from django.conf import settings
from appconf import AppConf

class MyAppConf(AppConf):
    SETTING_1 = "one"
    SETTING_2 = (
        "two",
    )

And in your code:

from myapp.conf import settings

def my_view(request):
    return settings.MYAPP_SETTINGS_1  # Note the handy prefix

Should you need to customize the setting in your site, a regular entry in your site's settings.py is all you need to do:

MYAPP_SETTINGS_1 = "four, four I say"
Reinout van Rees
  • 13,486
  • 2
  • 36
  • 68
  • 1
    "django-appconf" is very nice indeed. – mbaechtold Mar 28 '14 at 10:24
  • Django creates apps.py file for every new application. This file contains a class that inherits from AppConfig. Can this be used for the same purpose? – A. B May 25 '17 at 22:36
  • 1
    No, that is a different appconfig. According to https://pypi.python.org/pypi/django-appconf, "in other words, they solve a related but different use case than django-appconf and can’t easily be used as a replacement. The similarity in name is purely coincidental." – Reinout van Rees May 26 '17 at 09:16
  • I had to use django-appconf because the getattr approach made my tests fail when I ran them all at once. – adonig Mar 19 '23 at 21:17
10

Since Django 1.7 there is a django-based structure for app-oriented configurations! You could find descriptive solution here

In this new structure, conventionally you could have an apps.py file in your applications' folder which are embeded in project, something like this:

proj/
    proj/
         settings.py
    app1/
        apps.py
        views.py
    app2/
        apps.py
        views.py

app1/apps.py file could include something like this:

from django.apps import AppConfig


class App1Config(AppConfig):
    # typical systemic configurations
    name = 'app1'
    verbose_name = 'First App'

    # your desired configurations
    OPTION_A = 'default_value'
    APP_NAMESPACE = 'APP'
    APP_OPTION_B = 4

you could have app2/apps.py something different like this:

from django.apps import AppConfig


class App2Config(AppConfig):
    # typical systemic configurations
    name = 'app2'
    verbose_name = 'Second App'

    # your desired configurations
    OTHER_CONFIGURATION = 'default_value'
    OPTION_C = 5

and so etc for other apps.pys in you Django Application folder.

It's important that you should import applications you included apps.py in, as follows:

# proj/settings.py

INSTALLED_APPS = [
    'app1.apps.App1Config',
    'app2.apps.App2Config',
    # ...
]

‌Now, You could access desired app-based configuration someway like this:

from django.apps import apps
apps.get_app_config('app1').OPTION_A
AbdolHosein
  • 528
  • 4
  • 15
  • 9
    I don't think this solution addresses OP question -- it does not allow one to override the default app configuration in their project settings. Using the code here, you will always get the app's local default configuration setting value. Ideally something like https://pypi.python.org/pypi/django-appconf would be integrated into django.apps.AppConfig so a simple, standard solution to this common problem is in-the-box. – powderflask May 25 '20 at 20:06
6

Not sure about best practices but I don't have any problems with following style:

proj/settings.py

OPTION_A = 'value'

# or with namespace
APP_NAMESPACE = 'APP'
APP_OPTION_B = 4

app/settings.py

from django.conf import settings
from django.utils.functional import SimpleLazyObject

OPTION_A = getattr(settings, 'OPTION_A', 'default_value')

# or with namespace
NAMESPACE = getattr(settings, APP_NAMESPACE, 'APP')
OPTION_B = getattr(settings, '_'.join([NAMESPACE, 'OPTION_B']), 'default_value')
OPTION_C = getattr(settings, '_'.join([NAMESPACE, 'OPTION_C']), None)
if OPTION_C is None:
    raise ImproperlyConfigured('...')

# lazy option with long initialization
OPTION_D = SimpleLazyObject(lambda: open('file.txt').read())

app/views.py

from .settings import OPTION_A, OPTION_B
# or
from . import settings as app_settings
app_settings.OPTION_C
app_settings.OPTION_D  # initialized on access
Viktor Danyliuk
  • 154
  • 2
  • 5
1

you can use django-zero-settings, it lets you define your defaults and a key for your user settings, then it will handle user overrides too.

it also auto imports your settings, has the ability to handle removed settings, and can pre-check settings too.

as an example, define your settings:

from zero_settings import ZeroSettings

app_settings = ZeroSettings(
    key="APP",
    defaults={
        "TOKEN": "token"
    },
)

then you can use it like:

from app.settings import app_settings

print(app_settings.TOKEN)
aasmpro
  • 554
  • 9
  • 21
  • 1
    For the benefit of future readers I will point out that 1) zero_settings is written by the author of this post. 2) It has not been updated in the past 12 months. 3) It is released under GPL - meaning if you choose to use zero_settings, you will need to make your own project entirely open source – Dr Phil Aug 11 '22 at 02:28
  • ty @DrPhil for the comment, actually i was too busy to update it. but for the problem with the license what do you think would be better? i can change the license or remove it if needed. i don't want anyone to be forced to do anything. – aasmpro Aug 16 '22 at 12:54
  • Hi. I am uncomfortable making recommendations on a license. It's of course your decision. A quick search on GPL vs Apache vs MIT should give you high level differences. Since my own project is close source, I can only use something that's Apache or MIT. – Dr Phil Aug 16 '22 at 13:58
  • 1
    ty @DrPhil! well then i will change it to another license, and also did you manage to use the package? and test it? i wonder if there was sth you think i can improve or if you are interested in contributing? you can find a link to msg me on my profile ;) – aasmpro Aug 16 '22 at 14:33
0

My site is just starting and I want a the simplest but flexible configuration solution. That's what I came across with.

# in the end of the site's settings.py
. . .
# Custom settings
MYAPP_CONFIG_FILE = "/home/user/config/myapp_config.ini"

In my application's models.py:

from django.conf import settings
import configparser

config = configparser.ConfigParser()
config.read(settings.MYAPP_CONFIG_FILE, encoding='utf_8')

This config parser is described here. It's convenient enough but definitely not the only option.

Nick Legend
  • 789
  • 1
  • 7
  • 21