Crazy Import

最近发现 Django settings 在使用的时候,容易出现不经过检查的问题。 因此,想要在 settings 的基础上严格约束一下,减少问题。

在进行相关技术调研的时候,发现了django-class-settings这个库。 实现方式很有意思,于是就调研了一下,学习到了很多新的东西。

这个库的核心思想,是在代码中,把一个类转换成一个 module; 这非常酷,让我们看看这是如何实现的。

按照代码的阅读思路,我们首先看看这个包的用法…

以下是README.md中的内容;

django-class-settings aims to simplify complicated settings layouts by using classes instead of modules. Some of the benefits of using classes include:

  • Real inheritance
  • [Foolproof settings layouts][local_settings]
  • Properties
  • Implicit environment variable names

Example

# .env
export DJANGO_SECRET_KEY='*2#fz@c0w5fe8f-'
export DJANGO_DEBUG=true
# manage.py
import os
import sys

import class_settings
from class_settings import env

from django.core.management import execute_from_command_line

if __name__ == '__main__':
    env.read_env()
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
    os.environ.setdefault('DJANGO_SETTINGS_CLASS', 'MySettings')
    class_settings.setup()
    execute_from_command_line(sys.argv)
# myproject/settings.py
from class_settings import Settings, env


class MySettings(Settings):
    SECRET_KEY = env()
    DEBUG = env.bool(default=False)
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    ROOT_URLCONF = 'myproject.urls'
    WSGI_APPLICATION = 'myproject.wsgi.application'

Installation

Install it from [PyPI][pypi-url] with [pip][pip-url]:

pip install django-class-settings

我们可以看到,这个类关键入口可能在env.read_env()以及class_settings.setup()

我们首先看一下 src.class_settings.env 的代码。可以发现,这个代码用于加载 django 中的 env; 因此不是我们关注的重点。

让我们再看看关键函数 setup。

src/class_settings/__init__.py:

__all__ = ["Env", "Settings", "env", "setup"]
__version__ = "0.3.0-dev"

from .env import Env, env
from .settings import Settings


def setup():
    import sys
    from django.conf import settings
    from django.utils.functional import SimpleLazyObject
    from .importers import SettingsImporter, LazySettingsModule

    global _setup
    if _setup:
        return

    sys.meta_path.append(SettingsImporter)
    default_settings = LazySettingsModule()
    settings_module = SimpleLazyObject(lambda: default_settings.SETTINGS_MODULE)
    settings.configure(default_settings, SETTINGS_MODULE=settings_module)

    _setup = True


_setup = False

.settings.py中没有直接加载的代码,因此,我们可以直接阅读本文件。

未完待续…


也可以看看