尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Flask框架入门:从零构建Python Web应用的核心技术与实践

Flask框架入门:从零构建Python Web应用的核心技术与实践 1. 为什么是Flask从“轻量级”说起如果你刚开始接触Python Web开发或者是从Django那种“大而全”的框架转过来听到“Flask是一个轻量级Web框架”这句话可能第一反应是它功能少、简单、适合小项目。这个理解对但也不全对。Flask的“轻”核心在于它的设计哲学微内核与可扩展性。它不像Django那样默认就给你打包了ORM、Admin后台、用户认证、表单处理等一整套“全家桶”。Flask本身只提供了最核心的路由、请求/响应处理和模板渲染其他所有功能比如连接数据库、处理表单、用户登录都需要通过安装第三方扩展Extension来实现。这带来了一个巨大的优势自由度和可控性。你的项目不会因为引入一个庞大的框架而变得臃肿。你可以像搭积木一样只选择你需要的功能模块。比如你只想快速做一个接收API请求并返回JSON数据的服务那你可能连模板引擎都不需要安装。这种“按需取用”的特性让Flask在构建微服务、API后端、小型工具型Web应用以及作为大型应用的某个独立服务模块时显得游刃有余。这也是为什么很多初创公司、数据科学项目需要快速搭建一个展示结果的Web界面以及IoT设备的管理后台都喜欢用Flask。它让你能够快速启动并且随着项目复杂度的增长通过引入合适的扩展也能优雅地应对而不是在一开始就被迫接受一套可能用不上的复杂约定。从技术栈选择来看当你需要快速验证一个想法或者项目的核心复杂度不在Web层本身比如核心是算法、数据处理而Web只是一个交互界面时Flask几乎是首选。它的学习曲线相对平缓你不需要理解一大堆概念如Django的MTV模式、中间件、信号等就能做出东西来这种即时反馈对初学者尤其友好。接下来我们就从零开始搭建你的第一个Flask应用。2. 环境搭建与“Hello, World!”的诞生万事开头难但Flask让这个“难”变得非常简单。我们首先需要一个干净的Python环境。我强烈建议使用虚拟环境Virtual Environment这能避免不同项目间的依赖包版本冲突是Python开发的一个最佳实践。2.1 创建并激活虚拟环境打开你的终端Windows用CMD或PowerShellmacOS/Linux用Terminal进入你打算存放项目的目录。# 1. 创建项目文件夹并进入 mkdir my_first_flask_app cd my_first_flask_app # 2. 创建虚拟环境 # 对于Python 3.3可以使用内置的venv模块 python -m venv venv # 3. 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活后你的命令行提示符前通常会显示(venv)表示你已经在这个独立的Python环境中了。2.2 安装Flask在激活的虚拟环境中使用pip安装Flask。pip install flask这里有个小技巧为了确保项目可复现我们通常会把依赖包列表固定下来。安装完成后可以运行pip freeze requirements.txt这会将当前环境的所有包及其版本号写入requirements.txt文件。以后在新的环境部署时只需运行pip install -r requirements.txt即可一键安装所有依赖。2.3 编写第一个应用app.py在项目根目录下创建一个名为app.py的文件。这个文件名不是强制的但app.py或wsgi.py是常见的约定。# 导入Flask类 from flask import Flask # 创建Flask应用实例。__name__参数用于确定应用的根目录以便于查找静态文件、模板等资源。 app Flask(__name__) # 使用装饰器定义路由。当用户访问网站根路径/时将触发下面的函数。 app.route(/) def hello_world(): # 这个函数的返回值将作为HTTP响应体发送给浏览器。 return Hello, World! # 程序入口。如果直接运行此脚本而非作为模块导入则启动开发服务器。 if __name__ __main__: # debugTrue 开启调试模式代码修改后服务器会自动重启并在浏览器显示详细错误信息。 # 注意生产环境绝不允许开启debug模式 app.run(debugTrue)2.4 运行并访问在终端中确保你在虚拟环境下并且位于app.py所在的目录然后运行python app.py你会看到类似这样的输出* Serving Flask app app * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRLC to quit)现在打开你的浏览器访问http://127.0.0.1:5000。恭喜你应该能看到页面上显示着“Hello, World!”。注意默认的127.0.0.1:5000只能在本机访问。如果你想让同一局域网内的其他设备也能访问可以将app.run()改为app.run(host0.0.0.0, debugTrue)。但请务必注意调试模式debugTrue下对外网开放存在安全风险仅限内网测试使用。这个简单的过程揭示了Flask的核心工作流定义路由 - 编写视图函数 - 返回响应。你已经成功搭建了一个最小的Web应用。接下来我们要让这个应用变得有用比如展示动态内容和接收用户输入。3. 核心三要素路由、模板与表单处理一个基本的Web应用离不开这三样东西告诉浏览器哪个地址对应什么功能路由向用户展示漂亮的页面模板以及获取用户提交的数据表单。Flask对这三者的支持既简洁又强大。3.1 动态路由与变量规则我们的“Hello, World!”是静态的。如何向特定的人问好呢这就需要动态路由。app.route(/user/username) def show_user_profile(username): # 尖括号中的部分是动态片段会作为参数传递给视图函数。 return fUser: {username} app.route(/post/int:post_id) def show_post(post_id): # 可以指定转换器类型如int:确保参数是整数。 return fPost ID: {post_id}, Type: {type(post_id)}访问/user/zhangsan页面会显示“User: zhangsan”。访问/post/123post_id在函数内已经是整数类型。Flask内置了string默认、int、float、path、uuid等转换器。这种设计使得构建RESTful风格的API变得非常直观。3.2 使用Jinja2模板渲染HTML直接在视图函数里拼接HTML字符串既麻烦又容易出错也不利于前后端分离。Flask使用Jinja2作为模板引擎。首先在项目根目录下创建一个名为templates的文件夹Flask默认会在这里寻找模板。创建templates/index.html!DOCTYPE html html head title{{ title }} - My Flask App/title /head body h1Hello, {{ user.username }}!/h1 pYour email is: {{ user.email }}/p ul {% for item in nav %} lia href{{ item.url }}{{ item.caption }}/a/li {% endfor %} /ul {% if user.is_admin %} pstrongAdmin Panel/strong/p {% endif %} /body /htmlJinja2的语法非常直观{{ ... }}用于输出变量或表达式{% ... %}用于执行控制语句如循环和条件判断。然后修改app.py中的视图函数来渲染这个模板from flask import render_template app.route(/hello/name) def hello(name): # 模拟用户数据 user {username: name, email: f{name}example.com, is_admin: False} nav [{url: /, caption: Home}, {url: /about, caption: About}] # render_template函数第一个参数是模板文件名后续以关键字参数形式传入模板中使用的变量。 return render_template(index.html, titleHome, useruser, navnav)访问/hello/Alice你将看到一个根据传入数据动态生成的HTML页面。模板继承是Jinja2另一个强大功能可以创建一个基础模板base.html定义整体布局然后让其他模板继承它只覆盖特定的块block这能极大减少重复代码。3.3 处理Web表单接收用户输入没有交互的网页是单调的。让我们创建一个简单的登录表单。Flask本身不直接处理表单但有一个非常流行的扩展Flask-WTF它集成了WTForms能方便地处理表单验证、CSRF防护等。首先安装扩展pip install flask-wtf创建表单类。通常我会在一个单独的forms.py文件中定义# forms.py from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): # 定义表单字段。DataRequired验证器确保字段不为空。 username StringField(Username, validators[DataRequired(), Length(min4, max25)]) password PasswordField(Password, validators[DataRequired()]) submit SubmitField(Sign In)然后创建对应的模板templates/login.html{% extends base.html %} {% block content %} h2Login/h2 form methodPOST action {{ form.hidden_tag() }} !-- 渲染CSRF令牌隐藏字段非常重要 -- p {{ form.username.label }}br {{ form.username(size32) }}br {% for error in form.username.errors %} span stylecolor: red;[{{ error }}]/span {% endfor %} /p p {{ form.password.label }}br {{ form.password(size32) }}br {% for error in form.password.errors %} span stylecolor: red;[{{ error }}]/span {% endfor %} /p p{{ form.submit() }}/p /form {% endblock %}最后在app.py中编写处理登录的路由from flask import render_template, flash, redirect, url_for from forms import LoginForm app.route(/login, methods[GET, POST]) # 允许GET访问页面和POST提交表单方法 def login(): form LoginForm() if form.validate_on_submit(): # 如果是POST请求且表单验证通过 # 这里应该查询数据库验证用户名和密码 flash(fLogin requested for user {form.username.data}) return redirect(url_for(hello, nameform.username.data)) # 重定向到欢迎页面 # 如果是GET请求或表单验证失败渲染登录页面 return render_template(login.html, titleSign In, formform)这个例子涵盖了表单的展示、验证、错误信息反馈以及成功后的跳转。flash()函数用于在页面间传递一次性提示消息需要在模板中通过get_flashed_messages()获取并显示。url_for()函数用于动态生成URL避免硬编码是Flask中推荐的做法。4. 连接数据库让数据持久化一个没有数据存储的应用就像没有记忆的人。Flask与数据库的交互非常灵活你可以使用原生的SQL通过sqlite3或pymysql等驱动也可以使用ORM对象关系映射工具。对于大多数应用ORM能显著提升开发效率和代码可维护性。Flask-SQLAlchemy是SQLAlchemy的Flask封装是事实上的标准选择。4.1 配置与初始化Flask-SQLAlchemy首先安装pip install flask-sqlalchemy在app.py中进行配置和初始化from flask_sqlalchemy import SQLAlchemy import os app Flask(__name__) # 配置数据库URI。这里使用SQLite数据库文件将位于项目根目录的instance文件夹中。 basedir os.path.abspath(os.path.dirname(__file__)) app.config[SQLALCHEMY_DATABASE_URI] sqlite:/// os.path.join(basedir, instance, app.db) app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False # 关闭对模型修改的跟踪以节省资源 db SQLAlchemy(app) # 初始化扩展将应用实例传入SQLALCHEMY_DATABASE_URI的格式取决于你使用的数据库。例如MySQL可能是mysqlpymysql://username:passwordlocalhost/db_name。4.2 定义数据模型模型Model是Python类对应数据库中的表。我们在app.py或单独的models.py中定义# 示例用户模型和文章模型 class User(db.Model): id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(80), uniqueTrue, nullableFalse) email db.Column(db.String(120), uniqueTrue, nullableFalse) # backref在Post模型中创建一个名为author的属性用于反向引用。 # lazydynamic对于一对多关系返回一个查询对象而不是立即加载所有数据便于后续过滤。 posts db.relationship(Post, backrefauthor, lazydynamic) def __repr__(self): return fUser {self.username} class Post(db.Model): id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(120), nullableFalse) body db.Column(db.Text, nullableFalse) user_id db.Column(db.Integer, db.ForeignKey(user.id), nullableFalse) # 外键 def __repr__(self): return fPost {self.title}4.3 创建数据库与基本操作在Python交互式环境或创建一个初始化脚本如create_db.py中执行from app import app, db with app.app_context(): # 应用上下文是必须的 db.create_all() # 根据模型创建所有表现在你可以在视图函数中进行增删改查操作了app.route(/add_user) def add_user(): # 创建新用户对象 new_user User(usernametest, emailtestexample.com) # 添加到会话 db.session.add(new_user) # 提交到数据库 db.session.commit() return User added! app.route(/users) def show_users(): users User.query.all() # 查询所有用户 # 或者进行过滤查询User.query.filter_by(usernametest).first() return render_template(users.html, usersusers)实操心得db.session是一个非常重要的概念。所有的修改增、删、改都需要先add()到会话中最后通过commit()一次性提交。如果发生错误可以调用db.session.rollback()回滚。对于查询操作query对象提供了非常丰富的API如filter(),order_by(),limit(),paginate()等几乎可以满足所有复杂查询需求。务必花时间熟悉SQLAlchemy的查询语法这是高效使用它的关键。5. 项目结构优化从单文件到可维护的包当你的应用超过几百行代码时把所有东西都塞进一个app.py文件会变得难以维护。标准的Flask项目结构采用应用工厂模式Application Factory将应用拆分成模块。一个典型的项目结构如下my_flask_app/ ├── app/ │ ├── __init__.py # 应用工厂函数所在创建Flask app实例 │ ├── models.py # 数据库模型定义 │ ├── forms.py # 表单类定义 │ ├── routes/ │ │ ├── __init__.py │ │ ├── main.py # 主页、关于等通用路由 │ │ ├── auth.py # 认证相关路由登录、注册、登出 │ │ └── blog.py # 博客文章相关路由 │ ├── templates/ # 模板文件夹 │ │ ├── base.html │ │ ├── index.html │ │ └── auth/ │ │ └── login.html │ ├── static/ # 静态文件CSS, JS, images │ └── config.py # 配置文件不同环境开发、测试、生产 ├── migrations/ # 数据库迁移文件夹由Flask-Migrate生成 ├── tests/ # 单元测试 ├── venv/ # 虚拟环境不应提交到版本控制 ├── requirements.txt # 项目依赖 └── run.py # 启动脚本可选用于生产环境让我们看看核心部分如何实现。app/__init__.pyfrom flask import Flask from flask_sqlalchemy import SQLAlchemy from config import Config db SQLAlchemy() # 先创建扩展对象但不绑定应用 def create_app(config_classConfig): app Flask(__name__) app.config.from_object(config_class) db.init_app(app) # 将扩展与当前应用实例绑定 # 注册蓝图Blueprint from app.routes import main, auth, blog app.register_blueprint(main.bp) app.register_blueprint(auth.bp, url_prefix/auth) app.register_blueprint(blog.bp, url_prefix/blog) return app蓝图Blueprint是Flask中组织路由的模块化方式。例如app/routes/auth.pyfrom flask import Blueprint, render_template, redirect, url_for, flash from app.forms import LoginForm # 创建一个名为‘auth’的蓝图其下的路由都会自动加上/auth前缀在工厂函数中指定。 bp Blueprint(auth, __name__) bp.route(/login, methods[GET, POST]) def login(): form LoginForm() # ... 登录逻辑 ... return render_template(auth/login.html, formform)最后顶层的run.py非常简单from app import create_app app create_app() if __name__ __main__: app.run(debugTrue)这种结构的好处是显而易见的关注点分离。模型、视图、表单、配置各司其职。应用工厂模式使得创建不同配置的应用实例如用于测试变得非常容易。蓝图让功能模块化大型项目可以清晰地划分为用户、文章、管理等多个蓝图每个蓝图拥有自己的路由、模板和静态文件。6. 用户认证与会话管理构建安全边界几乎所有的Web应用都需要识别用户身份。实现一个完整的用户认证系统涉及注册、登录、登出、密码哈希存储、会话管理等多个环节。我们可以借助Flask-Login这个扩展来简化大部分工作。6.1 安装与配置Flask-Loginpip install flask-login在应用工厂函数中初始化from flask_login import LoginManager login_manager LoginManager() login_manager.login_view auth.login # 指定未登录用户访问受保护页面时重定向的登录页面 login_manager.login_message Please log in to access this page. def create_app(config_classConfig): app Flask(__name__) # ... 其他配置 ... login_manager.init_app(app) # ...6.2 修改用户模型Flask-Login要求用户模型实现几个特定的方法from app import db, login_manager from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash class User(UserMixin, db.Model): # 继承UserMixin它提供了默认的必需方法实现 id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(64), uniqueTrue, indexTrue) email db.Column(db.String(120), uniqueTrue, indexTrue) password_hash db.Column(db.String(128)) # 存储密码的哈希值而非明文 def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) # 这个回调函数用于根据存储在会话中的用户ID重新加载用户对象 login_manager.user_loader def load_user(id): return User.query.get(int(id))重要安全提示永远不要在数据库中存储用户的明文密码。使用werkzeug.security的generate_password_hash()和check_password_hash()是行业标准做法。它们使用加盐的哈希算法如pbkdf2:sha256即使数据库泄露攻击者也无法轻易还原出原始密码。6.3 实现登录与登出视图在认证蓝图如auth.py中from flask import render_template, redirect, url_for, flash, request from flask_login import login_user, logout_user, current_user, login_required from app import db from app.models import User from app.forms import LoginForm, RegistrationForm bp.route(/login, methods[GET, POST]) def login(): if current_user.is_authenticated: # 如果用户已登录直接跳转到首页 return redirect(url_for(main.index)) form LoginForm() if form.validate_on_submit(): user User.query.filter_by(usernameform.username.data).first() if user is None or not user.check_password(form.password.data): flash(Invalid username or password) return redirect(url_for(auth.login)) login_user(user, rememberform.remember_me.data) # 登录用户可选的“记住我” next_page request.args.get(next) # 防止开放重定向漏洞检查next参数是否是一个相对路径 if not next_page or url_parse(next_page).netloc ! : next_page url_for(main.index) return redirect(next_page) return render_template(auth/login.html, titleSign In, formform) bp.route(/logout) def logout(): logout_user() return redirect(url_for(main.index)) bp.route(/register, methods[GET, POST]) def register(): if current_user.is_authenticated: return redirect(url_for(main.index)) form RegistrationForm() if form.validate_on_submit(): user User(usernameform.username.data, emailform.email.data) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash(Congratulations, you are now a registered user!) return redirect(url_for(auth.login)) return render_template(auth/register.html, titleRegister, formform)6.4 保护视图与获取当前用户使用login_required装饰器可以保护需要登录才能访问的视图from flask_login import login_required bp.route(/profile) login_required # 必须登录才能访问 def profile(): return render_template(profile.html, usercurrent_user) # current_user是全局可用的代理对象在模板中也可以直接使用current_user来判断用户状态{% if current_user.is_authenticated %} a href{{ url_for(auth.logout) }}Logout/a {% else %} a href{{ url_for(auth.login) }}Login/a {% endif %}通过这一套组合拳我们就为应用建立了一个基本但稳固的用户认证系统。Flask-Login帮我们处理了会话管理、记住我、用户加载等繁琐细节让我们可以专注于业务逻辑。7. 部署上线从开发服务器到生产环境在本地debugTrue模式下跑得欢的应用直接扔到公网上是极其危险的。生产环境部署需要考虑性能、安全性和稳定性。这里我们介绍两种主流且相对简单的部署方式使用Gunicorn Nginx以及使用容器化部署Docker。7.1 部署准备关闭调试与设置密钥首先确保生产配置如config.py中的ProductionConfig关闭了调试模式并设置了一个强壮的SECRET_KEY。SECRET_KEY用于加密会话cookie、CSRF令牌等必须保密且随机生成。# config.py import os class Config: SECRET_KEY os.environ.get(SECRET_KEY) or a-hard-to-guess-string # 从环境变量读取或使用一个默认值仅用于开发 # ... class ProductionConfig(Config): DEBUG False # 生产环境的数据库URI也应从环境变量读取 SQLALCHEMY_DATABASE_URI os.environ.get(DATABASE_URL) or sqlite:///../prod.db7.2 方案一Gunicorn Nginx (Linux服务器)Gunicorn是一个Python WSGI HTTP服务器性能比Flask自带的开发服务器强得多。Nginx作为反向代理和静态文件服务器。步骤1安装Gunicorn在服务器上进入项目目录虚拟环境中pip install gunicorn步骤2使用Gunicorn启动应用假设你的应用工厂函数在app/__init__.py中名为create_app。gunicorn -w 4 -b 127.0.0.1:8000 app:create_app()-w 4: 启动4个工作进程根据CPU核心数调整。-b 127.0.0.1:8000: 绑定到本机的8000端口。app:create_app(): 告诉Gunicorn从app模块即app/__init__.py导入create_app函数并调用它来获取Flask应用实例。步骤3配置Nginx作为反向代理安装Nginx后编辑其站点配置文件如/etc/nginx/sites-available/myflaskappserver { listen 80; server_name your_domain.com www.your_domain.com; # 你的域名 location / { proxy_pass http://127.0.0.1:8000; # 转发给Gunicorn 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; } location /static { alias /path/to/your/my_flask_app/app/static; # 直接由Nginx处理静态文件效率更高 expires 30d; } }然后启用该配置并重启Nginx。步骤4使用系统服务管理Gunicorn以systemd为例创建服务文件/etc/systemd/system/myflaskapp.service[Unit] DescriptionGunicorn instance to serve my flask app Afternetwork.target [Service] Userwww-data # 运行用户 Groupwww-data WorkingDirectory/path/to/your/my_flask_app EnvironmentPATH/path/to/your/my_flask_app/venv/bin ExecStart/path/to/your/my_flask_app/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 app:create_app() [Install] WantedBymulti-user.target然后启动并启用服务sudo systemctl start myflaskapp sudo systemctl enable myflaskapp7.3 方案二Docker容器化部署Docker可以将应用及其所有依赖打包成一个标准化的镜像实现“一次构建处处运行”。步骤1编写Dockerfile在项目根目录创建Dockerfile# 使用官方Python运行时作为父镜像 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 将当前目录内容复制到容器的/app中 COPY . /app # 安装依赖 RUN pip install --no-cache-dir -r requirements.txt # 设置环境变量 ENV FLASK_APPapp ENV FLASK_ENVproduction # 暴露端口 EXPOSE 5000 # 运行Gunicorn CMD [gunicorn, -w, 4, -b, 0.0.0.0:5000, app:create_app()]步骤2构建并运行Docker镜像# 构建镜像 docker build -t my-flask-app . # 运行容器 docker run -d -p 80:5000 --name flask-app my-flask-app现在你就可以通过服务器的IP地址或域名访问你的应用了。使用Docker Compose可以更方便地管理多容器应用如搭配PostgreSQL数据库、Redis缓存等。部署避坑指南静态文件确保Nginx正确配置了静态文件路径或者Flask应用在生产模式下正确设置了STATIC_FOLDER。数据库连接生产环境务必使用更健壮的数据库如PostgreSQL、MySQL避免使用SQLite。数据库连接字符串、密钥等敏感信息务必通过环境变量传入不要硬编码在代码中。日志配置好Gunicorn和应用的日志记录方便排查问题。Gunicorn可以通过--access-logfile和--error-logfile参数指定日志文件。进程管理除了systemd也可以使用Supervisor来管理Gunicorn进程它提供了更丰富的进程监控和重启功能。HTTPS使用Let‘s Encrypt为你的域名申请免费SSL证书并在Nginx中配置HTTPS这是现代网站的标配。从一行“Hello, World!”代码到一个结构清晰、具备用户认证、可部署上线的完整Web应用我们走完了Flask入门的核心路径。Flask的魅力在于它用最少的约定给你最大的灵活性让你能够根据项目的实际需要来塑造它。记住官方文档是你最好的朋友而社区里海量的扩展Flask-Extensions能帮你解决几乎所有常见问题。
返回列表