flask面试题

1.flask的路由系统是怎样的?

Flask 的蓝图(Blueprint)是一个构建和组织应用程序的工具,蓝图允许开发者将应用程序的功能和视图分解成独立的组件,使得大型应用程序的结构更加清晰和模块化。通过使用蓝图,可以在一个项目中创建多个模块,每个模块可以包含自己的路由、视图函数、模板、静态文件等。
1.创建蓝图对象

# blueprints_example.py
from flask import Blueprint

# 创建一个蓝图对象,命名为 'example'
example_bp = Blueprint('example', __name__)

# 在蓝图中定义路由
@example_bp.route('/hello')
def hello():
    return "Hello from the example blueprint!"

2.在主应用中注册蓝图,让蓝图定义的路由可以加入到主应用中

# app.py
from flask import Flask
from blueprints_example import example_bp  # 导入蓝图模块

app = Flask(__name__)

# 注册蓝图到主应用
app.register_blueprint(example_bp, url_prefix='/example')
# 通过 `url_prefix`,可以为蓝图的所有路由添加统一的路径前缀

# 主应用路由
@app.route('/')
def index():
    return "Welcome to the Flask App!"

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

这时访问http://127.0.0.1:5000/example/hello 就可以访问到hello函数。
当项目目录较为复杂时,我们并不需要将所有的路由注册在根目录下的app.py中。当我们运行run.py时,run.py 通常是应用的入口文件,Python 解释器会加载 app1 包,为此会执行 app1/init.py 文件中的代码。这包括创建 Flask 应用实例、加载配置信息、初始化扩展(如 SQLAlchemy 和 Migrate)、导入蓝图并进行蓝图注册等。
image
image

当我有多个包(app1、app2,类似django中的app)时,你可以在根目录下的 run.py 文件中进行加载和注册。具体步骤如下:

确保每个包(如 app1 和 app2)都有自己独立的初始化文件和蓝图注册逻辑。
在根目录下的 run.py 文件中,从每个包中导入应用实例,并进行蓝图的注册。
项目架构:
image
app1/init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

# 创建 Flask 应用实例
app1 = Flask(__name__)
# 加载配置
app1.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app1)
# 初始化数据库迁移工具
migrate = Migrate(app1, db)

# 导入蓝图
from app1.views.student_views import student_bp

# 注册蓝图
app1.register_blueprint(student_bp, url_prefix='/app1')

# 导入模型,避免循环导入问题
from app1.models.student import Student

app2/init.py:

from flask import Flask
from config import Config

# 创建 Flask 应用实例
app2 = Flask(__name__)
# 加载配置
app2.config.from_object(Config)

# 导入蓝图
from app2.views.another_view import another_bp

# 注册蓝图
app2.register_blueprint(another_bp, url_prefix='/app2')

run.py:

from app1 import app1
from app2 import app2

if __name__ == '__main__':
    app1.run(debug=True, port=5000)
    app2.run(debug=True, port=5001)

但是如果你有多个包,建议在一个统一的应用实例中注册所有蓝图,例如:
run.py:

from flask import Flask
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

from app1 import app1
from app2 import app2

# 将 app1 和 app2 的蓝图都注册到主应用上
app.register_blueprint(app1.blueprints['student_bp'], url_prefix='/app1')
app.register_blueprint(app2.blueprints['another_bp'], url_prefix='/app2')

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

2.如何在Flask中进行数据库操作?

1.安装SQLAlchemy

pip install flask_sqlalchemy

2.配置数据库:
根目录config.py:

class Config:
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://admin:123456@localhost/test_flask'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

3.在每一个包(app)下初始化Flask应用并配置SQLAlchemy:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app)

4.定义模型表:

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

5.数据库迁移:
迁移每一个app下的表之前需要现将FLASK_APP设置为存放app对象的目录下:

export FLASK_APP=app1  # app对象在app1/__init__.py下
flask db init
"""
当你在命令行中输入 flask db init 并执行后,Flask - Migrate 会在项目根目录下创建一个名为 migrations 的文件夹,该文件夹用于存放数据库迁移相关的文件和配置信息。
"""
flask db migrate
"""
该命令会自动检测数据库模型(如 SQLAlchemy 定义的模型)和当前数据库模式之间的差异,并根据这些差异在migrations目录下生成一个新的迁移脚本。
"""
flask db upgrade
"""
此命令用于将新生成的迁移脚本应用到数据库中。Flask - Migrate 会在数据库中维护一个特殊的表(通常名为 alembic_version),用于记录当前数据库所应用的迁移版本
"""

6.操作数据库

3.Flask的中间件是什么?如何使用中间件?

Flask 的中间件是一段代码,它拦截并处理应用程序的请求和响应。中间件可以在 Flask 应用程序处理请求之前或生成响应之后执行操作,比如分析、过滤、修改请求或响应内容,或者记录日志等。

1.使用 before_request 和 after_request 装饰器:

app1/init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

# 创建 Flask 应用实例
app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app)
# 初始化数据库迁移工具
migrate = Migrate(app, db)

@app.before_request
def before_request_func():
    print('before_request')


@app.after_request
def after_request_func(response):  # 这个函数随意命名,但是装饰器必须按照after_request或者before_request_func
    print("before_request")
    return response
	# after_request装饰的函数必须有个入参response并且返回

# 导入蓝图
from app1.views.student_views import student_bp
from app1.views.teacher_views import teacher_bp

# 注册蓝图
app.register_blueprint(student_bp, url_prefix='/api')
app.register_blueprint(teacher_bp,url_prefix='/api/v2')

# 导入模型,避免循环导入问题
from app1.models.student import Student

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

如果想把中间件写在单独的一个文件里,需要显示地导入到app1/init.py中,否则这个文件不会被加载:
app1/init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

# 创建 Flask 应用实例
app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app)
# 初始化数据库迁移工具
migrate = Migrate(app, db)

from app1.views import middleware
# 导入蓝图
from app1.views.student_views import student_bp
from app1.views.teacher_views import teacher_bp

# 注册蓝图
app.register_blueprint(student_bp, url_prefix='/api')
app.register_blueprint(teacher_bp,url_prefix='/api/v2')

# 导入模型,避免循环导入问题
from app1.models.student import Student

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

app1/views/middleware.py:

from app1 import app

@app.before_request
def before_request_func():
    print('before_request')


@app.after_request
def xxx(response):
    print("after_request")
    return response

这样app1下的所有接口访问时都会执行before_request_func和xxx函数。
image

2.自定义 WSGI 中间件

Flask 是基于 WSGI(Web Server Gateway Interface)的,因此可以通过创建一个 WSGI 中间件来包装 Flask 应用。
app1/views/wsgi_middleware.py:

class CustomMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        print("在处理请求之前执行中间件逻辑")
        # 调用 Flask app 继续处理请求。通过 self.app(environ, start_response) 来调用真正的 Flask 应用程序。
        response = self.app(environ, start_response)
        print("在返回响应之后执行中间件逻辑")
        return response

app1/init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config
from app1.views.wsgi_middleware import CustomMiddleware

# 创建 Flask 应用实例
app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app)
# 初始化数据库迁移工具
migrate = Migrate(app, db)

app.wsgi_app = CustomMiddleware(app.wsgi_app)  # 包装为中间件

# from app1.views import middleware
# 导入蓝图
from app1.views.student_views import student_bp
from app1.views.teacher_views import teacher_bp

# 注册蓝图
app.register_blueprint(student_bp, url_prefix='/api')
app.register_blueprint(teacher_bp,url_prefix='/api/v2')

# 导入模型,避免循环导入问题
from app1.models.student import Student

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

image

4.flask如何捕获异常处理?

1.使用app对象.register_error_handler来处理:

app1/views/student_views.py:

@student_bp.route('/test/error', methods=['GET'])
def test_error():
    raise Exception("This is a test error message")

app1/init.py:

from flask import Flask, jsonify

app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 异常处理

def handle_exception(e):
    return jsonify({"error": str(e)}), 500
# handle_exception名字可以随便起

app.register_error_handler(Exception, handle_exception)

目录结构:
image
image

2.使用try-except处理:

app1/views/student_views.py:

@student_bp.route('/test/<int:num1>/<int:num2>', methods=['GET'])
def test_rollback(num1, num2):
    try:
        result = num1 / num2
        return jsonify({"result": result}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500

image

5.flask怎样自动以异常类?

自定义一个类继承Exception类(不用导入),在__init__中处理message信息:
app1/views/student_views.py:

from app1.exception import TestException
from app1 import  app

@app.route('/test/exception/<int:num1>/<int:num2>', methods=['GET'])  # 注意这里没有使用蓝图
def test_exception(num1, num2):
    try:
        result = num1 / num2
        return jsonify({"result": result}), 200
    except Exception as e:
        raise TestException("This is a test exception message")

app1/exception.py:

class TestException(Exception):
    def __init__(self, message):
        prefix = "Test Exception: "
        self.message = prefix + message
        super().__init__(self.message)

6.如何用flask构建一个用户管理系统?

1.首先定制模型表:
user/models.py:

from user import db
from user.auth import jwt, authenticate, generate_token, permission_required
from werkzeug.security import generate_password_hash, check_password_hash



# 用户角色关联表
user_roles = db.Table('user_roles',
                      db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
                      db.Column('role_id', db.Integer, db.ForeignKey('roles.id'))
                      )
"""
在 Flask 中,通常使用像上面这样的代码手动定义关联表。Flask 的数据库操作库(如 SQLAlchemy)没有像 Django 那样自动创建关联表的功能,需要开发者明确地定义关联表的结构,包括列的定义、外键关系等。这样做的好处是开发者对数据库结构有更精细的控制
"""

# 角色表
class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True, nullable=False)
    permissions = db.Column(db.String(255))

    def __repr__(self):
        return f'<Role {self.name}>'

# 用户表
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)
    roles = db.relationship('Role', secondary=user_roles, backref=db.backref('users', lazy='dynamic'))

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

    def has_permission(self, permission):
        for role in self.roles:
            if permission in role.permissions.split(','):
                return True
        return False

    def __repr__(self):
        return f'<User {self.username}>'

2.定义密码加密和解密操作:
generate_password_hash 和 check_password_hash 是 Werkzeug 库中用于密码哈希和验证的两个重要函数,Werkzeug 是一个为 Python Web 开发提供实用工具的库,Flask 也依赖于它。以下详细介绍它们的用法和作用:
generate_password_hash:该函数的主要作用是将明文密码转换为经过哈希处理后的密码。哈希处理是一种单向加密技术,将输入的密码通过特定的算法转换为一个固定长度的字符串,这个字符串无法通过反向计算还原出原始密码,从而提高了密码存储的安全性。
check_password_hash:该函数用于验证用户输入的明文密码与存储在数据库中的哈希密码是否匹配。它会将用户输入的明文密码进行相同的哈希处理,然后与存储的哈希密码进行比较,判断是否一致。

create_access_token 函数用于生成一个 JWT 访问令牌。这个令牌可以包含用户的身份信息(如用户 ID、用户名等),并在后续请求中用于验证用户身份。

get_jwt_identity 函数用于从请求的 JWT 中提取用户的身份信息。在受保护的路由中,可以使用这个函数来获取当前请求的用户身份,从而进行后续的权限验证等操作
user/auth.py:

from flask_jwt_extended import JWTManager, create_access_token, get_jwt_identity
from flask import jsonify, request

jwt = JWTManager()


def authenticate(username, password):
    from user.models import User
    user = User.query.filter_by(username=username).first()
    if user and user.check_password(password):
        return user
    return None


def identity(payload):
    from user.models import User
    user_id = payload['identity']
    return User.query.get(user_id)


def generate_token(user):
    access_token = create_access_token(identity=user.id)
    return access_token


def permission_required(permission):
    def decorator(func):
        def wrapper(*args, **kwargs):
            current_user_id = get_jwt_identity()
            current_user = User.query.get(current_user_id)
            if not current_user or not current_user.has_permission(permission):
                return jsonify({"message": "Permission denied"}), 403
            return func(*args, **kwargs)

        return wrapper

    return decorator

3.在该app下的__init__.py中配置数据库:
user/init.py:

from flask import Flask, jsonify
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

user_app = Flask(__name__)
# 加载配置
user_app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(user_app)
# 初始化数据库迁移工具
migrate = Migrate(user_app, db)

from .models import *

if __name__ == '__main__':
    user_app.run(debug=True)

4.执行数据库迁移操作:

export FLASK_APP=user
"""
在数据库迁移之前操作:
export FLASK_APP=user(在 Windows 系统中可能是set FLASK_APP=user)这步操作的作用是设置 Flask 应用的入口点,在 Flask 中,需要让系统知道哪个是 Flask 应用的实例,以便 Flask 的命令行工具或相关运行机制能够找到并加载应用。
像flask db migrate这样的数据库迁移命令也依赖于正确设置的FLASK_APP,以便找到应用的数据库配置和相关模型定义来执行迁移操作。
"""
flask db migrate
flask db upgrade

7.Flask中的上下文是什么?如何使用上下文?

在 Flask 里,上下文是一种用于管理应用程序运行时环境信息的机制。它能够让开发者在不同的函数和模块里访问当前请求、应用实例等重要信息。Flask 主要有两种上下文:应用上下文(Application Context)和请求上下文(Request Context)。
应用上下文提供了与 Flask 应用实例相关的信息。它主要用于在没有请求的情况下访问应用级别的信息,像配置、数据库连接等。
目录结构:
image

app.py:

from flask import Flask
from student import app
# app = Flask(__name__)


@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'


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

student/init.py:

from flask import Flask, current_app
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate


app = Flask(__name__)
# 加载配置
app.config.from_object(Config)

# 初始化数据库
db = SQLAlchemy(app)
# 初始化数据库迁移工具
migrate = Migrate(app, db)


from student.views import student_bp
app.register_blueprint(student_bp, url_prefix='/api')

# 导入模型,避免循环导入问题
from student.models import Student

if __name__ == '__main__':
    with app.app_context():
        # 访问应用的配置信息
        config_value = current_app.config.get('DEBUG')
        print("config_value", config_value)
    app.run(debug=True)

student/views.py:

from flask import Blueprint, current_app

student_bp = Blueprint('student_bp', __name__)

@student_bp.route('/student')
def student():
    with current_app.app_context():
        app_name = current_app.name
        print(f"当前应用名称: {app_name}")
        return f"当前应用名称: {app_name}"

image
image
生命周期:在应用启动时创建,在应用关闭时销毁。可以手动激活和退出,也可以在请求处理期间自动激活。可以用作记录日志。

8.Flask中的请求对象是什么?如何使用请求对象?

在 Flask 里,请求对象是用于处理客户端发送的 HTTP 请求的核心工具。它封装了与当前请求相关的所有信息,像请求方法、请求参数、请求头等。

@student_bp.route('/get/request/info')
def info():
    a = request.method  # 获取请求方法
    b = request.url  # 获取请求URL
    c = request.headers  # 获取请求头
    d = dict(request.args)  # 获取查询参数
    return "xx"

image
post请求获取请求体参数:

@student_bp.route('/get/request/info', methods=['POST'])
def info():
    data = request.get_json()
    return data

客户端发送的请求体是 JSON 格式的数据时,使用request.get_json(),request.get_data() 方法用于获取请求体的原始数据,它会以字节串(bytes)的形式返回请求体的内容。

posted @ 2025-02-27 23:04  ERROR404Notfound  阅读(1)  评论(0)    收藏  举报
Title