快速入门flask
本文最后更新于-1天前,其中的信息可能已经过时,如有错误请发送邮件到2392862431@qq.com

大学期间选择的是web方向,主要学习的也是前后端,也是经典vue+spingboot,python基本没有怎么学,现在因为导师那边一个项目,然后叫我们跟着师兄师姐他们做一点,所以不得以得学一下python这边的后端框架flask了,话不多说,开始今天的学习了。

1、准备工作

工欲善其事必先利器,准备好环境以及开发工具不用多说。python环境肯定得有,而且建议python版本在3.9以上,如果python版本比较低,可以先去官网下载较新的版本。然后我选择的开发工具pycharm,vscode也是可以的,pycharm建议还是用专业版,社区版连创建flask项目都没有,都得手动配置,有点难受。有了python环境,大家就可以全局安装flask了,我是小白,所以选择全局,python里还有虚拟容器啥的,我嫌弃麻烦,就没有学了(偷个懒,哈哈)。

直接在windows控制台输入下列命令:

pip install Flask 

然后就是打开pycharm,创建一个flask项目,这里建议选择第二个选项,Existing interpreter,就是已经存在的解释器,这样不用每次都要配。

如果大家安装了多个python版本,注意,在pycharm配置的解释器用你全局配置的版本所在的文件夹,我刚开始就因为这个pycharm选择配置的解释器和电脑全局使用的默认解释器不一样,导致我傻乎乎的全局安装了一些包,但是在pycharm里面却死活找不到包,一引用就报错,真被自己蠢哭了,我这里就再补充一下,先用命令看一下自己的使用python的路径:

py -0p 

创建好项目后,就会得到一个默认的项目框架,如下:

之后我介绍我学习的项目架构与这个又不一样,但是我感觉大致是一样的,然后我们可以设置一下配置信息,点击那个小辣椒(hh)有一个配置选项,打开后可以手动设置debug模式,这个模式是什么呢,就是我们在修改了一部分代码后,不需要重启项目,直接保存文件后,刷新一下就可以看结果了,然后可以使用—host=xxxx设置主机号,–port=xxxx设置端口号,如下所示:

到这里基本准备工作就结束了,点击运行就可以看到简单结果了,我这里就不展示了,下面着重记录我自己学的项目结构体系,以及我的理解。

2、企业开发常用项目结构介绍

在AI的辅助下,我快速搭建起了一个企业开发中常用的项目结构,我学习的是前后端分离式的,就是后端单纯搞后端,不放前端代码。项目结构如下所示:

flask_projects/ # 项目根目录
├── app/ # 应用主目录
│ ├── init.py # 应用工厂文件
│ ├── extensions.py # Flask扩展实例文件
│ ├── api/ # API蓝图目录
│ │ ├── init.py # API蓝图初始化
│ │ └── routes.py # 路由定义文件
│ ├── models/ # 数据模型目录
│ │ ├── init.py
│ │ ├── user.py # 用户模型
│ │ └── book.py # 图书模型
│ ├── services/ # 业务逻辑目录
│ │ ├── init.py
│ │ ├── user_service.py # 用户相关业务逻辑
│ │ └── book_service.py # 图书相关业务逻辑
│ ├── static/ # 静态文件目录
│ └── templates/ # 模板文件目录
├── config/ # 配置文件目录
│ ├── init.py # 配置加载逻辑
│ ├── development.py # 开发环境配置
│ └── production.py # 生产环境配置
└── run.py # 应用启动文件

2.1 项目根目录

项目根目录总run.py是应用程序的入口文件,也就是我们用户启动flask应用,而requirements.txt是用于记录flask的项目依赖,那他具体有什么用呢——当别人丢给你一个flask项目,那你想要跑起来,是不是要有环境,requirements.txt这个文件就是记录你想要运行这个项目需要下载的依赖。咱们拿到项目后,第一步就是执行下列命令,安装项目依赖:

pip install -r requirements.txt 

那又有问题了,我们要怎样将项目所需的依赖写入这个文件中呢,只需要执行下列命令:

pip freeze > requirements.txt

2.2 app目录

app目录是flask的核心应用目录,里面主要包括了api目录,models目录、services目录、static目录,还有初始化应用文件和扩展文件:

2.2.1 app下的初始化文件__init__.py

先讲讲__init__.py文件,可能就有同学问为啥这个文件这样取名字呢,额,我只能说可能就是一种规定,就是说如果你文件命名为这个文件,flask就会自动扫描到这个文件作为初始化文件,这也是为什么在run.py中,可以直接引入然后创建实例:

from app import create_app

app = create_app()

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

然后我们具体来看一下初始化文件__init__.py中干了什么事

from flask import Flask
from config import config_by_name
from app.extensions import db, cors

def create_app(config_name='development'):
    app = Flask(__name__)
    
    # 加载配置
    app.config.from_object(config_by_name[config_name])
    
    # 初始化扩展
    db.init_app(app)
    cors.init_app(app, supports_credentials=True)
    
    # 注册蓝图
    from app.api import api_bp
    app.register_blueprint(api_bp, url_prefix='/api')
    
    return app 

加载配置就是将数据库的配置项加载进来,实际开发中会有开发环境和部署环境

class DevelopmentConfig:
    DEBUG = True
    TESTING = False
    # 数据库配置
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://prod_user:prod_password@prod_host/flask_prod"
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # 密钥配置
    SECRET_KEY = 'dev_secret_key'
    # 其他开发环境特定配置
    CORS_HEADERS = 'Content-Type' 
class ProductionConfig:
    DEBUG = False
    TESTING = False
    # 数据库配置
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://prod_user:prod_password@prod_host/flask_prod'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # 密钥配置
    SECRET_KEY = 'your_production_secret_key'  # 在实际部署时应该使用环境变量
    # 其他生产环境特定配置
    CORS_HEADERS = 'Content-Type' 

初始化扩展,这里就是将数据库实例对象导入,然后配置跨域问题,什么是跨域呢跨域是指在浏览器中,一个域下的文档或脚本试图请求另一个域下的资源时,域名、协议或端口不同,浏览器会出于安全考虑,限制跨源HTTP请求,这种安全策略被称为同源策略。实际一点来讲,就是如果你前后端若没有配置,前后端就无法正常对接。要么前端配置,要么后端配置。

注册蓝图又是什么呢?我第一次接触也是不知道啥意思,直白来说,蓝图就像是一个小型的应用程序,它允许你将相关的视图、模板和静态文件组织在一起。想象成一个大型商场:

  • 商场就是你的 Flask 应用
  • 不同的楼层(服装区、美食区、电器区)就是不同的蓝图
  • 每个区域都有自己的管理方式,但都属于同一个商场

例如在我们的代码中:

Apply to production.p…

# app/api/__init__.py

from flask import Blueprint

api_bp = Blueprint(‘api’, __name__)  # 创建一个名为’api’的蓝图

python

Apply to production.p…

# app/__init__.py

app.register_blueprint(api_bp, url_prefix=’/api’)  # 注册蓝图,并设置URL前缀

这样做的好处是:

可以轻松添加新的功能模块(比如后面添加 admin 蓝图:/admin/…)

所有 API 相关的路由都以 /api 开头

代码更容易维护,因为相关功能都组织在一起

2.2.2 扩展文件extensions.py

扩展,我个人感觉就和vscode中的扩展一样,可以很方便的使用别人封装好的功能,这里同样也是,实现跨域啊,数据库操作啊,都可以使用扩展文件,我们只要下载好了python包,在扩展里导入,调用创建好实例就好,之后就可以在其他文件中导入这些实例实现相对应的扩展功能。

from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS

# 创建扩展实例
db = SQLAlchemy()
cors = CORS() 

2.2.3 app下的api文件夹

这里显然是用于写实际路由api接口的,我觉得后端其实都很像,我学过Express.js,Spring Boot,现在的flask,我觉得他们都是很相似的,我觉得如果你有一个后端框架的基础,上手会很快的。api文件夹核心也就两个文件,一个是暴露出去的init文件,一个就是路由配置文件了,刚才我们在app下总的__init__.py中看到导入了api的api_bp,这个实例就是来自api文件夹下的__int__.py了

from flask import Blueprint

api_bp = Blueprint('api', __name__)

from app.api import routes 

然后我们重点来看一下rotues.py里的代码:

from flask import request, jsonify
from app.api import api_bp
from app.services.user_service import UserService
from app.services.book_service import BookService

@api_bp.route('/')
def hello_world():
    return 'Hello!'

@api_bp.route("/home")
def get_home():
    return '这是home主页'

@api_bp.route("/blog/<int:blog_id>")
def blog_detail(blog_id):
    return "当前页面是 %d" % blog_id

@api_bp.route("/book/list")
def book_list():
    page = request.args.get("page", default=1, type=int)
    return f"您获取的是第{page}页"

@api_bp.route('/user/selectAll', methods=['GET'])
def get_all_users():
    result = UserService.get_all_users()
    return jsonify(result), result['code']

@api_bp.route('/user/<int:user_id>', methods=['GET'])
def get_user(user_id):
    result = UserService.get_user_by_id(user_id)
    return jsonify(result), result['code']

@api_bp.route('/user/adduser', methods=['POST'])
def create_user():
    user_data = request.get_json()
    result = UserService.create_user(user_data)
    return jsonify(result), result['code']

@api_bp.route('/user/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    user_data = request.get_json()
    result = UserService.update_user(user_id, user_data)
    return jsonify(result), result['code']

@api_bp.route('/user/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    result = UserService.delete_user(user_id)
    return jsonify(result), result['code']

# 图书相关路由
@api_bp.route('/book/list', methods=['GET'])
def get_all_books():
    result = BookService.get_all_books()
    return jsonify(result), result['code']

@api_bp.route('/book/<int:book_id>', methods=['GET'])
def get_book(book_id):
    result = BookService.get_book_by_id(book_id)
    return jsonify(result), result['code']

@api_bp.route('/book/add', methods=['POST'])
def create_book():
    book_data = request.get_json()
    result = BookService.create_book(book_data)
    return jsonify(result), result['code']

@api_bp.route('/book/<int:book_id>', methods=['PUT'])
def update_book(book_id):
    book_data = request.get_json()
    result = BookService.update_book(book_id, book_data)
    return jsonify(result), result['code']

@api_bp.route('/book/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
    result = BookService.delete_book(book_id)
    return jsonify(result), result['code'] 

这一眼看过去,就基本同其他后端框架一样,这里写路由,然后我们还需要一个实现类去实现实际的增删改查,这里只负责调用实现方法,然后以正确的结果返回,这里可以配置路由的请求类型,是get啊,还是post,还是put,还是delete,需要什么参数等等。

2.2.4 models文件夹

app下的models文件夹是数据模型目录,这些数据模型是和数据库中的表一一对应的,比如在我这个项目中,目前有用户表和图书表,那么在models中就有两个对应的数据模型:

用户模型:

from app.extensions import db

class User(db.Model):
    __tablename__ = 'user'
    
    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)
    
    def to_dict(self):
        return {
            'id': self.id,
            'username': self.username,
            # 出于安全考虑,不返回密码
        } 

图书模型:

from app.extensions import db

class Book(db.Model):
    __tablename__ = 'book'
    
    id = db.Column(db.Integer, primary_key=True)
    book_name = db.Column(db.String(100), nullable=False)
    price = db.Column(db.Float, nullable=False)
    count = db.Column(db.Integer, nullable=False)
    
    def to_dict(self):
        return {
            'id': self.id,
            'book_name': self.book_name,
            'price': self.price,
            'count': self.count
        } 

2.2.5 services文件夹

app下的services文件夹是用于业务逻辑处理,我觉得和springboot中的service层一样,以及和Express中的router_handler一样,都是路由接口的实际处理逻辑,操作数据库这些操作都是在这实现的,话不多说,大家可以看一下下面的代码就知道了,我这里同样有两个py文件,一个是用户的业务处理逻辑,另一个是图书的业务处理逻辑,我这里就以用户的为例:

from app.models.user import User, db

class UserService:
    @staticmethod
    def get_all_users():
        try:
            users = User.query.all()
            return {'code': 200, 'data': [user.to_dict() for user in users], 'message': '获取用户列表成功'}
        except Exception as e:
            return {'code': 500, 'message': f'获取用户列表失败: {str(e)}'}

    @staticmethod
    def get_user_by_id(user_id):
        try:
            user = User.query.get(user_id)
            if user:
                return {'code': 200, 'data': user.to_dict(), 'message': '获取用户信息成功'}
            return {'code': 404, 'message': '用户不存在'}
        except Exception as e:
            return {'code': 500, 'message': f'获取用户信息失败: {str(e)}'}

    @staticmethod
    def create_user(user_data):
        try:
            # 检查用户名是否已存在
            if User.query.filter_by(username=user_data['username']).first():
                return {'code': 400, 'message': '用户名已存在'}
            
            new_user = User(
                username=user_data['username'],
                password=user_data['password']  # 实际项目中应该对密码进行加密
            )
            db.session.add(new_user)
            db.session.commit()
            return {'code': 200, 'data': new_user.to_dict(), 'message': '创建用户成功'}
        except Exception as e:
            db.session.rollback()
            return {'code': 500, 'message': f'创建用户失败: {str(e)}'}

    @staticmethod
    def update_user(user_id, user_data):
        try:
            user = User.query.get(user_id)
            if not user:
                return {'code': 404, 'message': '用户不存在'}
            
            # 如果要更新用户名,检查新用户名是否与其他用户冲突
            if 'username' in user_data and user_data['username'] != user.username:
                if User.query.filter_by(username=user_data['username']).first():
                    return {'code': 400, 'message': '用户名已存在'}
                user.username = user_data['username']
            
            if 'password' in user_data:
                user.password = user_data['password']  # 实际项目中应该对密码进行加密
            
            db.session.commit()
            return {'code': 200, 'data': user.to_dict(), 'message': '更新用户信息成功'}
        except Exception as e:
            db.session.rollback()
            return {'code': 500, 'message': f'更新用户信息失败: {str(e)}'}

    @staticmethod
    def delete_user(user_id):
        try:
            user = User.query.get(user_id)
            if not user:
                return {'code': 404, 'message': '用户不存在'}
            
            db.session.delete(user)
            db.session.commit()
            return {'code': 200, 'message': '删除用户成功'}
        except Exception as e:
            db.session.rollback()
            return {'code': 500, 'message': f'删除用户失败: {str(e)}'} 

2.2.5 static文件夹

这个文件夹一看名字就知道是用于存放用户上传的一些静态文件,图片,视频这些,不用多说了。

2.3 config文件夹

config文件夹其实上面已经涉及到了,数据库连接的代码这些的基本配置都在这里,然后跨域的配置啊以及一些其他的,我这个项目也是比较简单的结构,也就没有很多配置,代码这里就不贴了,上面已经贴了。

3、总结

到此为之,一个简单的flask项目的结构就介绍的差不多了,我觉得对于有后端开发基础的同学,上手还是不是很难的,我这里也是简单记录一下我的学习过程。每天进步一点点!

欢迎参观我的个人博客https://blog.heartwarming.online/

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇