MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python Flask框架的基础教程

2021-11-093.5k 阅读

1. Flask 框架简介

Flask 是一个使用 Python 编写的轻量级 Web 应用框架。它基于 Werkzeug WSGI 工具箱和 Jinja2 模板引擎。WSGI(Web Server Gateway Interface)是 Python 中定义 Web 服务器与 Web 应用程序之间接口的规范,Werkzeug 则是对这个规范的一个实现,提供了请求、响应对象等一系列基础工具。Jinja2 是一个功能强大的模板引擎,用于将动态数据嵌入到 HTML 等模板文件中。

Flask 的设计理念是保持核心简单,同时提供足够的灵活性,允许开发者根据需求添加扩展。这使得 Flask 非常适合快速原型开发以及小型到中型规模的 Web 应用开发。与 Django 这样的大型框架相比,Flask 没有内置的数据库抽象层、表单验证等复杂功能,但这也意味着开发者可以自由选择适合项目需求的第三方库来实现这些功能。

2. 安装 Flask

在开始使用 Flask 之前,需要先安装它。假设你已经安装了 Python 和 pip(Python 的包管理工具),可以通过以下命令在终端中安装 Flask:

pip install flask

如果使用的是 Python 3.4 及以上版本,也可以使用 pip3 命令:

pip3 install flask

安装完成后,可以通过导入 flask 模块来验证安装是否成功:

import flask
print(flask.__version__)

上述代码会输出安装的 Flask 版本号。

3. 创建第一个 Flask 应用

下面来创建一个简单的 Flask 应用。在项目目录中创建一个新的 Python 文件,例如 app.py,并编写以下代码:

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'


if __name__ == '__main__':
    app.run()

在上述代码中:

  • 首先从 flask 模块导入 Flask 类。
  • 然后创建一个 Flask 类的实例 app__name__ 是 Python 的一个内置变量,它表示当前模块的名称。在主程序中,__name__ 的值通常是 __main__。Flask 使用这个名称来确定应用的根路径等信息。
  • @app.route('/') 是一个装饰器,它将一个函数绑定到一个 URL 规则。这里将 hello_world 函数绑定到根路径 '/'。当用户访问应用的根路径时,Flask 会调用 hello_world 函数,并将其返回值作为响应发送给客户端。
  • if __name__ == '__main__': 确保只有在直接运行这个脚本时,才会启动 Flask 开发服务器。app.run() 启动服务器,默认情况下,它会在 http://127.0.0.1:5000 上运行应用。

在终端中运行 python app.py,然后在浏览器中访问 http://127.0.0.1:5000,就可以看到 Hello, World! 的输出。

4. URL 路由

4.1 基本路由

URL 路由是 Flask 应用的核心功能之一,它决定了客户端请求的 URL 如何映射到相应的处理函数。前面已经看到了一个简单的根路径路由示例。可以定义更多不同的路由:

from flask import Flask

app = Flask(__name__)


@app.route('/about')
def about():
    return 'This is an about page'


@app.route('/contact')
def contact():
    return 'Contact us at contact@example.com'


if __name__ == '__main__':
    app.run()

在上述代码中,分别定义了 /about/contact 两个路由,当用户访问相应的 URL 时,会得到对应的响应。

4.2 动态路由

有时候需要在 URL 中包含动态部分,例如根据用户 ID 显示不同的用户信息。Flask 支持通过在路由中使用 <variable_name> 的形式来定义动态路由。

from flask import Flask

app = Flask(__name__)


@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}'


if __name__ == '__main__':
    app.run()

show_user_profile 函数的路由中,<username> 是一个字符串类型的变量,它会捕获 URL 中的相应部分并作为参数传递给函数。在 show_post 函数的路由中,<int:post_id> 表示捕获的变量 post_id 是整数类型。如果用户访问 /user/johnshow_user_profile 函数会接收到 username'john';如果访问 /post/123show_post 函数会接收到 post_id123

4.3 路由转换器

除了 int 类型的转换器,Flask 还提供了其他几种常用的路由转换器:

  • string:默认的转换器,匹配任何不包含斜杠的文本(字符串类型)。
  • int:匹配整数。
  • float:匹配浮点数。
  • path:类似 string,但可以匹配包含斜杠的文本。
  • uuid:匹配 UUID 字符串。

例如:

from flask import Flask

app = Flask(__name__)


@app.route('/file/<path:file_path>')
def show_file_path(file_path):
    return f'File path: {file_path}'


if __name__ == '__main__':
    app.run()

在上述代码中,/file/<path:file_path> 可以匹配像 /file/dir1/dir2/file.txt 这样包含斜杠的路径。

5. 请求与响应

5.1 请求对象

当 Flask 接收到客户端的请求时,会创建一个请求对象。可以通过从 flask 模块导入 request 来访问这个对象,它包含了关于请求的各种信息,例如请求方法、请求头、请求体等。

from flask import Flask, request

app = Flask(__name__)


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        return f'Logging in user {username} with password {password}'
    else:
        return '''
            <form method="post">
                <label for="username">Username:</label>
                <input type="text" id="username" name="username"><br><br>
                <label for="password">Password:</label>
                <input type="password" id="password" name="password"><br><br>
                <input type="submit" value="Login">
            </form>
        '''


if __name__ == '__main__':
    app.run()

在上述代码中:

  • @app.route('/login', methods=['GET', 'POST']) 表示 /login 路由支持 GETPOST 两种请求方法。
  • request.method 获取当前请求的方法。如果是 POST 方法,request.form 是一个类似字典的对象,用于获取通过表单提交的键值对数据,这里获取了 usernamepassword 字段。如果是 GET 方法,则返回一个包含登录表单的 HTML 字符串。

5.2 响应对象

Flask 视图函数的返回值会自动转换为响应对象。可以直接返回字符串,Flask 会将其作为响应体,并设置默认的响应头。也可以手动创建响应对象,以更精细地控制响应。

from flask import Flask, make_response

app = Flask(__name__)


@app.route('/')
def index():
    response = make_response('This is a custom response')
    response.headers['Content-Type'] = 'text/plain'
    return response


if __name__ == '__main__':
    app.run()

在上述代码中,使用 make_response 创建了一个响应对象,然后设置了 Content-Type 响应头为 text/plain。除了设置响应头,还可以设置状态码等其他属性:

from flask import Flask, make_response

app = Flask(__name__)


@app.route('/error')
def error():
    response = make_response('Error occurred', 404)
    return response


if __name__ == '__main__':
    app.run()

这里创建了一个状态码为 404 的响应,表示页面未找到。

6. 模板引擎 Jinja2

6.1 基本模板渲染

Jinja2 是 Flask 默认使用的模板引擎,它允许将动态数据嵌入到 HTML 模板中。首先在项目目录中创建一个 templates 文件夹,这是 Flask 默认查找模板文件的地方。然后创建一个 index.html 文件:

<!DOCTYPE html>
<html>

<head>
    <title>Flask Jinja2 Example</title>
</head>

<body>
    <h1>Hello, {{ name }}!</h1>
</body>

</html>

在 Python 代码中渲染这个模板:

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    name = 'John'
    return render_template('index.html', name=name)


if __name__ == '__main__':
    app.run()

在上述代码中,render_template 函数用于渲染指定的模板文件,并将 name 变量传递给模板。在模板中,{{ name }} 会被替换为实际的 name'John'

6.2 模板继承

模板继承是 Jinja2 的一个强大功能,它允许创建一个基础模板,其他模板可以继承并扩展这个基础模板。创建一个 base.html 文件作为基础模板:

<!DOCTYPE html>
<html>

<head>
    <title>{% block title %}Default Title{% endblock %}</title>
    {% block styles %}
    <style>
        body {
            font-family: Arial, sans-serif;
        }
    </style>
    {% endblock %}
</head>

<body>
    <header>
        <h1>My Website</h1>
    </header>
    {% block content %}
    <p>This is the default content</p>
    {% endblock %}
    <footer>
        <p>&copy; 2024 My Company</p>
    </footer>
</body>

</html>

然后创建一个 index.html 文件继承自 base.html

{% extends 'base.html' %}

{% block title %}Home Page{% endblock %}

{% block content %}
<p>This is the home page content</p>
{% endblock %}

在 Python 代码中渲染 index.html

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

在这个例子中,index.html 继承了 base.html 的结构,并重写了 titlecontent 块。这样可以避免在多个模板中重复编写相同的 HTML 结构,提高代码的可维护性。

6.3 模板过滤器

Jinja2 提供了许多内置的模板过滤器,用于在模板中对变量进行转换和格式化。例如,capitalize 过滤器将字符串的首字母大写:

<!DOCTYPE html>
<html>

<head>
    <title>Flask Jinja2 Filter Example</title>
</head>

<body>
    <p>{{ 'hello world' | capitalize }}</p>
</body>

</html>

上述代码会输出 Hello world。其他常用的过滤器包括 lower(将字符串转换为小写)、upper(将字符串转换为大写)、trim(去除字符串两端的空白字符)、format(格式化字符串)等。还可以自定义模板过滤器,在 Python 代码中:

from flask import Flask, render_template

app = Flask(__name__)


def reverse_string(s):
    return s[::-1]


app.add_template_filter(reverse_string,'reverse')


@app.route('/')
def index():
    text = 'Hello, Flask!'
    return render_template('index.html', text=text)

index.html 模板中可以使用自定义的 reverse 过滤器:

<!DOCTYPE html>
<html>

<head>
    <title>Custom Filter Example</title>
</head>

<body>
    <p>{{ text | reverse }}</p>
</body>

</html>

上述代码会将 text 变量的值反转输出。

7. 静态文件

Web 应用通常需要提供静态文件,如 CSS、JavaScript 和图片等。在 Flask 中,默认情况下,静态文件应该放在项目目录中的 static 文件夹下。可以通过以下方式在模板中引用静态文件:

<!DOCTYPE html>
<html>

<head>
    <title>Static Files Example</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>

<body>
    <h1>My Page</h1>
    <script src="{{ url_for('static', filename='script.js') }}"></script>
</body>

</html>

在上述代码中,url_for 函数用于生成静态文件的 URL。'static' 是 Flask 中用于表示静态文件的端点,filename 参数指定静态文件的名称。这样,当 Flask 应用运行时,会正确地将 styles.cssscript.js 文件的 URL 插入到 HTML 中。

8. 处理表单

8.1 使用 Flask-WTF 扩展

虽然可以直接通过 request.form 来处理表单数据,但 Flask-WTF 扩展提供了更方便和安全的方式来处理表单,它集成了 CSRF(跨站请求伪造)保护等功能。首先安装 Flask-WTF:

pip install flask-wtf

然后创建一个表单类:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Login')

在上述代码中:

  • flask_wtf 导入 FlaskForm 作为表单基类。
  • 使用 wtforms 中的 StringFieldPasswordFieldSubmitField 分别定义用户名、密码和提交按钮字段。
  • validators=[DataRequired()] 表示这些字段是必填的。

在视图函数中使用这个表单:

from flask import Flask, render_template, request
from flask_wtf.csrf import CSRFProtect
from forms import LoginForm

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
csrf = CSRFProtect(app)


@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        return f'Logging in user {username} with password {password}'
    return render_template('login.html', form=form)


if __name__ == '__main__':
    app.run()

login.html 模板中渲染表单:

<!DOCTYPE html>
<html>

<head>
    <title>Login</title>
</head>

<body>
    <form method="post">
        {{ form.csrf_token }}
        {{ form.username.label }}: {{ form.username }}<br><br>
        {{ form.password.label }}: {{ form.password }}<br><br>
        {{ form.submit }}
    </form>
</body>

</html>

在上述代码中:

  • app.config['SECRET_KEY'] 用于设置 CSRF 保护所需的密钥。
  • form.validate_on_submit() 方法会验证表单数据是否有效。如果有效,通过 form.field_name.data 可以获取表单字段的值。
  • 在模板中,{{ form.csrf_token }} 用于生成 CSRF 令牌,确保表单的安全性。

9. 数据库集成

9.1 使用 SQLite 和 Flask-SQLAlchemy

SQLite 是一个轻量级的嵌入式数据库,非常适合小型应用。Flask-SQLAlchemy 是一个用于 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:///example.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)


@app.route('/create')
def create():
    db.create_all()
    return 'Database created'


if __name__ == '__main__':
    app.run()

在上述代码中:

  • app.config['SQLALCHEMY_DATABASE_URI'] 设置了数据库的连接字符串,这里使用 SQLite 并指定数据库文件为 example.db
  • 创建了一个 User 模型类,继承自 db.Model。定义了 idusernameemail 字段,id 是主键,usernameemail 都设置为唯一且不能为空。
  • @app.route('/create') 对应的视图函数 create 调用 db.create_all() 来创建数据库表。

9.2 数据库操作

可以进行添加、查询、更新和删除等数据库操作:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///example.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)


@app.route('/add')
def add():
    new_user = User(username='john', email='john@example.com')
    db.session.add(new_user)
    db.session.commit()
    return 'User added'


@app.route('/query')
def query():
    users = User.query.all()
    result = ', '.join([user.username for user in users])
    return f'Users: {result}'


@app.route('/update')
def update():
    user = User.query.filter_by(username='john').first()
    if user:
        user.email = 'new_email@example.com'
        db.session.commit()
        return 'User updated'
    return 'User not found'


@app.route('/delete')
def delete():
    user = User.query.filter_by(username='john').first()
    if user:
        db.session.delete(user)
        db.session.commit()
        return 'User deleted'
    return 'User not found'


if __name__ == '__main__':
    app.run()

在上述代码中:

  • add 函数创建一个新的 User 实例并添加到数据库会话中,然后提交会话将数据保存到数据库。
  • query 函数查询所有用户,并将用户名拼接成字符串返回。
  • update 函数通过用户名查询用户,找到后更新其 email 字段并提交会话。
  • delete 函数通过用户名查询用户,找到后从数据库会话中删除并提交会话。

10. 部署 Flask 应用

10.1 使用 Gunicorn 和 Nginx

在生产环境中,通常不直接使用 Flask 自带的开发服务器,而是使用 Gunicorn 作为应用服务器,Nginx 作为反向代理服务器。首先安装 Gunicorn:

pip install gunicorn

假设应用的入口文件是 app.py,并且 Flask 实例名为 app,可以通过以下命令启动 Gunicorn:

gunicorn -w 4 -b 127.0.0.1:8000 app:app

在上述命令中,-w 4 表示使用 4 个工作进程,-b 127.0.0.1:8000 表示绑定到本地的 8000 端口,app:app 表示应用的入口文件 app.py 中的 app 实例。

然后配置 Nginx 作为反向代理,在 Nginx 的配置文件(通常在 /etc/nginx/sites - available/ 目录下)中添加以下内容:

server {
    listen 80;
    server_name your_domain.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}

保存配置文件后,创建一个符号链接到 /etc/nginx/sites - enabled/ 目录,然后重启 Nginx 服务:

sudo ln -s /etc/nginx/sites - available/your_config /etc/nginx/sites - enabled/
sudo systemctl restart nginx

这样,Nginx 会将外部请求转发到 Gunicorn 运行的 Flask 应用,提高应用的性能和安全性。

10.2 使用 Heroku

Heroku 是一个云平台即服务(PaaS),可以方便地部署 Flask 应用。首先在项目目录中创建一个 requirements.txt 文件,列出项目依赖:

flask
gunicorn

然后创建一个 Procfile 文件,指定启动命令:

web: gunicorn app:app

在 Heroku 官网注册账号并安装 Heroku CLI。登录 Heroku:

heroku login

初始化 Git 仓库并提交代码:

git init
git add.
git commit -m "Initial commit"

创建一个 Heroku 应用:

heroku create

将代码推送到 Heroku:

git push heroku main

Heroku 会自动检测项目类型,安装依赖并启动应用。可以通过 Heroku 提供的 URL 访问部署的 Flask 应用。

通过以上内容,对 Flask 框架从基础到较为深入的方面进行了学习,包括路由、请求响应处理、模板引擎、表单处理、数据库集成以及部署等,希望能帮助你更好地使用 Flask 开发 Web 应用。