跳转至

使用 Pycharm 开发 Flask应用

预计阅读时长 : 22 分钟

配置虚拟环境

在配置虚拟环境时,我们需要协同使用 pyenvpoetry 两个工具。

pyenv ⧉ 是一个 Python 版本管理工具。它可以让你在同一台机器上安装和使用多个 Python 版本,这对于需要在不同版本的 Python 之间切换的开发者来说非常有用。

poetry ⧉ 是一个 Python 的虚拟环境和依赖管理工具。在每个独立的虚拟环境中,你可以安装和管理依赖,而不会影响到其他项目或者系统级别的 Python 环境。

虚拟环境和包管理工具选择

pyenvpyenv-virtualenv 插件也可以为每个项目创建独立的虚拟环境和依赖管理,由于历史悠久它被很多的知名开源项目采用。不过现在看来,它的配置和维护比较复杂,所以推荐使用 poetry 来取代它,可以更加智能化的实现同样的效果。

所以,pyenvpoetry 的关系是,你可以使用 pyenv 来管理你的 Python 版本,然后用 poetry创建虚拟环境后,选择使用 pyenv 创建的某个版本,以及针对某个特定项目便捷的管理依赖。

在 PyCharm 配置项目基本信息时,我们可以在 Settings -> Project -> Python Interpreter 中选择 使用 Poetry Enviroment 创建项目需要的虚拟环境。

首先,在 Base Interpreter 中选择 pyenv 创建的特定版本 Python 对应的执行文件路径,然后,在 Poetry executable 中选择系统级 poetry 执行文件的路径。在 Arm 版芯片的 Mac 上,poetry 的执行文件路径为 /opt/homebrew/bin/poetry

Poetery 使用简介

在使用 Poetry 管理 Python 项目时,完全依赖于 pyproject.toml 文件,而不是 requirements.txt 文件。所以,如果你使用 Poetry 管理项目,那么就不需要再使用 pip 来安装依赖了,而是要根据情况改用 poetry add 命令来添加依赖。

关于依赖的处理方式有一些需要注意的地方:

1. 自动添加依赖

  • 当你在代码中使用了一个新的包,并且这个包尚未在 pyproject.toml 文件的依赖列表中时,Poetry 本身并不会自动将其添加到依赖中,你需要手动执行添加依赖的操作。在 PyCharm 中,点击 import 命令安装后,就会自动添加到依赖中。
  • 如果自动添加不成功,那么当你在代码中导入了一个新库(如 import requests),并且希望将其添加到项目依赖中时,你需要运行 poetry add requests 命令。

2. 使用 pip 安装的包

  • 如果你通过 pip 直接安装了一个包,而没有使用 poetry add,那么这个包不会被自动添加到 pyproject.toml 文件中。
  • 为了保持依赖的一致性和项目的可移植性,建议总是通过 poetry add 命令来添加依赖,而不是直接使用 pip

3. 手动添加依赖

  • 如果你已经通过 pip 安装了一些包,而这些包还没有在 pyproject.toml 中列出,你应该手动运行 poetry add <package-name> 来添加这些包。这样可以确保你的 pyproject.toml 文件反映了项目的真实依赖状态。

4. 同步依赖

  • 一旦你更新了 pyproject.toml 文件,你可以运行 poetry install 来确保所有列出的依赖都被安装在项目的虚拟环境中。

总之,虽然 Poetry 提供了强大的依赖管理功能,但它不会自动从你的代码或通过 pip 安装的包中推断依赖。你需要明确地使用 poetry add 命令来管理项目的依赖。这种方法有助于保持依赖的清晰和一致性,特别是在团队协作和项目迁移的场景中。

配置环境变量

如果我们使用 PyCharm 进行 Flask 应用开发,并在之后使用 Docker 进行部署,那么处理环境变量的最佳实践如下:

在 PyCharm 中设置环境变量

我们可以直接在 PyCharm 的运行配置 Run Configurations中设置环境变量。这对于本地开发和测试特别有用,因为它允许定义只在 PyCharm 内部有效的环境变量。

这种方法使得本地开发时不需要依赖 .env 文件或者其他外部方式来设置环境变量,而是可以直接在 PyCharm 的 GUI 中管理这些变量。

HOST 与 PORT 配置

  • FLASK_RUN_HOST 的变量值建议设置为 0.0.0.0,这样可以允许外部访问。尤其是使用 Docker 作为最终部署方案,这个变量值考虑到外部访问的需求。
  • FLASK_RUN_PORT 的变量值建议设置为 8080,因为 PyCharm 中 EndPoint 中自动生成 HTTP Client 时,默认的端口号为 8080

在 Docker 中设置环境变量

在部署到 Docker 时,可以在 Dockerfile 以及 docker-compose.yml 文件中设置环境变量。

使用 docker-compose.yml 是一个很好的实践,因为它允许非常灵活的进行环境变量的配置,尤其是对生产环境更为合适。具体的配置方式,详见后文的线上部署部分。

这种环境变量配置的最佳实践,结合了 PyCharm 强大的开发功能和 Docker 的灵活部署能力,使得整个开发到部署的过程更加顺畅和高效。

创建项目框架

PyCharm 虽然可以直接创建 Flask 项目,但是它的项目结构过于简单,并适合项目的长期迭代,所以我们需要对其进行调整。

项目结构

脚手架工具

在创建项目时,我们可以使用 Yeoman ⧉ 来自动化生成项目结构,这样可以节省很多时间。

虽然这个工具基于 Node.js,不像 Cookiecutter 那样是 Python 专用,但胜在扩展性强,用来生成 Python 项目结构也是非常方便的。

测试应用端点

在使用 PyCharm 进行 Flask 应用开发时,我们可以使用配合使用 Endpoints 面板和 HTTP Client 功能来进行 Endpoint 的测试。

首先,在 Endpoints 面板中,可以看到所有的 Endpoint 列表,包括 GETPOSTPUTDELETE 等。点击 HTTP Client 标签,可以看到自动生成的 HTTP Client 代码。

可以直接在 Endpoints 面板中进行 Endpoint 的测试,更建议将代码拷入到 yourproject.http 文件中,然后在文件中进行 Endpoint 的测试。这种测试方式的好处比较多,包括可以持久化存储访问代码,以及可以对不同版本 Endpoint 的返回值进行对比等。关于这个功能的详情,可以参考 JetBrains HTTP Client 官方文档 ⧉

然后,在 OpenAPI 标签中,我们可以看到自动生成的 OpenAPI 文档。初次使用,可以点击 Export 按钮,生成对应的 yourproject.yaml 文件。之后,如果有新增的 Endpoint,则可以将相应的代码添加到 yourproject.yaml 文件中,以便更新自动生成的 OpenAPI 交互文档。

本地部署测试

本地测试

进行本地测试时,我们可以使用 RUN/DEBUG 功能来启动 Flask 应用。应用成功启动之后,我们就可以在 Service 面板中看到应用的运行状态。

内网穿透

通过内网穿透,我们可以将本地部署的服务暴露到公网,以便进行更加贴近生产环境的测试。

在这里,我们使用 Frp ⧉ 进行内网穿透,具体的配置说明详见 使用 FRP 进行内网穿透

serverAddr = "xxx.xxx.xxx.xxx"
serverPort = 7000

# 配置验证方法
auth.method = "token"
auth.token = "#tPxxNyxisfdfZ2"

[[proxies]]
name = "tapi"
type = "http"
# 配置本地IP为宿主机IP
localIP = "host.docker.internal"
localPort = 8080
subdomain = "tapi"

线上部署集成

完成了本地开发和测试后,我们需要将项目部署到线上环境。这里,我们使用 Docker 来进行部署。

Dockerfile

首先,我们需要编写 Dockerfile 文件,用来构建 Docker 镜像。

这里,我们使用 python:3.12-slim 作为基础镜像,因为它是一个轻量级的 Python 镜像, 并且包含了 Poerty 所需的基础以来,适合用来部署 Flask 应用。

Flask 镜像生成脚本
# 使用 Python 轻量级基础镜像作为构建镜像
FROM python:3.12-slim as builder

# 安装 Poetry
RUN pip install poetry

# 设置 Poetry 环境变量
ENV POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_IN_PROJECT=1 \
    POETRY_VIRTUALENVS_CREATE=1 \
    POETRY_CACHE_DIR=/tmp/poetry_cache

# 设置工作目录为 /app
WORKDIR /app

# 复制 Poetry 的配置文件到工作目录
COPY pyproject.toml poetry.lock ./

# Buildkit缓存挂载,使用 Poetry 安装依赖
RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-dev --no-root

# 使用 Python 轻量级基础镜像作为运行镜像
FROM python:3.12-slim as runtime

# 设置虚拟工作环境路径
ENV VIRTUAL_ENV=/app/.venv \
    PATH="/app/.venv/bin:$PATH"

# 复制 Poetry 的虚拟环境到工作目录
COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}

# 将当前开发目录下的文件复制到工作目录
COPY . /app

# 指定 Flask 应用环境变量
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0

# 暴露容器内部的端口
EXPOSE 5000

# 指定启动 Flask 应用的命令入口
ENTRYPOINT ["flask", "run"]

关于如何精简 Flask 应用镜像大小,可以参考 Blazing fast Python Docker builds with Poetry 🏃 ⧉,文中详细描述了如何逐步减少镜像大小以及提升构建速度。

注意,以上的 Dockerfile 配置需要在 ~/.zshrc 添加环境变量以启用 Docker 的 Buildkit 特性。

启用 Docker Buildkit
export DOCKER_BUILDKIT=1

docker-compose.yml

然后,我们需要编写 docker-compose.yml 文件,用来启动 Docker 容器。

创建 Flask 容器
1
2
3
4
5
6
7
8
9
version: '3.8'
services:
  flask-app:
    build: .
    ports:
      - "4000:5000"
    environment:
      FLASK_APP: app.py
      FLASK_RUN_HOST: 0.0.0.0

持续集成

最后,我们需要编写 CI/CD 脚本,用来实现持续集成。

这里,我们使用 Jenkins 来实现持续集成。

参考文档

技术栈选择

  1. 我的核心技术栈的基础是 Python 的 Flask,然后文档的写作会配合 Mkdocs 进行,以及在 Utools 中将开发好的应用作为插件的形式提供对外服务
  2. 后端的开发我会使用 APIFlask 完成,然后前端的页面框架使用 Flask 的 Tempelate 生成,引入 Bootstrap 提升样式的美观性,并加入 HTMx 和 Alpines.js 增强页面的交互性
  3. HTMx 和 Alpines.js 也可以在 Mkdocs 中进行使用,提升文档的交互性

技术栈说明

您的开发框架概述看起来是合理且完善的,针对您的技术栈和工具选择,这里有一些思考和潜在的优化建议:

  1. Flask 和 APIFlask

    • Flask 是一个灵活的微框架,非常适合快速开发和原型制作。APIFlask 作为 Flask 的扩展,为创建 RESTful API 提供了更简洁的方法,这是一个很好的选择。
  2. 前端开发 - Flask Template, Bootstrap, HTMX, Alpine.js

    • 使用 Flask Template 生成页面是一个简单有效的方法,特别是对于简单到中等复杂度的应用。
    • 引入 Bootstrap 会显著提高样式的美观性,并利用其丰富的组件库加快开发进程。
    • HTMX 和 Alpine.js 的结合使用可以在不依赖复杂JavaScript框架的情况下,为页面添加丰富的动态功能和交互性。
  3. MkDocs 配合 HTMX 和 Alpine.js

    • 在 MkDocs 中使用 HTMX 和 Alpine.js 来增加文档的交互性是一个创新的做法。尽管 MkDocs 主要用于静态内容的展示,但适当的动态交互可以提升用户体验。
  4. 优化建议

    • 代码一致性:在不同部分(如后端API、前端页面、文档)保持代码风格和设计模式的一致性,有助于提高可维护性。
    • 性能考虑:对于前端,特别是在引入多个JavaScript库时,注意页面加载速度和性能。测试不同页面和交互的加载时间,确保用户体验不受影响。
    • 文档与应用的一致性:尽管 MkDocs 用于文档,考虑在视觉设计上保持与应用的一致性,以构建品牌识别。
    • 测试和反馈:定期进行用户测试和反馈收集,以持续优化用户界面和交互体验。

总的来说,您选择的技术栈和工具看起来非常合理,且已经很好地覆盖了开发的各个方面。只需确保在整个开发过程中保持对性能、用户体验和代码可维护性的关注。

二级目录配置

反向代理

本项目作为微信公众号服务的后端,建议配置 Nginx 作为反向代理。

如果需要通过二级目录进行访问,则需要在项目代码中引入中间件,以及对 Nginx 的 location 进行相应的配置。

中间件配置

from werkzeug.middleware.dispatcher import DispatcherMiddleware
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {"/wechat": app.wsgi_app})

Nginx 配置

server{
    listen 443 ssl;
    server_name wechat.10k.xyz;
    ssl_certificate /etc/nginx/ssl/*.10k.xyz.crt;
    ssl_certificate_key /etc/nginx/ssl/*.10k.xyz.key;
    ssl_trusted_certificate /etc/nginx/ssl/*.10k.xyz.ca.crt;

    location /wechat/ {                             # 注意这里必须有斜杠
        proxy_pass http://127.0.0.1:8080;           # 注意这里必须没有斜杠
        proxy_set_header X-Script-Name /wechat;     # 注意这里要和上面的一致
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header Authorization;
    }
}