# Managing environment-specific Python settings

**URL:** <https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447>\
**Category:** Using Django\
**Created:** [October 31, 2019, 3:04am UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447 "2019-10-31T03:04:31Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mblayman](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mblayman/32/101_2.png) [@mblayman](https://forum.djangoproject.com/u/mblayman)\
**Post date:** [October 31, 2019, 3:04am UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/1 "2019-10-31T03:04:31Z")

</div>

**How do you deal with settings changes that _manipulate Python_ for different environments?** That’s a question that came up as I was doing my [Django Twitch stream](https://www.twitch.tv/mblayman) (shameless plug 😄) tonight.

In my current project configuration, I have multiple settings files for different environments like `settings/development.py`, `settings/production.py`, and so on which inherit settings from a `settings/base.py` file. I’m trying to switch to a scheme that only uses a single settings file and reads stuff externally. I happened to select [goodconf](https://github.com/lincolnloop/goodconf), but you can totally do something similar with [django-environ](https://django-environ.readthedocs.io/en/latest/).

My trouble with this switch is that I don’t know what to do when one of my other settings files manipulated Python. For instance, here is `settings/development.py`:

```python
from settings.base import * # noqa

INTERNAL_IPS = ("127.0.0.1",)
INSTALLED_APPS += ("debug_toolbar",) # noqa
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa

```

The only substantial change that this settings file is doing is configuring [django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/stable/). To do that, I’m monkeying with `INSTALLED_APPS` and `MIDDLEWARE` at the Python level (i.e., manipulating lists).

If you use a tool like django-environ, how do you like to deal with these environment-specific customizations? It looks like [django-configurations](https://django-configurations.readthedocs.io/en/stable/) might support this with [setup methods](https://django-configurations.readthedocs.io/en/stable/patterns/#setup-methods). I’m interested to see other techniques that people use.

---

<div class="post-metadata">

**Author:** ![jeff](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jeff/32/55_2.png) [@jeff](https://forum.djangoproject.com/u/jeff)\
**Post date:** [October 31, 2019, 3:30am UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/2 "2019-10-31T03:30:24Z")

</div>

I only use two settings files in my projects.: `settings.py` and `test_settings.py`. My main `settings.py` file uses `django-environ` and has good, local defaults which are overrideable with environment variables.

While I don’t use `debug_toolbar`, I would do something like:

```auto
DEBUG = env.bool("DJANGO_DEBUG", default=False)

if DEBUG:
    INTERNAL_IPS = ("127.0.0.1",)
    INSTALLED_APPS.append("debug_toolbar") # noqa
    MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") # noqa

```

My `test_settings.py` is disabled migrations, using a faster password hasher, disables logging, and basically a bunch of other tricks to make my testsuite run as fast as possible.

Your production settings shouldn’t be in git so set them via environment variables and/or SECRETS with your hosting provider.

---

<div class="post-metadata">

**Author:** ![wsvincent](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/wsvincent/32/52_2.png) [@wsvincent](https://forum.djangoproject.com/u/wsvincent)\
**Post date:** [October 31, 2019, 2:18pm UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/3 "2019-10-31T14:18:13Z")

</div>

I strongly agree with Jeff’s approach. Using environment variables like this is much easier, to me, to reason about.

---

<div class="post-metadata">

**Author:** ![jpic](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jpic/32/96_2.png) [@jpic](https://forum.djangoproject.com/u/jpic)\
**Post date:** [January 15, 2020, 9:53pm UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/4 "2020-01-15T21:53:50Z")

</div>

> [@mblayman](#):
>
> INTERNAL\_IPS = (“127.0.0.1”,) INSTALLED\_APPS += (“debug\_toolbar”,) # noqa MIDDLEWARE.insert(0, “debug\_toolbar.middleware.DebugToolbarMiddleware”)

Why not have only one settings file like this:

```auto
if DEBUG:
    INTERNAL_IPS = ("127.0.0.1",)
    INSTALLED_APPS += ("debug_toolbar",) # noqa
    MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")

```

Or even:

```auto
try:
    import debug_toolbar
except ImportError:
    pass
else:
    INTERNAL_IPS = ("127.0.0.1",)
    INSTALLED_APPS += ("debug_toolbar",) # noqa
    MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")

```

Then you don’t need development.py

---

<div class="post-metadata">

**Author:** ![mblayman](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mblayman/32/101_2.png) [@mblayman](https://forum.djangoproject.com/u/mblayman)\
**Post date:** [January 16, 2020, 12:35am UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/5 "2020-01-16T00:35:39Z")

</div>

Yep, I’ve opted to use two files for my next project like what @jeff suggested.

---

<div class="post-metadata">

**Author:** ![jeff](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jeff/32/55_2.png) [@jeff](https://forum.djangoproject.com/u/jeff)\
**Post date:** [January 16, 2020, 3:10pm UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/6 "2020-01-16T15:10:46Z")

</div>

> [@jpic](#):
>
> Why not have only one settings file like this:

It’s a personal preference. I have worked on projects with dozens of settings per project and 1000+ line settings files that try to account for particular environments, and two feels like the right mix for me.

Splitting out your testing config makes CI/testing a bit easier because you can point pytest at the test\_settings.py and avoid having to configure extra environment variables. I try to avoid having to set any outside of `DATABASE_URL`.

I think it’s more readable too. Here’s an example of the pattern that I use in my personal and client projects: [https://github.com/jefftriplett/django-jobs/blob/master/config/test\_settings.py](https://github.com/jefftriplett/django-jobs/blob/master/config/test_settings.py)

---

<div class="post-metadata">

**Author:** ![jpic](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jpic/32/96_2.png) [@jpic](https://forum.djangoproject.com/u/jpic)\
**Post date:** [January 23, 2020, 3:27pm UTC](https://forum.djangoproject.com/t/managing-environment-specific-python-settings/447/7 "2020-01-23T15:27:12Z")

</div>

> [@jeff](#):
>
> configure extra environment variables

I thought that would be a good reason to program as much configuration unfolding as possible. Nonetheless, anything works as long as you _practice_ CI/CD, by whatever means necessary 👍

> I think it’s more readable too

That’s debatable of course but my example code has a very low complexity and few caracters so for many people I think it’s still readable.

Honestly, I don’t really read configuration files much anymore, I use the `djcli setting` command to print out setting values after interpretation. Basically it’s the same as `echo 'from django.conf import settings; print(settings.SOME_SETTING)' | manage.py shell` which I used prior to having djcli.

Anyway, splitting it in modules is fine - it worked for me prior to adhering to the 12factor practice, but 1000 lines would look ok for me given the low complexity of a programed configuration file. I recon there’s definitely a learning curve for developers who aren’t familiar with environment variables, so, that would be the only inconvenient to report in my exerience.
