如何启动一个可生产的Django项目(详细教程)

740 阅读8分钟

在本教程中,我将向你展示我现在通常是如何启动和组织一个新的Django项目的。我尝试过很多不同的配置和方式来组织项目,但在过去4年左右的时间里,这一直是我常用的设置。

请注意,这并不是一个 "最佳实践 "指南,也不适合所有的使用案例。这只是我喜欢的使用Django的方式,这也是我发现的可以让你的项目健康成长的方式。

前提

通常这些是我在建立项目时考虑到的前提:

  • 代码和配置的分离
  • 多种环境(生产、暂存、开发、本地)
  • 首先是本地/开发环境
  • 国际化和本地化
  • 测试和文档
  • 静态检查和造型规则
  • 不是所有的应用程序都必须是可插拔的
  • 调试和记录

环境/模式

通常,我在我的代码中使用三个环境维度:本地测试生产。我喜欢把它看作是我如何运行项目的一种 "模式"。决定我在哪个模式下运行项目的是我目前使用的settings.py

本地

本地维度总是第一位的。它是开发人员在本地机器上使用的设置和设定。

所有的默认值和配置都必须先做,以参加本地开发环境。

我喜欢这样做的原因是,项目必须尽可能的简单,以便新员工克隆仓库,运行项目并开始编码

生产环境通常会由有经验的开发者和对代码库本身比较熟悉的人进行配置和维护。因为部署应该是自动化的,人们没有理由一次又一次地重新创建生产服务器。因此,生产设置需要一些额外的步骤和配置是完全可以的。

测试

测试环境也可以在本地使用,所以开发人员可以测试代码并运行静态检查。

测试环境的想法是将其暴露在CI环境中,如Travis CI、Circle CI、AWS Code Pipeline等。

这是一个简单的设置,你可以安装该项目并运行所有的单元测试。

生产

生产层面是真正的交易。这是在没有测试和调试工具的情况下投入使用的环境。

我也使用这个 "模式 "或维度来运行暂存服务器。

在应用到生产服务器之前,暂存服务器是你推出新功能和错误修复的地方。

这里的想法是,你的暂存服务器应该以生产模式运行,唯一的区别是你的静态/媒体服务器和数据库服务器。这可以通过改变配置来实现,例如,告诉数据库连接字符串是什么。

但最主要的是,你的代码中不应该有任何条件来检查是生产服务器还是暂存服务器。项目的运行方式应该与生产中的运行方式完全相同。


项目配置

从一开始,设置一个远程版本控制服务是个好主意。通常我会先创建远程仓库,然后在本地机器上克隆它来开始使用。

假设我们的项目叫simple ,在GitHub上创建仓库后,我会在本地机器上创建一个名为simple 的目录,然后在simple 目录中克隆仓库,就像下面的结构所示:

simple/
└── simple/  (git repo)

然后我在 Git 仓库外创建virtualenv

simple/
├── simple/
└── venv/

然后在simplevenv 目录旁边,我可能会放置一些与项目有关的其他支持文件,我不打算提交到 Git 仓库。

我这样做的原因是,销毁和重新创建/克隆虚拟环境或仓库本身都比较方便。

把虚拟环境存放在git仓库/项目根目录之外也是不错的选择,这样在使用flake8、isort、black、tox等lib的时候就不需要费心去忽略它的路径了。

你也可以使用像virtualenvwrapper 这样的工具来管理你的虚拟环境,但我更喜欢这样做,因为所有东西都在一个地方。而且如果我不再需要在我的本地机器上保留某个项目,我可以完全删除它,而不会在我的机器上留下任何与该项目有关的东西。

下一步是在virtualenv里面安装Django,这样我们就可以使用django-admin 命令了:

source venv/bin/activate
pip install django

simple 目录内(git repository被克隆的地方)启动一个新的项目:

django-admin startproject simple .

注意命令后面的. 。有必要不创建另一个名为simple 的目录。

所以现在的结构应该是这样的:

simple/                   <- (1) Wrapper directory with all project contents including the venv
├── simple/               <- (2) Project root and git repository
│   ├── .git/
│   ├── manage.py
│   └── simple/           <- (3) Project package, apps, templates, static, etc
│       ├── __init__.py
│       ├── asgi.py
│       ├── settings.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

在这一点上,我已经在项目包目录中补充了三个额外的目录:templates,staticlocale

templatesstatic 我们将在项目级和应用程序级进行管理。这些是指全局模板和静态文件。

如果你使用i18n ,将你的应用程序翻译成其他语言,那么locale 是必要的。所以这里是你要存储.mo.po 文件的地方。

所以现在的结构应该是这样的:

simple/
├── simple/
│   ├── .git/
│   ├── manage.py
│   └── simple/
│       ├── locale/
│       ├── static/
│       ├── templates/
│       ├── __init__.py
│       ├── asgi.py
│       ├── settings.py
│       ├── urls.py
│       └── wsgi.py
└── venv/
要求

在项目根目录(2)中,我喜欢创建一个名为requirements 的目录,其中包含所有的.txt 文件,像这样分解项目的依赖性。

  • base.txt:主要的依赖性,是使项目运行的严格必要条件。对所有环境都是通用的
  • tests.txt:继承自base.txt + 测试实用程序
  • local.txt:继承自tests.txt + 开发实用程序
  • production.txt:继承自base.txt + 仅用于生产的依赖性

注意,我没有一个staging.txt 的需求文件,这是因为暂存环境将使用production.txt 的需求,所以我们有一个生产环境的精确拷贝:

simple/
├── simple/
│   ├── .git/
│   ├── manage.py
│   ├── requirements/
│   │   ├── base.txt
│   │   ├── local.txt
│   │   ├── production.txt
│   │   └── tests.txt
│   └── simple/
│       ├── locale/
│       ├── static/
│       ├── templates/
│       ├── __init__.py
│       ├── asgi.py
│       ├── settings.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

现在让我们来看看这些需求文件的内容,以及无论我在开发什么类型的Django项目,我都会使用哪些python库。

base.txt

dj-database-url==0.5.0
Django==3.2.4
psycopg2-binary==2.9.1
python-decouple==3.4
pytz==2021.1
  • dj-database-url:这是一个非常方便的Django库,可以创建一个单行的数据库连接字符串,方便安全地存储在.env
  • Django:Django本身
  • psycopg2-binary:PostgreSQL是我使用Django时的首选数据库。所以我总是把它放在这里,用于我的所有环境。
  • python-decouple:一个类型化的环境变量管理器,帮助保护流向你的settings.py 模块的敏感数据。它也有助于将配置与源代码解耦。
  • pytz:用于了解时区的数据字段

test.txt

-r base.txt

black==21.6b0
coverage==5.5
factory-boy==3.2.0
flake8==3.9.2
isort==5.9.1
tox==3.23.1

-r base.txt 继承了base.txt 文件中定义的所有要求:

  • black:一个Python自动格式化器,这样你就不必为代码的样式和格式化而烦恼。它让你在编码和进行代码审查时专注于真正重要的事情。
  • 覆盖率:Lib,用于生成项目的测试覆盖率报告
  • factory-boy:一个模型工厂,帮助你设置复杂的测试用例,你所测试的代码依赖于多个模型以某种方式被设置。
  • flake8:检查代码的复杂性、PEPs、格式化规则等。
  • isort:自动格式化你的导入,所以所有的导入都是按区块组织的(标准库、Django、第三方、第一方等)。
  • tox:我使用 tox 作为 CI 工具的接口来运行所有代码检查和单元测试。

local.txt

-r tests.txt

django-debug-toolbar==3.2.1
ipython==7.25.0

-r tests.txt 继承了base.txttests.txt 文件中定义的所有需求

  • django-debug-toolbar:99%的时间我都用它来调试复杂视图的查询次数,这样你就可以优化你的数据库访问。
  • ipython:改进的Python shell。我在开发阶段一直用它来开始一些执行工作或检查代码

production.txt

-r base.txt

gunicorn==20.1.0
sentry-sdk==1.1.0

-r base.txt 继承了base.txt 文件中定义的所有要求

  • gunicorn:一个用于生产的Python WSGI HTTP服务器,在Nginx等代理服务器后面使用。
  • sentry-sdk:错误报告/日志工具,用于捕捉生产中出现的异常。
设置

在环境和模式的前提下,我也喜欢设置多个设置模块。这些将作为入口点,以确定我在哪个模式下运行项目。

simple 项目包中,我创建了一个名为settings 的新目录,并对文件进行了这样的分解:

simple/                       (1)
├── simple/                   (2)
│   ├── .git/
│   ├── manage.py
│   ├── requirements/
│   │   ├── base.txt
│   │   ├── local.txt
│   │   ├── production.txt
│   │   └── tests.txt
│   └── simple/              (3)
│       ├── locale/
│       ├── settings/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── local.py
│       │   ├── production.py
│       │   └── tests.py
│       ├── static/
│       ├── templates/
│       ├── __init__.py
│       ├── asgi.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

请注意,我删除了以前住在simple/ (3) 目录中的settings.py

大部分的代码将存在于base.py 设置模块中。

所有我们只能在base.py 中设置一次并通过python-decouple 来改变其值的东西,我们都应该保留在base.py 中,而不应该在其他设置模块中重复/覆盖。

在移除主settings.py 之后,一个很好的方法是修改manage.py 文件,将local.py 设置为默认的设置模块,这样我们仍然可以运行像python manage.py runserver 这样的命令而不需要任何其他参数。

manage.py

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'simple.settings.local')  # <- here!
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

现在让我们来看看这些设置模块中的每一个。

base.py

滚动以查看所有的文件内容

from pathlib import Path

import dj_database_url
from decouple import Csv, config

BASE_DIR = Path(__file__).resolve().parent.parent


# ==============================================================================
# CORE SETTINGS
# ==============================================================================

SECRET_KEY = config("SECRET_KEY", default="django-insecure$simple.settings.local")

DEBUG = config("DEBUG", default=True, cast=bool)

ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="127.0.0.1,localhost", cast=Csv())

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

ROOT_URLCONF = "simple.urls"

INTERNAL_IPS = ["127.0.0.1"]

WSGI_APPLICATION = "simple.wsgi.application"


# ==============================================================================
# MIDDLEWARE SETTINGS
# ==============================================================================

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]


# ==============================================================================
# TEMPLATES SETTINGS
# ==============================================================================

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]


# ==============================================================================
# DATABASES SETTINGS
# ==============================================================================

DATABASES = {
    "default": dj_database_url.config(
        default=config("DATABASE_URL", default="postgres://simple:simple@localhost:5432/simple"),
        conn_max_age=600,
    )
}


# ==============================================================================
# AUTHENTICATION AND AUTHORIZATION SETTINGS
# ==============================================================================

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]


# ==============================================================================
# I18N AND L10N SETTINGS
# ==============================================================================

LANGUAGE_CODE = config("LANGUAGE_CODE", default="en-us")

TIME_ZONE = config("TIME_ZONE", default="UTC")

USE_I18N = True

USE_L10N = True

USE_TZ = True

LOCALE_PATHS = [BASE_DIR / "locale"]


# ==============================================================================
# STATIC FILES SETTINGS
# ==============================================================================

STATIC_URL = "/static/"

STATIC_ROOT = BASE_DIR.parent.parent / "static"

STATICFILES_DIRS = [BASE_DIR / "static"]

STATICFILES_FINDERS = (
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
)


# ==============================================================================
# MEDIA FILES SETTINGS
# ==============================================================================

MEDIA_URL = "/media/"

MEDIA_ROOT = BASE_DIR.parent.parent / "media"



# ==============================================================================
# THIRD-PARTY SETTINGS
# ==============================================================================


# ==============================================================================
# FIRST-PARTY SETTINGS
# ==============================================================================

SIMPLE_ENVIRONMENT = config("SIMPLE_ENVIRONMENT", default="local")

对整个基础设置文件内容做一些评论:

  • config() 是来自python-decouple 库。它将配置暴露在环境变量中,并根据预期的数据类型检索其值。在本指南中阅读更多关于python-decouple如何使用Python解耦
  • 请看像SECRET_KEY,DEBUGALLOWED_HOSTS 这样的配置是如何默认为本地/开发环境值的。这意味着一个新的开发者不需要设置一个本地的.env ,并提供一些初始值在本地运行。
  • 在数据库设置块中,我们使用dj_database_url ,按照Django的期望,将这一行字符串翻译成Python字典。
  • 请注意,在MEDIA_ROOT ,我们是如何向上导航两个目录,在git仓库之外但在我们的项目工作区(目录simple/ (1) )内创建一个media 。所以一切都很方便,我们不会把测试上传的内容提交到我们的仓库里
  • base.py 设置的最后,我为我可能安装的第三方Django库保留了两个区块,如Django Rest Framework或Django Crispy Forms。而第一方设置是指我可能为我们的项目专门创建的自定义设置。通常我会在它们前面加上项目名称,如SIMPLE_XXX

local.py

# flake8: noqa

from .base import *

INSTALLED_APPS += ["debug_toolbar"]

MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")


# ==============================================================================
# EMAIL SETTINGS
# ==============================================================================

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

例如,我将在这里设置Django Debug Toolbar。或者将电子邮件后端设置为在控制台显示已发送的电子邮件,而不是在项目中设置一个有效的电子邮件服务器来工作。

所有只与开发过程有关的代码都在这里。

你可以用它来设置其他的lib,比如Django Silk来运行剖析,而不暴露给生产。

tests.py

# flake8: noqa

from .base import *

PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]


class DisableMigrations:
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


MIGRATION_MODULES = DisableMigrations()

在这里我添加了一些配置,帮助我们更快地运行测试案例。有时候,如果你的应用模型之间有相互依赖的关系,禁用迁移可能会不起作用,所以Django可能会在没有迁移的情况下无法创建数据库。

在一些项目中,最好在执行后保留测试数据库。

production.py

# flake8: noqa

import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

import simple
from .base import *

# ==============================================================================
# SECURITY SETTINGS
# ==============================================================================

CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True

SECURE_HSTS_SECONDS = 60 * 60 * 24 * 7 * 52  # one year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

SESSION_COOKIE_SECURE = True


# ==============================================================================
# THIRD-PARTY APPS SETTINGS
# ==============================================================================

sentry_sdk.init(
    dsn=config("SENTRY_DSN", default=""),
    environment=SIMPLE_ENVIRONMENT,
    release="simple@%s" % simple.__version__,
    integrations=[DjangoIntegration()],
)

生产设置中最重要的部分是启用Django提供的所有安全设置,我喜欢这样做,因为你不能在开发服务器上运行大多数这些配置。

另一件事是Sentry的配置。

注意simple.__version__ 上的发布。接下来我们要探讨一下我通常是如何管理项目的版本的。

版本

我喜欢重用Django的get_version 工具来做一个简单的和PEP 440投诉的版本识别。

在项目的__init__.py 模块里面:

simple/
├── simple/
│   ├── .git/
│   ├── manage.py
│   ├── requirements/
│   └── simple/
│       ├── locale/
│       ├── settings/
│       ├── static/
│       ├── templates/
│       ├── __init__.py     <-- here!
│       ├── asgi.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

你可以做这样的事情:

from django import get_version

VERSION = (1, 0, 0, "final", 0)

__version__ = get_version(VERSION)

直接从Django模块中使用get_version ,唯一的缺点是它无法解析alpha版本的git哈希值。

一个可能的解决方案是将django/utils/version.py 文件复制到你的项目中,然后你在本地导入它,这样它就能在项目文件夹中识别你的git仓库。

但这也取决于你的项目使用什么样的版本管理。如果你的项目的版本与终端用户并不真正相关,而你想为内部管理保持跟踪,比如识别Sentry问题上的版本,你可以使用基于日期的版本管理。


应用程序配置

Django应用是一个Python包,你使用设置文件中的INSTALLED_APPS "安装"。一个应用程序几乎可以存在于任何地方:在项目包内部或外部,甚至在你使用pip 安装的库中。

的确,你的Django应用程序可以在其他项目中重复使用。但这并不意味着它应该如此。不要让它破坏你的项目设计,也不要为它着迷。另外,它不一定要代表你的网站/网络应用的一个 "部分"。

有些应用没有模型,或者其他应用只有视图,这都是完全可以的。你的一些模块甚至根本不需要是Django应用。我喜欢把我的Django项目看作是一个大的Python包,并以合理的方式组织它,而不是试图把所有东西都放在可重用的应用程序中。

Django官方文档的一般建议是将你的应用程序放在项目根部(与manage.py文件一起,在本教程中以simple/ (2) 文件夹标识)。

但实际上我更喜欢在项目包内创建我的应用程序(在本教程中用simple/ (3) 文件夹标识)。我创建了一个名为apps 的模块,然后在apps 中创建我的 Django 应用程序。主要原因是,它为应用程序创建了一个很好的命名空间。它可以帮助你轻松识别某个特定的导入是你项目的一部分。此外,这个命名空间还有助于创建日志规则,以不同方式处理事件。

下面是一个我如何做的例子:

simple/                      (1)
├── simple/                  (2)
│   ├── .git/
│   ├── manage.py
│   ├── requirements/
│   └── simple/              (3)
│       ├── apps/            <-- here!
│       │   ├── __init__.py
│       │   ├── accounts/
│       │   └── core/
│       ├── locale/
│       ├── settings/
│       ├── static/
│       ├── templates/
│       ├── __init__.py
│       ├── asgi.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

在上面的例子中,文件夹accounts/core/ 是用命令django-admin startapp 创建的 Django 应用程序。

这两个应用也一直在我的项目中。accounts 应用程序是我用来替换默认的 DjangoUser 模型的,也是我最终创建密码重置、账户激活、注册等的地方。

core 应用程序我用于一般/全局的实现。例如,定义一个将在其他大多数应用程序中使用的模型。我试图让它与其他应用程序脱钩,不导入其他应用程序的资源。它通常是实现通用或可重复使用的视图和混合器的一个好地方。

使用这种方法需要注意的是,你需要在Django应用的apps.py 文件中改变应用配置的name

accounts/apps.py

from django.apps import AppConfig

class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'accounts'  # <- this is the default name created by the startapp command

你应该这样重命名它,以尊重命名空间:

from django.apps import AppConfig

class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'simple.apps.accounts'  # <- change to this!

然后在你的INSTALLED_APPS ,你要像这样创建一个对你的模型的引用:

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    
    "simple.apps.accounts",
    "simple.apps.core",
]

命名空间也有助于组织你的INSTALLED_APPS ,使你的项目应用容易被识别。

应用程序结构

这就是我的应用程序结构的样子:

simple/                              (1)
├── simple/                          (2)
│   ├── .git/
│   ├── manage.py
│   ├── requirements/
│   └── simple/                      (3)
│       ├── apps/
│       │   ├── accounts/            <- My app structure
│       │   │   ├── migrations/
│       │   │   │   └── __init__.py
│       │   │   ├── static/
│       │   │   │   └── accounts/
│       │   │   ├── templates/
│       │   │   │   └── accounts/
│       │   │   ├── tests/
│       │   │   │   ├── __init__.py
│       │   │   │   └── factories.py
│       │   │   ├── __init__.py
│       │   │   ├── admin.py
│       │   │   ├── apps.py
│       │   │   ├── constants.py
│       │   │   ├── models.py
│       │   │   └── views.py
│       │   ├── core/
│       │   └── __init__.py
│       ├── locale/
│       ├── settings/
│       ├── static/
│       ├── templates/
│       ├── __init__.py
│       ├── asgi.py
│       ├── urls.py
│       └── wsgi.py
└── venv/

我做的第一件事是创建一个名为tests 的文件夹,这样我就可以把我的测试分解成几个文件。我总是添加一个factories.py ,使用factory-boy 库创建我的模型工厂。

对于statictemplates 总是首先创建一个与应用程序同名的目录,以避免在Django收集所有静态文件并试图解决模板时发生名称冲突。

admin.py 可能在那里,也可能不在那里,这取决于我是否在使用Django Admin contrib应用程序。

其他常见的模块可能有utils.py,forms.py,managers.py,services.py 等。


代码风格和格式化

现在我将向你展示我对isort,black,flake8,coveragetox 等工具的配置。

编辑器配置

.editorconfig 文件是所有主要IDE和代码编辑器都认可的标准。它可以帮助编辑器了解项目中使用的文件格式规则是什么。

它告诉编辑器该项目是用制表符还是空格缩进。多少个空格/制表符。一行代码的最大长度是多少。

我喜欢使用Django的.editorconfig 文件。下面是它的样子。

.editorconfig

# https://editorconfig.org/

root = true

[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8

# Docstrings and comments use max_line_length = 79
[*.py]
max_line_length = 119

# Use 2 spaces for the HTML files
[*.html]
indent_size = 2

# The JSON files contain newlines inconsistently
[*.json]
indent_size = 2
insert_final_newline = ignore

[**/admin/js/vendor/**]
indent_style = ignore
indent_size = ignore

# Minified JavaScript files shouldn't be changed
[**.min.js]
indent_style = ignore
insert_final_newline = ignore

# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab

# Batch files use tabs for indentation
[*.bat]
indent_style = tab

[docs/**.txt]
max_line_length = 79

[*.yml]
indent_size = 2
Flake8

Flake8是一个Python库,它封装了PyFlakes、pycodestyle和Ned Batchelder的McCabe脚本。它是一个伟大的工具包,用于检查你的代码库的编码风格(PEP8)、编程错误(如 "已导入但未使用的库 "和 "未定义的名称")以及检查循环复杂性。

要了解更多关于flake8的信息,请看我不久前发布的这个教程。如何使用Flake8

setup.cfg

[flake8]
exclude = .git,.tox,*/migrations/*
max-line-length = 119
isort

isort是一个Python工具/库,用于按字母顺序对进口文件进行排序,并自动分成不同的部分。

要了解更多关于isort的信息,请看我不久前发布的这个教程。如何使用Python isort库

setup.cfg

[isort]
force_grid_wrap = 0
use_parentheses = true
combine_as_imports = true
include_trailing_comma = true
line_length = 119
multi_line_output = 3
skip = migrations
default_section = THIRDPARTY
known_first_party = simple
known_django = django
sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER

注意known_first_party ,它应该是你的项目名称,这样isort就可以将你的项目的进口分组。

Black

Black是一个改变生活的库,用于自动格式化你的Python应用程序。现在我在用Python编码时,不可能不使用Black。

下面是我使用的基本配置。

pyproject.toml

[tool.black]
line-length = 119
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
  /(
      \.eggs
    | \.git
    | \.hg
    | \.mypy_cache
    | \.tox
    | \.venv
    | _build
    | buck-out
    | build
    | dist
    | migrations
  )/
'''

结论

在本教程中,我描述了我在使用Django时常用的项目设置。这几乎是我现在所有项目的开始方式。

下面是最终的项目结构,供参考:

simple/
├── simple/
│   ├── .git/
│   ├── .gitignore
│   ├── .editorconfig
│   ├── manage.py
│   ├── pyproject.toml
│   ├── requirements/
│   │   ├── base.txt
│   │   ├── local.txt
│   │   ├── production.txt
│   │   └── tests.txt
│   ├── setup.cfg
│   └── simple/
│       ├── __init__.py
│       ├── apps/
│       │   ├── accounts/
│       │   │   ├── migrations/
│       │   │   │   └── __init__.py
│       │   │   ├── static/
│       │   │   │   └── accounts/
│       │   │   ├── templates/
│       │   │   │   └── accounts/
│       │   │   ├── tests/
│       │   │   │   ├── __init__.py
│       │   │   │   └── factories.py
│       │   │   ├── __init__.py
│       │   │   ├── admin.py
│       │   │   ├── apps.py
│       │   │   ├── constants.py
│       │   │   ├── models.py
│       │   │   └── views.py
│       │   ├── core/
│       │   │   ├── migrations/
│       │   │   │   └── __init__.py
│       │   │   ├── static/
│       │   │   │   └── core/
│       │   │   ├── templates/
│       │   │   │   └── core/
│       │   │   ├── tests/
│       │   │   │   ├── __init__.py
│       │   │   │   └── factories.py
│       │   │   ├── __init__.py
│       │   │   ├── admin.py
│       │   │   ├── apps.py
│       │   │   ├── constants.py
│       │   │   ├── models.py
│       │   │   └── views.py
│       │   └── __init__.py
│       ├── locale/
│       ├── settings/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── local.py
│       │   ├── production.py
│       │   └── tests.py
│       ├── static/
│       ├── templates/
│       ├── asgi.py
│       ├── urls.py
│       └── wsgi.py
└── venv/