搭建 Flask Web 应用编程环境

文章来源声明: 原文作者:czsmith; 来源站点:掘金; 原文链接:https://juejin.cn/post/7689424360591867940; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

纯命令式清单,跳过原理只给步骤,适合已有基础的开发者快速复现环境。遇到包冲突、多解释器切换或数据库升级时可直接照做,新手建议对照官方文档补齐细节。

> 这是一份简要的安装 Python+VSCode 的操作教程。 主要是命令,不包含每一步为什么这么做。
  1. 安装步骤概览

  1. 准备 Python 环境
  2. 装依赖,跑一次自检
  3. 跑示例程序
  4. 接 VSCode

把数据库换成 MariaDB 是可选的第 5 步;修复和重装看第 7 步。

  1. 准备 Python 环境

方案 A:Anaconda(多环境)

conda create --name flask-web python=3.11 -y
conda activate flask-web

Python 版本推荐 3.11+。

方案 B:venv (单项目)

  • 从官网 下载 Python。
  • 安装后修改 PATH 变量。
  • 然后在项目文件夹执行以下命令:
python -m venv .venv
.venv\Scripts\activate

  1. 装依赖,跑自检

设置国内源,加速下载

pip config set global.index-url https://mirrors.aliyun.com/pypi/simple

安装常用包

cd web-basic
pip install -r requirements.txt

requirements.txt:

Flask
Flask-SQLAlchemy
Flask-Migrate
Flask-Login
python-dotenv
Flask-WTF
email-validator
#waitress

  1. 跑示例程序

首次运行前建表:

flask --app app db init
flask --app app db migrate
flask --app app db upgrade

启动示例程序(见附录):

python app.py

用浏览器打开 http://127.0.0.1:5000/,看到 "users": 0 才算成功 —— 只看 status 不行,表没建时它也是 ok。

顺手再看一个受保护接口:http://127.0.0.1:5000/profile 返回 {"error": "未登录"},说明拦截生效。

按 Ctrl+C 停止。

  1. 接 VSCode

  1. 官网下载 VSCode code.visualstudio.com/ 并安装

  2. 装扩展 Python(Microsoft)

  3. Ctrl+Shift+P → Python: Select Interpreter → 选 flask-web 环境(或项目下的 .venv)

    • 如不确定 conda 装在哪: ``` conda info --base

  4. 如 PowerShell 报“在此系统上禁止运行脚本”时,用管理员身份执行一次: ``` Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

    
    
  5. 装 MariaDB(免费 MySQL)数据库(可选)


如需将默认的 SQLite 升级为 MySQL 数据库:

  1. 下载安装:官方地址。安装时设好 root 密码,如 abc123。

  2. 装驱动``` pip install pymysql

  3. 建库:用 MariaDB 安装包自带的 HeidiSQL 建,不用敲 SQL(安装时默认勾选,开始菜单里能找到)。

    1. 打开 HeidiSQL,点左下角 新建 建会话:用户 root,密码填安装时设的那个,然后点右下角 打开 按钮。
    2. 左侧连接树里右键单击刚建好的会话 → 创建新的 → 数据库。
    3. 名称填 flaskweb,字符校对选 utf8mb4_unicode_520_ci,点 确定。
  4. 改 .env: ``` DATABASE_URL=mysql+pymysql://root:你的密码@127.0.0.1:3306/flaskweb

  5. 重新建表:flask --app app db upgrade 再跑一次即可。migrations/ 还在,不用再 init。

  6. 样例代码


(略)

  1. 修复和重建环境

遇到包冲突,如果可以确定某个包的问题,比如 flask-socketio,可以试着重装这个包:

pip install --force-reinstall flask-socketio

实在修不了,重建

conda deactivate
conda env remove --name flask-web
conda create --name flask-web python=3.11 -y
conda activate flask-web
pip install -r requirements.txt

附:示例程序

<span>import</span> os

<span>from</span> dotenv <span>import</span> load_dotenv
<span>from</span> flask <span>import</span> Flask, jsonify, request
<span>from</span> flask_login <span>import</span> (LoginManager, UserMixin, current_user, 
    login_required, login_user, logout_user)
<span>from</span> flask_migrate <span>import</span> Migrate
<span>from</span> flask_sqlalchemy <span>import</span> SQLAlchemy
<span>from</span> sqlalchemy.exc <span>import</span> SQLAlchemyError
<span>from</span> werkzeug.security <span>import</span> check_password_hash, generate_password_hash

load_dotenv()

db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()


<span>class</span> <span>User</span>(UserMixin, db.Model):
    __tablename__ = <span>'users'</span>

    <span>id</span> = db.Column(db.Integer, primary_key=<span>True</span>)
    username = db.Column(db.String(<span>64</span>), unique=<span>True</span>, nullable=<span>False</span>, index=<span>True</span>)
    password_hash = db.Column(db.String(<span>255</span>), nullable=<span>False</span>)

    <span>def</span> <span>set_password</span>(<span>self, raw</span>):
        <span>''' 保存密码哈希 '''</span>
        self.password_hash = generate_password_hash(raw)

    <span>def</span> <span>check_password</span>(<span>self, raw</span>):
        <span>return</span> check_password_hash(self.password_hash, raw)


<span>def</span> <span>create_app</span>():
    app = Flask(__name__)
    app.config[<span>'SECRET_KEY'</span>] = os.getenv(<span>'SECRET_KEY'</span>, <span>'dev-secret-change-me'</span>)
    app.config[<span>'SQLALCHEMY_DATABASE_URI'</span>] = os.getenv(<span>'DATABASE_URL'</span>, <span>'sqlite:///app.db'</span>)

    db.init_app(app)
    migrate.init_app(app, db)
    login_manager.init_app(app)

<span>    @login_manager.user_loader</span>
    <span>def</span> <span>load_user</span>(<span>user_id</span>):
        <span>return</span> db.session.get(User, <span>int</span>(user_id))

<span>    @login_manager.unauthorized_handler</span>
    <span>def</span> <span>unauthorized</span>():
        <span>return</span> jsonify({<span>'error'</span>: <span>'未登录'</span>}), <span>401</span>

<span>    @app.get(<span><span>'/'</span></span>)</span>
    <span>def</span> <span>index</span>():
        state = {
            <span>'status'</span>: <span>'ok'</span>,
            <span>'user'</span>: current_user.username <span>if</span> current_user.is_authenticated <span>else</span> <span>None</span>,
        }
        <span>try</span>:
            state[<span>'users'</span>] = db.session.query(User).count()
        <span>except</span> SQLAlchemyError:
            db.session.rollback()
            state[<span>'users'</span>] = <span>None</span>
            state[<span>'hint'</span>] = <span>'数据表还没建,先执行 flask --app app db upgrade'</span>
        <span>return</span> jsonify(state)

<span>    @app.post(<span><span>'/register'</span></span>)</span>
    <span>def</span> <span>register</span>():
        data = request.get_json(silent=<span>True</span>) <span>or</span> {}
        username = <span>str</span>(data.get(<span>'username'</span>) <span>or</span> <span>''</span>).strip()
        password = <span>str</span>(data.get(<span>'password'</span>) <span>or</span> <span>''</span>)
        <span>if</span> <span>not</span> username <span>or</span> <span>not</span> password:
            <span>return</span> jsonify({<span>'error'</span>: <span>'需要 username 和 password 两个字段'</span>}), <span>400</span>
        <span>if</span> db.session.query(User).filter_by(username=username).first():
            <span>return</span> jsonify({<span>'error'</span>: <span>'用户名已存在'</span>}), <span>409</span>

        user = User(username=username)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        <span>return</span> jsonify({<span>'id'</span>: user.<span>id</span>, <span>'username'</span>: user.username}), <span>201</span>

<span>    @app.post(<span><span>'/login'</span></span>)</span>
    <span>def</span> <span>login</span>():
        data = request.get_json(silent=<span>True</span>) <span>or</span> {}
        username = <span>str</span>(data.get(<span>'username'</span>) <span>or</span> <span>''</span>).strip()
        password = <span>str</span>(data.get(<span>'password'</span>) <span>or</span> <span>''</span>)
        user = db.session.query(User).filter_by(username=username).first()
        <span>if</span> user <span>is</span> <span>None</span> <span>or</span> <span>not</span> user.check_password(password):
            <span>return</span> jsonify({<span>'error'</span>: <span>'用户名或密码不正确'</span>}), <span>401</span>
        login_user(user)
        <span>return</span> jsonify({<span>'id'</span>: user.<span>id</span>, <span>'username'</span>: user.username})

<span>    @app.post(<span><span>'/logout'</span></span>)</span>
<span>    @login_required</span>
    <span>def</span> <span>logout</span>():
        logout_user()
        <span>return</span> jsonify({<span>'status'</span>: <span>'已登出'</span>})

<span>    @app.get(<span><span>'/profile'</span></span>)</span>
<span>    @login_required</span>
    <span>def</span> <span>profile</span>():
        <span>return</span> jsonify({<span>'id'</span>: current_user.<span>id</span>, <span>'username'</span>: current_user.username})

    <span>return</span> app


app = create_app()


<span>if</span> __name__ == <span>'__main__'</span>:
    <span>print</span>(<span>'数据库  : {}'</span>.<span>format</span>(app.config[<span>'SQLALCHEMY_DATABASE_URI'</span>]))
    app.run(port=<span>5000</span>, debug=<span>True</span>)
    <span># 生产环境服务器</span>
    <span>#from waitress import serve</span>
    <span>#serve(app, host='0.0.0.0', port=8080, threads=4)</span>