Repository for example styleguide project
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 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与我们联系。
目录:
- 如何提问或提出建议?
- 这是什么?
- 项目结构
- 通用 API 相关事项
- 认证 - JWT
- 认证 - 会话
- 示例列表 API
- 文件上传
- 不使用
docker compose进行本地开发的有用命令 - 使用
docker compose进行本地开发的有用命令 - 部署
- 代码检查工具和代码格式化工具
How to ask a question or propose something?
以下是一些指引:
- 如果您遇到与 Django Styleguide Example 相关的问题 - 直接创建一个 issue。我们会回复。
- 如果您有一般性的问题或建议 - 直接创建一个 issue。我们会回复。
- 即使您不确定某个问题是否与 Django Styleguide 相关 - 无论如何,还是创建一个 issue 吧。我们会回复。
就是这样 ✨
What is this?
您好 👋
本项目具有以下用途:
- 作为我们 Django 风格指南 的示例,人们可以在这里探索实际代码,而不仅仅是代码片段。
- 作为一个 Django 项目,我们可以在这里测试各种事物和概念。您在这里看到的很多内容都被用作 HackSoft 内部项目的基础。
- 通常,某些内容就是这样最终成为 Django 风格指南 中的一个章节的。
- 作为我们 博客 所有代码示例的存放地。
- 代码片段往往会过时,而 我们希望大多数博客文章都能保持最新。 这就是为什么我们将代码放在这里,为其编写测试,并保证示例具有更长的使用寿命。
如果您想了解更多关于 Django 风格指南的信息,可以观看以下视频:
Radoslav Georgiev 的 Django structure for scale and longevity,讲述风格指南背后的理念:
Radoslav Georgiev 和 Ivaylo Bachvarov 在 HackCast 上围绕 Django 风格指南的 讨论:
结构
初始结构灵感来源于 cookiecutter-django。
当前结构是根据我们在 Django 方面的工作和生产经验进行了修改。
几个重要事项:
- Linux / Ubuntu 是我们的主要操作系统,所有内容均针对该系统进行测试。
- 它已容器化,可通过
docker compose进行本地开发。 - 它使用 Postgres 作为主要数据库。
- 它内置了
whitenoise配置,即使在本地开发环境中也能使用。 - 它已配置
mypy,并同时使用 https://github.com/typeddjango/django-stubs 和 https://github.com/typeddjango/djangorestframework-stubs/- 基本的
mypy配置位于setup.cfg mypy作为构建步骤在.github/workflows/django.yml中运行- ⚠️ 提供的配置相当基础。你应根据团队需求进行相应配置 - https://mypy.readthedocs.io/en/stable/config_file.html
- 基本的
- 它支持 GitHub Actions,基于该文章
- 它可以轻松部署到 Heroku 或 AWS ECS。
- 它包含一个示例列表 API,该 API 使用
django-filter进行过滤,并使用 DRF 提供的分页功能。 - 它内置了 Django Debug Toolbar 的配置
- 它包含使用 fakes 和 factories 编写测试的示例,基于以下文章 - https://www.hacksoft.io/blog/improve-your-tests-django-fakes-and-factories,https://www.hacksoft.io/blog/improve-your-tests-django-fakes-and-factories-advanced-usage
- 它包含如何添加 Google 登录的示例,基于以下文章 - https://www.hacksoft.io/blog/adding-google-login-to-your-existing-django-and-django-rest-framework-applications
通用 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如下:
/api/auth/jwt/login//api/auth/jwt/logout/
当前登录API的实现仅返回令牌:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InJhZG9yYWRvQGhhY2tzb2Z0LmlvIiwiaWF0IjoxNjQxMjIxMDMxLCJleHAiOjE2NDE4MjU4MzEsImp0aSI6ImIyNTEyNmY4LTM3ZDctNGI5NS04Y2M0LTkzZjI3MjE4ZGZkOSIsInVzZXJfaWQiOjJ9.TUoQQPSijO2O_3LN-Pny4wpQp-0rl4lpTs_ulkbxzO4"
}
这可以通过 auth_jwt_response_payload_handler 进行修改。
要求身份验证
我们遵循以下理念:
- 所有 API 默认都是公开的(没有默认的身份验证类)
- 如果希望某个 API 需要身份验证,只需为其添加
ApiAuthMixin。
身份验证 - 会话
本项目使用 Django 中已有的基于 cookie 的会话身份验证:
- 身份验证成功后,Django 会返回
sessionidcookie:
sessionid=5yic8rov868prmfoin2vhtg4vx35h71p; expires=Tue, 13 Apr 2021 11:17:58 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
- 从前端发起调用时,不要忘记包含凭据。例如,使用
axios时:
axios.get(url, { withCredentials: true });
axios.post(url, data, { withCredentials: true });
-
为方便起见,
CSRF_USE_SESSIONS被设置为True -
有关会话的所有配置,请查看
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
跨域
我们有以下几种常见情况:
- 当前配置在
localhost开发环境下可直接使用。 - 如果后端位于
*.domain.com且前端也位于*.domain.com,则当前配置可直接使用。 - 如果后端位于
somedomain.com而前端位于anotherdomain.com,那么你需要设置SESSION_COOKIE_SAMESITE = 'None'和SESSION_COOKIE_SECURE = True
API
- 向
/api/auth/session/login/发送POST请求,需要包含email和password的 JSON 请求体。 - 向
/api/auth/me/发送GET请求,若请求已通过认证(具有相应的sessionidcookie),则返回当前用户信息。 - 向
/api/auth/logout/发送GET或POST请求,将移除sessionidcookie,从而实现登出。
HTTP Only / SameSite
当前 /api/auth/session/login 的实现包含两个功能:
- 设置一个带有会话 ID 的
HTTP Onlycookie。 - 从 JSON 响应体中返回实际的会话 ID。
之所以需要第二个功能,是因为 Safari 浏览器不遵守 cookie 的 SameSite = None 选项。
有关此问题的更多信息,请查看 - https://www.chromium.org/updates/same-site/incompatible-clients
阅读清单
由于 cookie 可能有些难以理解,建议查看以下链接:
- https://docs.djangoproject.com/en/3.1/ref/settings/#sessions - 建议阅读每个
SESSION_*配置的描述。 - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies - 建议通读全文,并且可以多读几遍。
示例列表 API
你可以在 styleguide_example/users/apis.py 中找到 UserListApi
列表 API 位于:
http://localhost:8000/api/users/
该 API 支持过滤:
- http://localhost:8000/api/users/?is_admin=True
- http://localhost:8000/api/users/?id=1
- http://localhost:8000/api/users/?email=radorado@hacksoft.io
示例数据结构:
{
"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 中查看可用选项。
目前支持以下功能:
- 标准本地文件上传。
- 标准 S3 文件上传。
- 使用 CloudFront 作为 CDN。
- 所谓的“直接”上传,可在本地和 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
部署
本项目已准备就绪,可部署在Heroku或AWS ECS上。
Heroku
在 Heroku 上部署 Python/Django 应用相当简单,本项目已做好部署准备。
要了解 Heroku 部署的工作原理,建议先阅读此文档 - https://devcenter.heroku.com/articles/deploying-python
与 Heroku 部署相关的文件:
Procfile- 包含默认的
web、worker和beat进程。 - 此外,还有一个
release阶段,用于在发布新构建前安全地运行迁移。
- 包含默认的
.python-version- 用于指定要使用的 Python 版本。
requirements.txt- Heroku 需要根目录下的
requirements.txt,因此我们已添加该文件。
- Heroku 需要根目录下的
此外,您至少需要指定以下设置:
DJANGO_SETTINGS_MODULE,通常设为config.django.productionSECRET_KEY,应设为保密值。可参考此处获取生成思路。ALLOWED_HOSTS,通常设为默认的 Heroku 域名(例如 -hacksoft-styleguide-example.herokuapp.com)
除此之外,我们还添加了 gunicorn.conf.py,其中包含一些示例设置。
建议阅读以下资料,以了解 gunicorn 的默认值和配置:
- https://devcenter.heroku.com/articles/python-gunicorn
- https://adamj.eu/tech/2019/09/19/working-around-memory-leaks-in-your-django-app/
- https://adamj.eu/tech/2021/12/29/set-up-a-gunicorn-configuration-file-and-test-it/
- Worker 设置 - https://docs.gunicorn.org/en/latest/settings.html#worker-processes
- Gunicorn 架构简介 - https://docs.gunicorn.org/en/latest/design.html
AWS ECS
即将推出
代码检查工具与代码格式化工具
在我们所有的 Django 项目中,我们使用:
- ruff - 一款速度极快的 Python 代码检查工具和代码格式化工具,使用 Rust 编写。
- pre-commit - 一款在每次提交前触发代码检查工具的工具。
为确保上述所有工具协同工作,您需要添加一些配置:
- 在项目根目录添加
.pre-commit-config.yaml文件。您可以在其中添加pre-commit的指令。 - 在项目根目录添加
pyproject.toml文件。您可以在其中添加ruff的配置。 - 确保在您的 CI 上对每个 PR 运行代码检查工具。如果您使用 GH actions,以下是所需的配置:
- 如果您将其作为构建过程中的一个单独步骤运行:
build:
runs-on: ubuntu-latest
steps:
- name: Run ruff
uses: chartboost/ruff-action@v1
- 如果你希望将其作为另一个步骤的一部分运行,而该步骤已执行过包安装命令:
- name: Run ruff
run: ruff check .
- 最后但同样重要的是,我们强烈建议你将编辑器设置为每次保存新的 Python 文件时运行
ruff。
要测试你的本地设置是否是最新的,你可以:
- 尝试提交一次代码,看看
pre-commit是否会被触发。 - 或者在项目根目录中运行
ruff check .。

