Flask是Python中最流行的Web框架之一,以其轻量级、灵活性和易于学习而闻名。本文将带您从零开始学习如何使用Flask构建Web应用,并最终将其部署到生产环境。
Flask简介
Flask是一个微型Web框架,由Armin Ronacher创建,基于Werkzeug WSGI工具包和Jinja2模板引擎。与Django等全功能框架不同,Flask只提供核心功能,让开发者可以根据需要添加扩展。这种设计理念使Flask非常灵活,适合各种规模的项目。
Flask的主要特点
- 轻量级:核心简单但可扩展
- 灵活性:不强制特定的项目结构或依赖
- 易于学习:API简洁明了
- 丰富的扩展生态系统:可以添加各种功能,如表单验证、数据库集成、用户认证等
- 内置开发服务器和调试器:便于开发和测试
安装Flask
开始使用Flask前,我们需要先安装它。推荐使用虚拟环境来隔离项目依赖:
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate
# 安装Flask
pip install flask
创建第一个Flask应用
让我们创建一个简单的"Hello, World!"应用来了解Flask的基本结构:
from flask import Flask
# 创建Flask应用实例
app = Flask(__name__)
# 定义路由和视图函数
@app.route('/')
def hello_world():
return 'Hello, World!'
# 运行应用
if __name__ == '__main__':
app.run(debug=True)
将上面的代码保存为app.py
,然后在命令行中运行:
python app.py
现在,打开浏览器访问http://127.0.0.1:5000/
,你应该能看到"Hello, World!"消息。
Flask路由和视图
Flask使用装饰器来定义路由,将URL映射到视图函数:
@app.route('/about')
def about():
return 'About Page'
@app.route('/user/<username>')
def show_user_profile(username):
return f'User: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post: {post_id}'
路由可以包含变量部分,用<variable_name>
表示。Flask支持以下转换器:
string
:默认,接受任何不包含斜杠的文本int
:接受整数float
:接受浮点数path
:接受包含斜杠的文本uuid
:接受UUID字符串
HTTP方法
默认情况下,路由只响应GET请求。要处理其他HTTP方法,可以使用methods
参数:
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 处理表单提交
username = request.form.get('username')
password = request.form.get('password')
# 验证用户...
return f'登录成功,欢迎 {username}!'
else:
# 显示登录表单
return '''
<form method="post">
<p><input type="text" name="username" placeholder="用户名"></p>
<p><input type="password" name="password" placeholder="密码"></p>
<p><input type="submit" value="登录"></p>
</form>
'''
模板渲染
Flask使用Jinja2作为模板引擎,可以将Python变量传递给HTML模板:
from flask import render_template
@app.route('/hello/<name>')
def hello(name):
return render_template('hello.html', name=name)
创建一个templates
文件夹,并在其中创建hello.html
:
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
Jinja2模板语法
Jinja2提供了强大的模板功能:
{{ variable }}
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
{% extends "base.html" %}
{% block title %}Page Title{% endblock %}
{% block content %}
<h1>Hello, World!</h1>
{% endblock %}
静态文件
Flask自动为static
文件夹中的文件提供服务:
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='logo.png') }}">
重定向和错误处理
from flask import redirect, url_for, abort
@app.route('/redirect-example')
def redirect_example():
# 重定向到另一个路由
return redirect(url_for('hello_world'))
@app.route('/error-example')
def error_example():
# 返回404错误
abort(404)
# 自定义错误页面
@app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'), 404
Flask应用结构
随着应用规模的增长,良好的项目结构变得至关重要。以下是一个典型的Flask项目结构:
my_flask_app/
├── app/
│ ├── __init__.py # 应用工厂
│ ├── models/ # 数据库模型
│ ├── routes/ # 路由和视图
│ ├── static/ # 静态文件
│ └── templates/ # HTML模板
├── config.py # 配置文件
├── requirements.txt # 依赖列表
└── run.py # 启动脚本
使用应用工厂
应用工厂模式允许创建多个应用实例,便于测试和不同配置的部署:
# app/__init__.py
from flask import Flask
def create_app(config_name='default'):
app = Flask(__name__)
# 加载配置
if config_name == 'development':
app.config.from_object('config.DevelopmentConfig')
elif config_name == 'production':
app.config.from_object('config.ProductionConfig')
else:
app.config.from_object('config.DefaultConfig')
# 注册蓝图
from app.routes.main import main_bp
app.register_blueprint(main_bp)
return app
使用蓝图
蓝图(Blueprint)是组织相关路由的方式,可以将应用分割成更小的、可重用的组件:
# app/routes/main.py
from flask import Blueprint, render_template
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
return render_template('index.html')
@main_bp.route('/about')
def about():
return render_template('about.html')
数据库集成
Flask本身不提供数据库支持,但可以通过扩展如Flask-SQLAlchemy集成数据库:
pip install flask-sqlalchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return f'<User {self.username}>'
# 创建数据库表
with app.app_context():
db.create_all()
# 添加用户
@app.route('/add_user/<username>/<email>')
def add_user(username, email):
user = User(username=username, email=email)
db.session.add(user)
db.session.commit()
return f'User {username} added!'
# 查询用户
@app.route('/users')
def get_users():
users = User.query.all()
result = '<h1>Users</h1><ul>'
for user in users:
result += f'<li>{user.username} ({user.email})</li>'
result += '</ul>'
return result
表单处理
Flask-WTF是处理表单的流行扩展:
pip install flask-wtf
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('Login')
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 处理表单提交
email = form.email.data
password = form.password.data
# 验证用户...
return f'Login successful for {email}'
return render_template('login.html', form=form)
在模板中使用表单:
<form method="post">
{{ form.hidden_tag() }}
<div>
{{ form.email.label }}
{{ form.email }}
{% if form.email.errors %}
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
{% endif %}
</div>
<div>
{{ form.password.label }}
{{ form.password }}
{% if form.password.errors %}
{% for error in form.password.errors %}
<span>{{ error }}</span>
{% endfor %}
{% endif %}
</div>
<div>
{{ form.submit }}
</div>
</form>
用户认证
Flask-Login是处理用户会话管理的扩展:
pip install flask-login
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
login_manager = LoginManager(app)
login_manager.login_view = 'login'
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
def __repr__(self):
return f'<User {self.username}>'
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'POST':
email = request.form.get('email')
password = request.form.get('password')
user = User.query.filter_by(email=email).first()
if user and user.password == password: # 实际应用中应使用密码哈希
login_user(user)
return redirect(url_for('index'))
return 'Invalid credentials'
return render_template('login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))
@app.route('/profile')
@login_required
def profile():
return f'Hello, {current_user.username}!'
RESTful API
Flask可以轻松创建RESTful API:
from flask import jsonify, request
# 获取所有用户
@app.route('/api/users', methods=['GET'])
def get_users_api():
users = User.query.all()
result = []
for user in users:
result.append({
'id': user.id,
'username': user.username,
'email': user.email
})
return jsonify(result)
# 获取单个用户
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user_api(user_id):
user = User.query.get_or_404(user_id)
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email
})
# 创建用户
@app.route('/api/users', methods=['POST'])
def create_user_api():
data = request.get_json()
if not data or not all(key in data for key in ('username', 'email', 'password')):
return jsonify({'error': 'Missing data'}), 400
if User.query.filter_by(username=data['username']).first():
return jsonify({'error': 'Username already exists'}), 400
if User.query.filter_by(email=data['email']).first():
return jsonify({'error': 'Email already exists'}), 400
user = User(username=data['username'], email=data['email'], password=data['password'])
db.session.add(user)
db.session.commit()
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email
}), 201
部署Flask应用
在生产环境中,不应使用Flask的开发服务器。以下是一些常见的部署选项:
使用Gunicorn和Nginx
# 安装Gunicorn
pip install gunicorn
# 创建wsgi.py
from app import create_app
app = create_app('production')
# 运行Gunicorn
gunicorn -w 4 -b 127.0.0.1:8000 wsgi:app
Nginx配置示例:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
使用Docker部署
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "wsgi:app"]
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
restart: always
结论
Flask是一个强大而灵活的Web框架,适合从小型应用到大型项目的各种需求。本文介绍了Flask的基础知识,包括路由、模板、数据库集成、表单处理、用户认证和部署等方面。
随着您的不断学习和实践,您可以进一步探索Flask的更多功能和扩展,如Flask-RESTful(构建API)、Flask-Migrate(数据库迁移)、Flask-Admin(管理界面)等。
希望本文能帮助您开始使用Flask构建Web应用的旅程!