Django app defaults?
<p>I'm looking for a way to have application defaults and settings that are easy to use, difficult to get wrong, and have little overhead..</p>

<p>Currently I have it organized as follows:</p>

<pre><code>myapp/defaults.py
    # application defaults
    import sys
    if sys.platform == 'win32':
         MYAPP_HOME_ROOT = os.path.dirname(os.environ['USERPROFILE'])
    else:
         MYAPP_HOME_ROOT = '/home'
</code></pre>

<p>in my project I have:</p>

<pre><code>mysite/settings.py
    from myapp.defaults import *       # import all default from myapp
    MYAPP_HOME_ROOT = '/export/home'   # overriding myapp.defaults 
</code></pre>

<p>With this setup I could import and use settings in the regular django way (<code>from django.conf import settings</code> and <code>settings.XXX</code>).</p>

<p><strong>update-3 (why we need this)</strong></p>

<ol>
<li>Default settings ("defaults"):

<ol>
<li>An application is more convenient to use if it can be configured by overriding a set of sensible default settings.</li>
<li>the application "has domain knowledge", so it makes sense for it to provide sensible defaults whenever possible.</li>
<li>it isn't convenient for a user of the application to need to provide all the settings needed by every app, it should be sufficient to override a small subset and leave the rest with default values.</li>
<li>it is <em>very</em> useful if defaults can react to the environment. You'll often want to do something different when <code>DEBUG</code> is True, but any other global setting could be useful: e.g. <code>MYAPP_IDENTICON_DIR = os.path.join(settings.MEDIA_ROOT, 'identicons')</code> (<a href="https://en.wikipedia.org/wiki/Identicon" rel="nofollow">https://en.wikipedia.org/wiki/Identicon</a>)</li>
<li>project (site/global) settings must override app-defaults, i.e. a user who defined <code>MYAPP_IDENTICON_DIR = 's3://myappbucket.example.com/identicons'</code> in the (global) <code>settings.py</code> file for their site should get this value, and not the application's default.</li>
<li>any solution that keeps close to the normal way of using settings (<code>import .. settings; settings.FOO</code>) is superior to a solution that needs new syntax (since new syntax will diverge and we would get new and unique ways to use settings from app to app).</li>
<li>the zen of python probably applies here: 

<ul>
<li>If the implementation is hard to explain, it's a bad idea.</li>
<li>If the implementation is easy to explain, it may be a good idea.</li>
</ul></li>
</ol></li>
</ol>

<p>(The original post posited the two key problems below, leaving the above assumptions unstated..)</p>

<p><em>Problem #1:</em> When running unit tests for the app there is no site however, so <code>settings</code> wouldn't have any of the <code>myapp.defaults</code>.</p>

<p><em>Problem #2:</em> There is also a big problem if <code>myapp.defaults</code> needs to use anything from settings (e.g. <code>settings.DEBUG</code>), since you can't import settings from <code>defaults.py</code> (since that would be a circular import).</p>

<p>To solve problem #1 I created a layer of indirection:</p>

<pre><code>myapp/conf.py
    from . import defaults
    from django.conf import settings

    class Conf(object):
        def __getattr__(self, attr):
            try:
                return getattr(settings, attr)
            except AttributeError:
                return getattr(defaults, attr)
    conf = Conf()  # !&lt;-- create Conf instance
</code></pre>

<p>and usage:</p>

<pre><code>myapp/views.py
    from .conf import conf as settings
    ...
    print settings.MYAPP_HOME_ROOT   # will print '/export/home' when used from mysite
</code></pre>

<p>This allows the <code>conf.py</code> file to work with an "empty" settings file too, and the myapp code can continue using the familiar <code>settings.XXX</code>.</p>

<p>It doesn't solve problem #2, defining application settings based on e.g. <code>settings.DEBUG</code>. My current solution is to add to the <code>Conf</code> class:</p>

<pre><code>    from . import defaults
    from django.conf import settings

    class Conf(object):
        def __getattr__(self, attr):
            try:
                return getattr(settings, attr)
            except AttributeError:
                return getattr(defaults, attr)

        if settings.DEBUG:
            MYAPP_HOME_ROOT = '/Users'
        else:
            MYAPP_HOME_ROOT = '/data/media'
    conf = Conf()  # !&lt;-- create Conf instance
</code></pre>

<p>but this is not satisfying since mysite can no longer override the setting, and myapp's defaults/settings are now spread over two files...</p>

<p>Is there an easier way to do this?</p>

<p><strong>update-4: "just use the django test runner.."</strong></p>

<blockquote>
  <p>The app you are testing relies on the Django framework - and you cannot get around the fact that you need to bootstrap the framework first before you can test the app. Part of the bootstrapping process is creating a default settings.py and further using the django-supplied test runners to ensure that your apps are being testing in the environment that they are likely to be run.</p>
</blockquote>

<p>While that sounds like it ought to be true, it doesn't actually make much sense, e.g. there is no such thing as a default <code>settings.py</code> (at least not for a reusable app). When talking about integration testing it makes sense to test an app with the site/settings/apps/database(s)/cache(s)/resource-limits/etc. that it will encounter in production. For unit testing, however, we want to test just the unit of code that we're looking at - with as few external dependencies (and settings) as possible. The Django test runner(s) do, and should, mock out major parts of the framework, so it can't be said to be running in any "real" environment.</p>

<p>While the Django test runner(s) are great, there are a long list of issues it doesn't handle. The two biggies for us are (i) running tests sequentially is so slow that the test suite becomes unused (&lt;5mins when running in parallel, almost an hour when running sequentially), (ii) sometimes you just need to run tests on <em>big</em> databases (we restore last night's backup to a test database that the tests can run against - much too big for fixtures).</p>

<p>The people who made nose, py.test, twill, Selenium, and any of the fuzz testing tools really do know testing well, since that is their only focus. It would be a shame to not be able to draw on their collective experience.</p>

<p>I am not the first to have encountered this problem, and there doesn't look like there is an easy or common solution.  Here are two projects that have different solution:</p>

<p><strong>Update, python-oidc-provider method</strong>:</p>

<p>The python-oidc-provider package (<a href="https://github.com/juanifioren/django-oidc-provider" rel="nofollow">https://github.com/juanifioren/django-oidc-provider</a>) has another creative way to solve the app-settings/defaults problem. It uses properties to define defaults in a <code>myapp/settings.py</code> file:</p>

<pre><code>from django.conf import settings
class DefaultSettings(object):
    @property
    def MYAPP_HOME_ROOT(self):
        return ...

default_settings = DefaultSettings() 

def get(name):
    value = None
    try:
        value = getattr(default_settings, name)
        value = getattr(settings, name)
    except AttributeError:
        if value is None:
            raise Exception("Missing setting: " + name)
</code></pre>

<p>using a setting inside myapp becomes:</p>

<pre><code>from myapp import settings
print settings.get('MYAPP_HOME_ROOT')
</code></pre>

<p><em>good:</em> solves problem #2 (using settings when defining defaults), solves problem #1 (using default settings from tests).</p>

<p><em>bad:</em> different syntax for accessing settings (<code>settings.get('FOO')</code> vs the normal <code>settings.FOO</code>), myapp cannot provide defaults for settings that will be used outside of myapp (the settings you get from <code>from django.conf import settings</code> will not contain any defaults from myapp).  External code can do <code>from myapp import settings</code> to get regular settings and myapp defaults, but this breaks down if more than one app wants to do this...</p>

<p><strong>Update2, the django-appconf package:</strong>
(Note: not related to Django's AppConfig..)</p>

<p>With <code>django-appconfig</code>, app settings are created in <code>myapp/conf.py</code> (which needs to be loaded early, so you should probably import the conf.py file from models.py - since it is loaded early):</p>

<pre><code>from django.conf import settings
from appconf import AppConf
class MyAppConf(AppConf):
    HOME_ROOT = '...'
</code></pre>

<p>usage:</p>

<pre><code>from myapp.conf import settings
print settings.MYAPP_HOME_ROOT
</code></pre>

<p>AppConf will automagically add the <code>MYAPP_</code> prefix, and also automagically detect if <code>MYAPP_HOME_ROOT</code> has been redefined/overridden in the project's settings.</p>

<p><em>pro:</em> simple to use, solves problem #1 (accessing app-settings from tests), and problem #2 (using settings when defining defaults). As long as the conf.py file is loaded early, external code should be able to use defaults defined in myapp.</p>

<p><em>con:</em> significantly magical. The name of the setting in conf.py is different from its usage (since appconf automatically adds the <code>MYAPP_</code> prefix). Extenal/opaque dependency.</p>

<blockquote>
  <p>Problem #1: When running unit tests for the app there is no site
  however, so settings wouldn't have any of the myapp.defaults.</p>
</blockquote>

<p>This problem is solved by using the testing framework that comes with django (see <a href="https://docs.djangoproject.com/en/1.10/topics/testing/overview/" rel="nofollow">the documentation</a>) as it bootstraps the test environment correctly for you.</p>

<p>Keep in mind that django tests always run with <code>DEBUG=False</code></p>

<blockquote>
  <p>Problem #2: There is also a big problem if myapp.defaults needs to use
  anything from settings (e.g. settings.DEBUG), since you can't import
  settings from defaults.py (since that would be a circular import).</p>
</blockquote>

<p>This is not really a problem; once you import from <code>myapp.defaults</code> in <code>myapp.settings</code>, everything in <code>settings</code> will be in scope. So you don't to import <code>DEBUG</code>, it is already available to you as its in the global scope.</p>

python django