How to define a default value for a custom Django

2019-02-05 15:26发布

问题:

The Django documentation mentions that you can add your own settings to django.conf.settings. So if my project's settings.py defines

APPLES = 1

I can access that with settings.APPLES in my apps in that project.

But if my settings.py doesn't define that value, accessing settings.APPLES obviously won't work. Is there some way to define a default value for APPLES that is used if there is no explicit setting in settings.py?

I'd like best to define the default value in the module/package that requires the setting.

回答1:

In my apps, I have a seperate settings.py file. In that file I have a get() function that does a look up in the projects settings.py file and if not found returns the default value.

from django.conf import settings

def get(key, default):
    return getattr(settings, key, default)


APPLES = get('APPLES', 1)

Then where I need to access APPLES I have:

from myapp import settings as myapp_settings

myapp_settings.APPLES

This allows an override in the projects settings.py, getattr will check there first and return the value if the attribute is found or use the default defined in your apps settings file.



回答2:

How about just:

getattr(app_settings, 'SOME_SETTING', 'default value')


回答3:

Here are two solutions. For both you can set settings.py files in your applications and fill them with default values.

Configure default value for a single application

Use from MYAPP import settings instead of from django.conf import settings in your code.

Edit YOURAPP/__init__.py:

from django.conf import settings as user_settings
from . import settings as default_settings

class AppSettings:
    def __getattr__(self, name):
        # If the setting you want is filled by the user, let's use it.
        if hasattr(user_settings, name):
            return getattr(user_settings, name)

        # If the setting you want has a default value, let's use it.
        if hasattr(default_settings, name):
            return getattr(default_settings, name)

        raise AttributeError("'Settings' object has no attribute '%s'" % name)

settings = AppSettings()

Configure default values for a whole project

Use from MYPROJECT import settings instead of from django.conf import settings in your code.

Edit MYPROJECT/MYPROJECT/__init__.py

import os, sys, importlib
from . import settings as user_settings

def get_local_apps():
    """Returns the locally installed apps names"""
    apps = []
    for app in user_settings.INSTALLED_APPS:
        path = os.path.join(user_settings.BASE_DIR, app)
        if os.path.exists(path) and app != __name__:
            apps.append(sys.modules[app])
    return apps

class AppSettings:
    SETTINGS_MODULE = 'settings'

    def __getattr__(self, setting_name):

        # If the setting you want is filled by the user, let's use it.
        if hasattr(user_settings, setting_name):
            return getattr(user_settings, setting_name)

        # Let's check every local app loaded by django.
        for app in get_local_apps():
            module_source = os.path.join(app.__path__[0], "%s.py" % self.SETTINGS_MODULE)
            module_binary = os.path.join(app.__path__[0], "%s.pyc" % self.SETTINGS_MODULE)
            if os.path.exists(module_source) or os.path.exists(module_binary):
                module = importlib.import_module("%s.%s" % (app.__name__, self.SETTINGS_MODULE))

                # Let's take the first default value for this setting we can find in any app
                if hasattr(module, setting_name):
                    return getattr(module, setting_name)

        raise AttributeError("'Settings' object has no attribute '%s'" % setting_name)

settings = AppSettings()

This solution may seem more easier to install, but it does not guarantee that the good default value will be returned. If several applications declare the same variable in their settings.py, you can not be sure which one will return the default value you asked.



回答4:

Starting from Mike's answer, I now wrapped the default setting handling into a class with easy to use interface.

Helper module:

from django.conf import settings

class SettingsView(object):
   class Defaults(object):
      pass

   def __init__(self):
      self.defaults = SettingsView.Defaults()

   def __getattr__(self, name):
      return getattr(settings, name, getattr(self.defaults, name))

Usage:

from localconf import SettingsView

settings = SettingsView()
settings.defaults.APPLES = 1

print settings.APPLES

This prints the value from django.conf.settings, or the default if it isn't set there. This settings object can also be used to access all the standard setting values.



回答5:

I recently had the same problem and created a Django app that is designed to be used for exactly such a case. It allows you to define default values for certain settings. It then first checks whether the setting is set in the global settings file. If not, it will return the default value.

I've extended it to also allow for some type checking or pre handling of the default value (e.g. a dotted class path can be converted to the class itself on load)

The app can be found at: https://pypi.python.org/pypi?name=django-pluggableappsettings&version=0.2.0&:action=display