Django-Styleguide-Example:基于 Django 生态的风格指南示例项目

Repository for example styleguide project

分支10Tags0
文件最后提交记录最后更新时间
10 个月前
10 个月前
2 年前
3 年前
10 个月前
6 年前
10 个月前
3 年前
10 个月前
11 个月前
6 年前
10 个月前
1 年前
2 年前
10 个月前
3 年前
1 年前
2 年前
4 年前
3 年前
10 个月前

Django Styleguide Example

👀 您的 Django 项目需要帮助吗? HackSoft 随时为您服务。请通过 consulting@hacksoft.io 与我们联系。

目录:


How to ask a question or propose something?

以下是一些指引:

  1. 如果您遇到与 Django Styleguide Example 相关的问题 - 直接创建一个 issue。我们会回复。
  2. 如果您有一般性的问题或建议 - 直接创建一个 issue。我们会回复。
  3. 即使您不确定某个问题是否与 Django Styleguide 相关 - 无论如何,还是创建一个 issue 吧。我们会回复。

就是这样 ✨

What is this?

您好 👋

本项目具有以下用途:

  1. 作为我们 Django 风格指南 的示例,人们可以在这里探索实际代码,而不仅仅是代码片段。
  2. 作为一个 Django 项目,我们可以在这里测试各种事物和概念。您在这里看到的很多内容都被用作 HackSoft 内部项目的基础。
  3. 作为我们 博客 所有代码示例的存放地。
    • 代码片段往往会过时,而 我们希望大多数博客文章都能保持最新。 这就是为什么我们将代码放在这里,为其编写测试,并保证示例具有更长的使用寿命。

如果您想了解更多关于 Django 风格指南的信息,可以观看以下视频:

Radoslav Georgiev 的 Django structure for scale and longevity,讲述风格指南背后的理念:

Radoslav Georgiev 的 Django structure for scale and longevity

Radoslav Georgiev 和 Ivaylo Bachvarov 在 HackCast 上围绕 Django 风格指南的 讨论

HackCast S02E08 - Django Community & Django Styleguide

结构

初始结构灵感来源于 cookiecutter-django

当前结构是根据我们在 Django 方面的工作和生产经验进行了修改。

几个重要事项:

通用 API 相关内容

跨域资源共享(CORS)

本项目使用 django-cors-headers,其通用配置如下:

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = True

对于 production.py,我们有以下内容:

CORS_ALLOW_ALL_ORIGINS = False
CORS_ORIGIN_WHITELIST = env.list('CORS_ORIGIN_WHITELIST', default=[])

认证 - JWT

本项目使用https://github.com/Styria-Digital/django-rest-framework-jwt来实现基于JWT的认证功能。

设置

所有与JWT相关的设置都位于config/settings/jwt.py中。

⚠️ 我们强烈建议您阅读该项目文档中的完整设置页面 - https://styria-digital.github.io/django-rest-framework-jwt/#additional-settings - 以明确您的需求并为您确定合适的默认值!

默认设置还将JWT令牌包含为一个Cookie。

有关Cookie设置方式的具体细节,可在此处查看 - https://github.com/Styria-Digital/django-rest-framework-jwt/blob/master/src/rest_framework_jwt/compat.py#L43

API

与JWT相关的API如下:

  1. /api/auth/jwt/login/
  2. /api/auth/jwt/logout/

当前登录API的实现仅返回令牌:

{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InJhZG9yYWRvQGhhY2tzb2Z0LmlvIiwiaWF0IjoxNjQxMjIxMDMxLCJleHAiOjE2NDE4MjU4MzEsImp0aSI6ImIyNTEyNmY4LTM3ZDctNGI5NS04Y2M0LTkzZjI3MjE4ZGZkOSIsInVzZXJfaWQiOjJ9.TUoQQPSijO2O_3LN-Pny4wpQp-0rl4lpTs_ulkbxzO4"
}

这可以通过 auth_jwt_response_payload_handler 进行修改。

要求身份验证

我们遵循以下理念:

  1. 所有 API 默认都是公开的(没有默认的身份验证类)
  2. 如果希望某个 API 需要身份验证,只需为其添加 ApiAuthMixin

身份验证 - 会话

本项目使用 Django 中已有的基于 cookie 的会话身份验证

  1. 身份验证成功后,Django 会返回 sessionid cookie:
sessionid=5yic8rov868prmfoin2vhtg4vx35h71p; expires=Tue, 13 Apr 2021 11:17:58 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
  1. 从前端发起调用时,不要忘记包含凭据。例如,使用 axios 时:
axios.get(url, { withCredentials: true });
axios.post(url, data, { withCredentials: true });
  1. 为方便起见,CSRF_USE_SESSIONS 被设置为 True

  2. 有关会话的所有配置,请查看 config/settings/sessions.py

DRF 与重写 SessionAuthentication

由于 SessionAuthentication 的默认实现会强制执行 CSRF 检查,而这并非我们 API 所需的行为,因此我们执行了以下操作:

from rest_framework.authentication import SessionAuthentication


class CsrfExemptedSessionAuthentication(SessionAuthentication):
    """
    DRF SessionAuthentication is enforcing CSRF, which may be problematic.
    That's why we want to make sure we are exempting any kind of CSRF checks for APIs.
    """
    def enforce_csrf(self, request):
        return

然后使用它来构建一个 ApiAuthMixin,该 mixin 用于标记需要身份验证的 API:

from rest_framework.permissions import IsAuthenticated


class ApiAuthMixin:
    authentication_classes = (CsrfExemptedSessionAuthentication, )
    permission_classes = (IsAuthenticated, )

默认情况下,所有 API 均为公开的,除非你添加了 ApiAuthMixin

跨域

我们有以下几种常见情况:

  1. 当前配置在 localhost 开发环境下可直接使用。
  2. 如果后端位于 *.domain.com 且前端也位于 *.domain.com,则当前配置可直接使用。
  3. 如果后端位于 somedomain.com 而前端位于 anotherdomain.com,那么你需要设置 SESSION_COOKIE_SAMESITE = 'None'SESSION_COOKIE_SECURE = True

API

  1. /api/auth/session/login/ 发送 POST 请求,需要包含 emailpassword 的 JSON 请求体。
  2. /api/auth/me/ 发送 GET 请求,若请求已通过认证(具有相应的 sessionid cookie),则返回当前用户信息。
  3. /api/auth/logout/ 发送 GETPOST 请求,将移除 sessionid cookie,从而实现登出。

HTTP Only / SameSite

当前 /api/auth/session/login 的实现包含两个功能:

  1. 设置一个带有会话 ID 的 HTTP Only cookie。
  2. 从 JSON 响应体中返回实际的会话 ID。

之所以需要第二个功能,是因为 Safari 浏览器不遵守 cookie 的 SameSite = None 选项。

有关此问题的更多信息,请查看 - https://www.chromium.org/updates/same-site/incompatible-clients

阅读清单

由于 cookie 可能有些难以理解,建议查看以下链接:

  1. https://docs.djangoproject.com/en/3.1/ref/settings/#sessions - 建议阅读每个 SESSION_* 配置的描述。
  2. https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies - 建议通读全文,并且可以多读几遍。

示例列表 API

你可以在 styleguide_example/users/apis.py 中找到 UserListApi

列表 API 位于:

http://localhost:8000/api/users/

该 API 支持过滤:

示例数据结构:

{
    "limit": 1,
    "offset": 0,
    "count": 4,
    "next": "http://localhost:8000/api/users/?limit=1&offset=1",
    "previous": null,
    "results": [
        {
            "id": 1,
            "email": "radorado@hacksoft.io",
            "is_admin": false
        }
    ]
}

文件上传

参考这篇文章 - https://www.hacksoft.io/blog/direct-to-s3-file-upload-with-django - Django-Styleguide-Example 中提供了完善的文件上传实现。

所有相关功能都位于 files 应用中。

配置方面,所有设置都位于 config/settings/files_and_storages.py

此外,你可以在 .env.example 中查看可用选项。

目前支持以下功能:

  1. 标准本地文件上传。
  2. 标准 S3 文件上传。
  3. 使用 CloudFront 作为 CDN。
  4. 所谓的“直接”上传,可在本地和 S3 环境下工作(更多背景信息,请查看文章

你可以将此作为文件上传功能的基础进行使用。

不使用 docker compose 进行本地开发的实用命令

创建 Postgres 数据库:

sudo -u postgres createdb -O your_postgres_user_here database_name_here

如果您想重新创建数据库,可以使用引导脚本:

./scripts/bootstrap.sh your_postgres_user_here

启动 Celery:

celery -A styleguide_example.tasks worker -l info --without-gossip --without-mingle --without-heartbeat

启动 Celery Beat:

celery -A styleguide_example.tasks beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler

使用 docker compose 进行本地开发的实用命令

构建并运行所有内容

docker compose up

要运行迁移

docker compose run django python manage.py migrate

进入 Shell

docker compose run django python manage.py shell

部署

本项目已准备就绪,可部署在HerokuAWS ECS上。

Heroku

在 Heroku 上部署 Python/Django 应用相当简单,本项目已做好部署准备。

要了解 Heroku 部署的工作原理,建议先阅读此文档 - https://devcenter.heroku.com/articles/deploying-python

与 Heroku 部署相关的文件:

  1. Procfile
    • 包含默认的 webworkerbeat 进程。
    • 此外,还有一个 release 阶段,用于在发布新构建前安全地运行迁移。
  2. .python-version
    • 用于指定要使用的 Python 版本。
  3. requirements.txt
    • Heroku 需要根目录下的 requirements.txt,因此我们已添加该文件。

此外,您至少需要指定以下设置:

  1. DJANGO_SETTINGS_MODULE,通常设为 config.django.production
  2. SECRET_KEY,应设为保密值。可参考此处获取生成思路
  3. ALLOWED_HOSTS,通常设为默认的 Heroku 域名(例如 - hacksoft-styleguide-example.herokuapp.com

除此之外,我们还添加了 gunicorn.conf.py,其中包含一些示例设置。

建议阅读以下资料,以了解 gunicorn 的默认值和配置:

  1. https://devcenter.heroku.com/articles/python-gunicorn
  2. https://adamj.eu/tech/2019/09/19/working-around-memory-leaks-in-your-django-app/
  3. https://adamj.eu/tech/2021/12/29/set-up-a-gunicorn-configuration-file-and-test-it/
  4. Worker 设置 - https://docs.gunicorn.org/en/latest/settings.html#worker-processes
  5. Gunicorn 架构简介 - https://docs.gunicorn.org/en/latest/design.html

AWS ECS

即将推出

代码检查工具与代码格式化工具

在我们所有的 Django 项目中,我们使用:

  • ruff - 一款速度极快的 Python 代码检查工具和代码格式化工具,使用 Rust 编写。
  • pre-commit - 一款在每次提交前触发代码检查工具的工具。

为确保上述所有工具协同工作,您需要添加一些配置:

  1. 在项目根目录添加 .pre-commit-config.yaml 文件。您可以在其中添加 pre-commit 的指令。
  2. 在项目根目录添加 pyproject.toml 文件。您可以在其中添加 ruff 的配置。
  3. 确保在您的 CI 上对每个 PR 运行代码检查工具。如果您使用 GH actions,以下是所需的配置:
  • 如果您将其作为构建过程中的一个单独步骤运行:
build:
  runs-on: ubuntu-latest
  steps:
    - name: Run ruff
	  uses: chartboost/ruff-action@v1
  • 如果你希望将其作为另一个步骤的一部分运行,而该步骤已执行过包安装命令:
- name: Run ruff
  run: ruff check .
  1. 最后但同样重要的是,我们强烈建议你将编辑器设置为每次保存新的 Python 文件时运行 ruff

要测试你的本地设置是否是最新的,你可以:

  1. 尝试提交一次代码,看看 pre-commit 是否会被触发。
  2. 或者在项目根目录中运行 ruff check .

项目介绍

Repository for example styleguide project

定制我的领域