简体   繁体   English

可插入应用程序的Django默认设置约定?

[英]Django default settings convention for pluggable app?

What's a djangonautic way of handling default settings in an app if one isn't defined in settings.py ?如果没有在settings.py定义,在应用程序中处理默认设置的 Django 方法是什么?

I've currently placed a default_settings file in the app and I've considered a few options.我目前已经在应用程序中放置了一个default_settings文件,并且我已经考虑了一些选项。 I'm leaning towards the first option, but there may be pitfalls I'm not aware of in using globals()我倾向于第一个选项,但在使用globals()可能存在我不知道的陷阱

I've mostly seen apps do a FOO = getattr(settings, 'FOO', False) at the top of the file that uses the setting but I think there are readability/repetition problems with this approach if the values / names are long.FOO = getattr(settings, 'FOO', False)看到应用程序在使用该设置的文件顶部执行FOO = getattr(settings, 'FOO', False)但我认为如果值/名称很长,这种方法存在可读性/重复问题。


1: Place settings in a function and iterate over locals / set globals 1:将设置放在函数中并迭代局部变量/设置全局变量

def setup_defaults():
    FOO = 'bar'
    for key, value in locals().items():
        globals()[key] = getattr(settings, key, value)

setup_defaults()

Pros:优点:

  • Only have to write var name once to pull default of same name from django settings.只需写入一次 var name 即可从 django 设置中提取相同名称的默认值。

Cons:缺点:

  • Not used to using globals() and don't know of any implications不习惯使用 globals() 并且不知道任何含义

2: Write getattr(settings, 'MY_SETTING', default_settings.MY_SETTING) every call 2:每次调用都写getattr(settings, 'MY_SETTING', default_settings.MY_SETTING)

Pros: - Very clear.优点: - 非常清楚。

Cons: - Repetitive缺点: - 重复


3: Always define settings as FOO = getattr(settings, 'FOO', '...setting here...') 3:始终将设置定义为FOO = getattr(settings, 'FOO', '...setting here...')

Pros: - Defaults are always overridden优点: - 默认值总是被覆盖

Cons:缺点:

  • Repetitive (must define var twice - once in string form, once in var)重复(必须定义 var 两次 - 一次是字符串形式,一次是 var)
  • Setting is not as readable since it's now the third argument设置不那么可读,因为它现在是第三个参数

4: Create utility function to get_or_default(setting) 4:创建实用函数来get_or_default(setting)

Pros:优点:

  • Simple简单的
  • Don't have to repeat string representation of setting不必重复设置的字符串表示

Cons:缺点:

  • Have to call it必须叫它

5: Create a settings class 5:创建设置类

class Settings(object):
    FOO = 'bar'

    def __init__(self):
         # filter out the startswith('__') of 
         # self.__dict__.items() / compare to django.conf.settings?

my_settings = Settings()

Cons:缺点:

  • Can't do from foo.bar.my_settings import FOO (actually, that's a terrible deal breaker!)不能从 foo.bar.my_settings import FOO 做(实际上,这是一个可怕的交易破坏者!)

I'd love to hear feedback.我很想听听反馈。

I think it's quite common to create a settings.py in your app's package, where you define your settings like this:我认为在你的应用程序包中创建一个settings.py是很常见的,你可以在其中定义你的设置:

from django.conf import settings
FOO = getattr(settings, 'FOO', "default_value")

In your app you can import them from your app's settings module:在您的应用中,您可以从应用的settings模块中导入它们:

from myapp.settings import *

def print_foo():
    print FOO

But I think everybody agrees that Django is lacking a better generic architecture for this!但我认为每个人都同意 Django 缺乏一个更好的通用架构! If you're looking for a more sophisticated way to handle this, there are some third party apps for this like django-appconf , but it's your decision if you want to introduce one more dependency for your app or not!如果你正在寻找一种更复杂的方法来处理这个问题,有一些第三方应用程序可以解决这个问题,比如django-appconf ,但是你是否想为你的应用程序引入更多的依赖是你的决定!

Updated for 2020 2020 年更新

In settings.py , put settings.* before the property.settings.py ,将settings.*放在属性之前。

from django.conf import settings
settings.FOO = getattr(settings, 'FOO', "default_value")

It seems that every solution I see there tends to create an internal copy of application settings, proxy, wrap or whatever.似乎我在那里看到的每个解决方案都倾向于创建应用程序设置、代理、包装或其他任何内容的内部副本。 This is confusing and creates problems when settings are modified in run time like they do in tests .当设置在运行时像在测试中那样修改时,这会令人困惑并且会产生问题。

To me all settings belong in django.conf.settings and only there.对我来说,所有设置都属于django.conf.settings并且只在那里。 You should not read them from somewhere else nor copy it for later use (as they may change).您不应该从其他地方阅读它们,也不应该复制它以备后用(因为它们可能会改变)。 You should set them once and don't bother about defaults later on.您应该设置它们一次,以后不要担心默认值。

I understand the impulse to drop the app prefix when app setting is used internally, but this also is IMHO a bad idea.我理解在内部使用应用程序设置时删除应用程序前缀的冲动,但这也是恕我直言一个坏主意。 When in trouble looking for SOME_APP_FOO will not yield results, as it's used just as FOO internally.当遇到麻烦时,查找SOME_APP_FOO不会产生结果,因为它在内部用作FOO Confusing right?混淆对吗? And for what, few letters?为什么,几个字母? Remember that explicit is better ?还记得显式更好吗?

IMHO the best way is to just set those defaults in Django's own settings, and why don't use piping that is already there?恕我直言,最好的方法是在 Django 自己的设置中设置这些默认值,为什么不使用已经存在的管道? No module import hooks or hijacking models.py being always imported to initialize some extra and complicated meta class piping.没有模块导入钩子或劫持models.py总是被导入来初始化一些额外和复杂的元类管道。

Why not use AppConfig.ready for setting defaults?为什么不使用AppConfig.ready来设置默认值?

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from django.conf import settings
        settings = settings._wrapped.__dict__
        settings.setdefault('FOO_BAR_SETTING', 'whatever')

Or better yet define them in clean simple way in a separate module and import them as (or close to how) Settings class does it:或者更好的是,在单独的模块中以简洁的方式定义它们,并将它们作为(或接近于) 设置类的方式导入:

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from . import app_settings as defaults
        from django.conf import settings
        for name in dir(defaults):
            if name.isupper() and not hasattr(settings, name):
                setattr(settings, name, getattr(defaults, name))

I'm not sure use of __dict__ is the best solution, but you get the idea, you can always user hasattr / setattr combo to get the efect.我不确定使用__dict__是最好的解决方案,但你明白了,你总是可以使用hasattr / setattr组合来获得效果。

This way your app settings are:这样你的应用设置是:

  1. exposed to others — if they should rely on them in some rare cases, if of course apps are configured in order rely on each other暴露给其他人——如果他们在极少数情况下应该依赖他们,当然如果应用程序被配置为相互依赖
  2. read normally as any other setting正常读取任何其他设置
  3. nicely declared in their own module在他们自己的模块中很好地声明
  4. lazy enough够懒
  5. controlled how they're are set in django.conf.settings — you can implement some transposition of names if you want to控制它们在django.conf.settings中的设置方式——如果你想,你可以实现一些名称的换位

PS.附注。 There is a warning about not modifying settings in run time but it does not explain why.有一个关于不在运行时修改设置警告,但它没有解释原因。 So I think this one time, during initialization may be a reasonable exception ;)所以我认为这一次,在初始化期间可能是一个合理的例外 ;)

PS2. PS2。 Don't name the separate module just settings as this may get confusing when you import settings from django.conf .不要将单独的模块命名为settings因为当您从django.conf导入settings时,这可能会引起混淆。

How about this?这个怎么样?

In myapp/settings.py:在 myapp/settings.py 中:

from django.conf import settings

FOO = 'bar'
BAR = 'baz'

_g = globals()
for key, value in _g.items():
    _g[key] = getattr(settings, key, value)

In myapp/other.py:在 myapp/other.py 中:

import myapp.settings

print myapp.settings.FOO

Given this answer by ncoghlan, I feel ok using globals() this way.鉴于 ncoghlan 的这个答案,我觉得这样使用 globals() 没问题。

In response to Phil Gyford's comment , exposing the problem of settings not overwritten in tests (since already imported in modules), what I did was to define an AppSettings class in __init__.py with:为了回应 Phil Gyford 的评论,暴露了在测试中没有覆盖设置的问题(因为已经导入到模块中),我所做的是在__init__.py定义一个AppSettings类:

  • an __init__ method to initialize each setting to None一个__init__方法将每个设置初始化为None
  • a load method to load every settings from getters从 getter 加载每个设置的load方法
  • static getters for each setting每个设置的静态吸气剂

Then in the code:然后在代码中:

from . import AppSettings

def in_some_function():
    some_setting = AppSettings.get_some_setting()

Or if you want to load them all in once (but overriding settings in tests won't work for the impacted module):或者,如果您想一次性加载它们(但测试中的覆盖设置不适用于受影响的模块):

from . import AppSettings

app_settings = AppSettings()
app_settings.load()

def in_some_function():
   print(app_settings.some_setting)

You can then use the override_settings decorator in your tests, and still have some DRY and clear way of using app settings, at the cost of more instructions executed each time you want to get a setting (just for tests...).然后你可以在你的测试中使用override_settings装饰器,并且仍然有一些使用应用设置的简单和清晰的方式,代价是每次你想要获取设置时执行更多指令(仅用于测试......)。

you can use django-zero-settings which lets you define your defaults and a setting key for user settings to auto-override defaults, has auto-import strings, removed settings management, cache, pre-checks, etc.您可以使用django-zero-settings来定义默认值和用户设置的设置键以自动覆盖默认值、自动导入字符串、删除设置管理、缓存、预检查等。

to create app settings like your example:像您的示例一样创建应用程序设置:

from zero_settings import ZeroSettings

app_settings = ZeroSettings(
    key="APP",
    defaults={
        "FOO": "bar"
    },
)

then you can use it like:那么你可以像这样使用它:

from app.settings import app_settings

print(app_settings.FOO)  # which prints bar

user settings will auto override defaults, like:用户设置将自动覆盖默认值,例如:

# this is settings.py, Django settings file
SECRET_KEY = "some_key"
# other settings ...
# the key `APP` is same key arg for ZeroSettings
APP = {
    "FOO": "not_bar"
}

and then:进而:

from app.settings import app_settings

print(app_settings.FOO)  # this time you get not_bar

Number 3 is best because it is the most simple one.数字 3 是最好的,因为它是最简单的。 And very consistent look.和非常一致的外观。

Number 1: it is easy to overlook it.第 1 点:很容易忽略它。 If I'll open your code and I won't scroll to the bottom I'll miss it and I will think that settings can't be overridden in my own module.如果我打开您的代码并且我不会滚动到底部,我会错过它,并且我会认为无法在我自己的模块中覆盖设置。

Number 2: is not only repetitive, it is harder to read because it is too long, also default values will be defined multiple time and scattered all over your code.数字 2:不仅是重复的,而且因为太长而难以阅读,而且默认值将被多次定义并分散在您的代码中。

Number 4: non-consistent-look, repetitive calls.数字 4:不一致的外观,重复的调用。

Number 5: Non consistent, we expect settings to be defined in a module not in a class.数字 5:不一致,我们希望设置在模块中而不是在类中定义。 Well at least I do expect to find to be defined as module because I've seen many apps using method 3, and I use it my self so I might be biased.好吧,至少我确实希望找到被定义为模块,因为我已经看到许多应用程序使用方法 3,而且我自己使用它,所以我可能有偏见。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM