简体中文 繁體中文 English Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français Japanese

站内搜索

搜索
AI 风月

活动公告

03-01 22:34
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

深入探索Flask项目目录的最佳组织方式从基础结构到高级模式打造可维护可扩展的Web应用架构

3万

主题

602

科技点

3万

积分

白金月票

碾压王

积分
32704

立华奏

发表于 2025-9-20 11:50:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 引言

Flask作为Python最受欢迎的微框架之一,以其简洁、灵活和高度可定制性而闻名。然而,这种灵活性也带来了一个挑战:如何组织项目目录结构以适应不同规模的应用需求?一个好的项目结构不仅能提高代码的可读性和可维护性,还能为未来的扩展奠定坚实基础。

本文将深入探讨Flask项目的目录组织方式,从最简单的单文件应用开始,逐步过渡到复杂的大型企业级应用架构,帮助开发者根据项目规模和需求选择最合适的结构模式。

2. Flask基础项目结构

2.1 最简单的Flask应用

对于极小型项目或原型验证,Flask应用可以是一个简单的Python文件:
  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def hello():
  5.     return "Hello, World!"
  6. if __name__ == '__main__':
  7.     app.run(debug=True)
复制代码

这种结构适合快速验证想法,但缺乏组织性,难以扩展。

2.2 基础包结构

当项目开始增长时,可以采用最基础的包结构:
  1. myapp/
  2.     app.py
  3.     requirements.txt
  4.     config.py
  5.     static/
  6.         css/
  7.         js/
  8.         images/
  9.     templates/
  10.         index.html
复制代码

在这个结构中:

• app.py包含主应用逻辑
• config.py存储配置信息
• static/存放静态文件
• templates/存放HTML模板

一个简单的app.py示例:
  1. from flask import Flask, render_template
  2. import config
  3. app = Flask(__name__)
  4. app.config.from_object(config)
  5. @app.route('/')
  6. def index():
  7.     return render_template('index.html')
  8. if __name__ == '__main__':
  9.     app.run(debug=True)
复制代码

对应的config.py:
  1. DEBUG = True
  2. SECRET_KEY = 'your-secret-key'
复制代码

3. 中等规模项目的目录组织

随着应用功能的增加,基础结构会显得力不从心。这时,我们需要采用更有组织性的方式。

3.1 应用工厂模式

应用工厂模式允许我们在不同环境中创建应用实例,便于测试和部署:
  1. myapp/
  2.     app/
  3.         __init__.py
  4.         routes.py
  5.         models.py
  6.         templates/
  7.         static/
  8.     config.py
  9.     requirements.txt
  10.     run.py
复制代码

app/__init__.py实现应用工厂:
  1. from flask import Flask
  2. from flask_sqlalchemy import SQLAlchemy
  3. from config import Config
  4. db = SQLAlchemy()
  5. def create_app(config_class=Config):
  6.     app = Flask(__name__)
  7.     app.config.from_object(config_class)
  8.    
  9.     db.init_app(app)
  10.    
  11.     from app import routes
  12.     app.register_blueprint(routes.bp)
  13.    
  14.     return app
复制代码

run.py作为应用入口:
  1. from app import create_app
  2. app = create_app()
  3. if __name__ == '__main__':
  4.     app.run(debug=True)
复制代码

3.2 蓝图(Blueprints)的使用

蓝图允许我们将应用分割为多个组件,每个组件负责一部分功能:
  1. myapp/
  2.     app/
  3.         __init__.py
  4.         auth/
  5.             __init__.py
  6.             routes.py
  7.             models.py
  8.             templates/
  9.         main/
  10.             __init__.py
  11.             routes.py
  12.             models.py
  13.             templates/
  14.         static/
  15.         templates/
  16.     config.py
  17.     requirements.txt
  18.     run.py
复制代码

auth/__init__.py创建蓝图:
  1. from flask import Blueprint
  2. bp = Blueprint('auth', __name__, url_prefix='/auth')
  3. from app.auth import routes
复制代码

auth/routes.py定义路由:
  1. from flask import render_template, redirect, url_for
  2. from app.auth import bp
  3. from app.auth.forms import LoginForm
  4. @bp.route('/login', methods=['GET', 'POST'])
  5. def login():
  6.     form = LoginForm()
  7.     if form.validate_on_submit():
  8.         # 处理登录逻辑
  9.         return redirect(url_for('main.index'))
  10.     return render_template('auth/login.html', title='Sign In', form=form)
复制代码

然后在主应用工厂中注册蓝图:
  1. def create_app(config_class=Config):
  2.     app = Flask(__name__)
  3.     app.config.from_object(config_class)
  4.    
  5.     db.init_app(app)
  6.    
  7.     from app.auth import bp as auth_bp
  8.     app.register_blueprint(auth_bp)
  9.    
  10.     from app.main import bp as main_bp
  11.     app.register_blueprint(main_bp)
  12.    
  13.     return app
复制代码

3.3 配置管理

对于更复杂的配置需求,可以采用基于类的配置:
  1. class Config:
  2.     SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard-to-guess-string'
  3.     SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
  4.         'sqlite:///' + os.path.join(basedir, 'app.db')
  5.     SQLALCHEMY_TRACK_MODIFICATIONS = False
  6. class DevelopmentConfig(Config):
  7.     DEBUG = True
  8. class TestingConfig(Config):
  9.     TESTING = True
  10.     SQLALCHEMY_DATABASE_URI = 'sqlite://'
  11. class ProductionConfig(Config):
  12.     pass
  13. config = {
  14.     'development': DevelopmentConfig,
  15.     'testing': TestingConfig,
  16.     'production': ProductionConfig,
  17.     'default': DevelopmentConfig
  18. }
复制代码

然后可以通过环境变量选择配置:
  1. def create_app(config_name=None):
  2.     if config_name is None:
  3.         config_name = os.environ.get('FLASK_CONFIG', 'default')
  4.     app = Flask(__name__)
  5.     app.config.from_object(config[config_name])
  6.     # ...
复制代码

4. 大型项目的高级模式

对于大型企业级应用,我们需要更复杂的架构来确保代码的可维护性和可扩展性。

4.1 分层架构
  1. myapp/
  2.     app/
  3.         __init__.py
  4.         domain/              # 领域模型和业务逻辑
  5.             __init__.py
  6.             models/
  7.                 __init__.py
  8.                 user.py
  9.                 product.py
  10.             services/
  11.                 __init__.py
  12.                 user_service.py
  13.                 product_service.py
  14.             repositories/
  15.                 __init__.py
  16.                 user_repository.py
  17.                 product_repository.py
  18.         infrastructure/       # 基础设施
  19.             __init__.py
  20.             database/
  21.                 __init__.py
  22.                 connection.py
  23.             external_apis/
  24.                 __init__.py
  25.                 payment_gateway.py
  26.             auth/
  27.                 __init__.py
  28.                 jwt_handler.py
  29.         interfaces/          # 接口层
  30.             __init__.py
  31.             web/
  32.                 __init__.py
  33.                 auth/
  34.                     __init__.py
  35.                     routes.py
  36.                     forms.py
  37.                     templates/
  38.                 main/
  39.                     __init__.py
  40.                     routes.py
  41.                     forms.py
  42.                     templates/
  43.                 static/
  44.             api/
  45.                 __init__.py
  46.                 v1/
  47.                     __init__.py
  48.                     auth.py
  49.                     users.py
  50.             cli/
  51.                 __init__.py
  52.                 commands.py
  53.         tests/               # 测试
  54.             __init__.py
  55.             unit/
  56.             integration/
  57.             e2e/
  58.     config/
  59.         __init__.py
  60.         default.py
  61.         development.py
  62.         production.py
  63.         testing.py
  64.     migrations/             # 数据库迁移
  65.     requirements/
  66.         base.txt
  67.         development.txt
  68.         production.txt
  69.     scripts/                # 实用脚本
  70.     docker/                 # Docker配置
  71.     .env.example
  72.     .gitignore
  73.     Dockerfile
  74.     docker-compose.yml
  75.     requirements.txt
  76.     run.py
复制代码

4.2 领域驱动设计(DDD)思想应用

在大型应用中,可以引入领域驱动设计的思想,将应用划分为不同的限界上下文(Bounded Context):
  1. myapp/
  2.     app/
  3.         __init__.py
  4.         identity/           # 身份限界上下文
  5.             __init__.py
  6.             domain/
  7.                 models/
  8.                 services/
  9.                 repositories/
  10.             infrastructure/
  11.                 persistence/
  12.                 email/
  13.             interfaces/
  14.                 web/
  15.                 api/
  16.         catalog/            # 目录限界上下文
  17.             __init__.py
  18.             domain/
  19.                 models/
  20.                 services/
  21.                 repositories/
  22.             infrastructure/
  23.                 persistence/
  24.                 search/
  25.             interfaces:
  26.                 web/
  27.                 api/
  28.         orders/             # 订单限界上下文
  29.             __init__.py
  30.             domain/
  31.                 models/
  32.                 services/
  33.                 repositories/
  34.             infrastructure/
  35.                 persistence/
  36.                 payment/
  37.             interfaces:
  38.                 web/
  39.                 api/
  40.         shared/             # 共享内核
  41.             __init__.py
  42.             kernel/
  43.                 exceptions.py
  44.                 value_objects.py
  45.             infrastructure/
  46.                 logging/
  47.                 monitoring/
复制代码

4.3 微服务架构准备

当应用进一步增长,可能需要考虑微服务架构。以下是准备微服务化的项目结构:
  1. myapp/
  2.     services/
  3.         user-service/
  4.             app/
  5.                 __init__.py
  6.                 domain/
  7.                 infrastructure/
  8.                 interfaces/
  9.             config/
  10.             requirements.txt
  11.             Dockerfile
  12.         product-service/
  13.             app/
  14.                 __init__.py
  15.                 domain/
  16.                 infrastructure/
  17.                 interfaces/
  18.             config/
  19.             requirements.txt
  20.             Dockerfile
  21.         order-service/
  22.             app/
  23.                 __init__.py
  24.                 domain/
  25.                 infrastructure/
  26.                 interfaces/
  27.             config/
  28.             requirements.txt
  29.             Dockerfile
  30.     shared/
  31.         libraries/
  32.             auth-lib/
  33.             events-lib/
  34.             logging-lib/
  35.     api-gateway/
  36.         app/
  37.             __init__.py
  38.             routes/
  39.             services/
  40.         config/
  41.         requirements.txt
  42.         Dockerfile
  43.     docker-compose.yml
复制代码

4.4 测试组织

对于大型项目,良好的测试组织至关重要:
  1. app/
  2.     tests/
  3.         __init__.py
  4.         conftest.py          # pytest配置和共享fixture
  5.         unit/
  6.             __init__.py
  7.             test_models.py
  8.             test_services.py
  9.             test_utils.py
  10.         integration/
  11.             __init__.py
  12.             test_database.py
  13.             test_apis.py
  14.         e2e/
  15.             __init__.py
  16.             test_user_flows.py
  17.         fixtures/
  18.             users.json
  19.             products.json
复制代码

conftest.py示例:
  1. import pytest
  2. from app import create_app
  3. from app.domain.models import db
  4. from app.infrastructure.database import reset_database
  5. @pytest.fixture
  6. def app():
  7.     app = create_app('testing')
  8.     with app.app_context():
  9.         db.create_all()
  10.         yield app
  11.         db.drop_all()
  12. @pytest.fixture
  13. def client(app):
  14.     return app.test_client()
  15. @pytest.fixture
  16. def runner(app):
  17.     return app.test_cli_runner()
  18. @pytest.fixture
  19. def auth_headers(client):
  20.     response = client.post('/api/auth/login', json={
  21.         'username': 'testuser',
  22.         'password': 'testpass'
  23.     })
  24.     token = response.get_json()['access_token']
  25.     return {'Authorization': f'Bearer {token}'}
复制代码

5. 实际案例分析

5.1 小型项目示例:个人博客
  1. blog/
  2.     app/
  3.         __init__.py
  4.         models.py
  5.         routes.py
  6.         forms.py
  7.         templates/
  8.             base.html
  9.             index.html
  10.             post.html
  11.             login.html
  12.         static/
  13.             css/
  14.                 style.css
  15.             js/
  16.                 main.js
  17.     migrations/
  18.     config.py
  19.     requirements.txt
  20.     run.py
复制代码

app/models.py:
  1. from app import db
  2. from datetime import datetime
  3. class Post(db.Model):
  4.     id = db.Column(db.Integer, primary_key=True)
  5.     title = db.Column(db.String(100), nullable=False)
  6.     content = db.Column(db.Text, nullable=False)
  7.     author = db.Column(db.String(50), nullable=False)
  8.     date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
  9.    
  10.     def __repr__(self):
  11.         return f"Post('{self.title}', '{self.author}', '{self.date_posted}')"
复制代码

app/routes.py:
  1. from flask import render_template, flash, redirect, url_for, request
  2. from app import app, db
  3. from app.models import Post
  4. from app.forms import PostForm
  5. @app.route("/")
  6. @app.route("/home")
  7. def home():
  8.     posts = Post.query.all()
  9.     return render_template('index.html', posts=posts)
  10. @app.route("/post/new", methods=['GET', 'POST'])
  11. def new_post():
  12.     form = PostForm()
  13.     if form.validate_on_submit():
  14.         post = Post(title=form.title.data, content=form.content.data, author=form.author.data)
  15.         db.session.add(post)
  16.         db.session.commit()
  17.         flash('Your post has been created!', 'success')
  18.         return redirect(url_for('home'))
  19.     return render_template('create_post.html', title='New Post', form=form, legend='New Post')
复制代码

5.2 中型项目示例:电子商务网站
  1. ecommerce/
  2.     app/
  3.         __init__.py
  4.         models/
  5.             __init__.py
  6.             user.py
  7.             product.py
  8.             order.py
  9.             category.py
  10.         routes/
  11.             __init__.py
  12.             main.py
  13.             auth.py
  14.             products.py
  15.             cart.py
  16.             orders.py
  17.         services/
  18.             __init__.py
  19.             auth_service.py
  20.             product_service.py
  21.             order_service.py
  22.         forms/
  23.             __init__.py
  24.             auth_forms.py
  25.             product_forms.py
  26.         templates/
  27.             base.html
  28.             auth/
  29.                 login.html
  30.                 register.html
  31.             products/
  32.                 product_list.html
  33.                 product_detail.html
  34.             cart/
  35.                 cart.html
  36.             orders/
  37.                 checkout.html
  38.                 order_confirmation.html
  39.         static/
  40.             css/
  41.             js/
  42.             img/
  43.     config/
  44.         __init__.py
  45.         default.py
  46.         development.py
  47.         production.py
  48.     migrations/
  49.     requirements/
  50.         base.txt
  51.         development.txt
  52.     tests/
  53.         __init__.py
  54.         unit/
  55.         integration/
  56.     run.py
复制代码

app/models/product.py:
  1. from app import db
  2. from datetime import datetime
  3. class Product(db.Model):
  4.     id = db.Column(db.Integer, primary_key=True)
  5.     name = db.Column(db.String(100), nullable=False)
  6.     description = db.Column(db.Text, nullable=False)
  7.     price = db.Column(db.Float, nullable=False)
  8.     stock = db.Column(db.Integer, nullable=False, default=0)
  9.     image_url = db.Column(db.String(255))
  10.     category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
  11.     date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
  12.    
  13.     def __repr__(self):
  14.         return f"Product('{self.name}', '{self.price}', '{self.stock}')"
  15.    
  16.     def is_in_stock(self, quantity=1):
  17.         return self.stock >= quantity
  18.    
  19.     def reduce_stock(self, quantity):
  20.         if self.is_in_stock(quantity):
  21.             self.stock -= quantity
  22.             db.session.commit()
  23.             return True
  24.         return False
复制代码

app/services/product_service.py:
  1. from app.models.product import Product
  2. from app import db
  3. class ProductService:
  4.     @staticmethod
  5.     def get_all_products():
  6.         return Product.query.all()
  7.    
  8.     @staticmethod
  9.     def get_product_by_id(product_id):
  10.         return Product.query.get(product_id)
  11.    
  12.     @staticmethod
  13.     def get_products_by_category(category_id):
  14.         return Product.query.filter_by(category_id=category_id).all()
  15.    
  16.     @staticmethod
  17.     def create_product(name, description, price, stock, image_url=None, category_id=None):
  18.         product = Product(
  19.             name=name,
  20.             description=description,
  21.             price=price,
  22.             stock=stock,
  23.             image_url=image_url,
  24.             category_id=category_id
  25.         )
  26.         db.session.add(product)
  27.         db.session.commit()
  28.         return product
  29.    
  30.     @staticmethod
  31.     def update_product(product_id, **kwargs):
  32.         product = Product.query.get(product_id)
  33.         if not product:
  34.             return None
  35.         
  36.         for key, value in kwargs.items():
  37.             if hasattr(product, key):
  38.                 setattr(product, key, value)
  39.         
  40.         db.session.commit()
  41.         return product
  42.    
  43.     @staticmethod
  44.     def delete_product(product_id):
  45.         product = Product.query.get(product_id)
  46.         if product:
  47.             db.session.delete(product)
  48.             db.session.commit()
  49.             return True
  50.         return False
复制代码

5.3 大型项目示例:企业级SaaS平台
  1. saas_platform/
  2.     app/
  3.         __init__.py
  4.         domain/
  5.             __init__.py
  6.             shared/
  7.                 __init__.py
  8.                 kernel/
  9.                     exceptions.py
  10.                     value_objects.py
  11.                     entities.py
  12.             identity/
  13.                 __init__.py
  14.                 models/
  15.                     user.py
  16.                     role.py
  17.                     permission.py
  18.                     tenant.py
  19.                 services/
  20.                     user_service.py
  21.                     auth_service.py
  22.                     tenant_service.py
  23.                 repositories/
  24.                     user_repository.py
  25.                     role_repository.py
  26.                     tenant_repository.py
  27.             billing/
  28.                 __init__.py
  29.                 models/
  30.                     subscription.py
  31.                     plan.py
  32.                     payment.py
  33.                     invoice.py
  34.                 services/
  35.                     subscription_service.py
  36.                     payment_service.py
  37.                     invoice_service.py
  38.                 repositories/
  39.                     subscription_repository.py
  40.                     payment_repository.py
  41.             analytics/
  42.                 __init__.py
  43.                 models/
  44.                     event.py
  45.                     metric.py
  46.                     report.py
  47.                 services/
  48.                     event_service.py
  49.                     metric_service.py
  50.                     report_service.py
  51.                 repositories/
  52.                     event_repository.py
  53.                     metric_repository.py
  54.         infrastructure/
  55.             __init__.py
  56.             persistence/
  57.                 __init__.py
  58.                 database.py
  59.                 session.py
  60.                 repositories/
  61.                     user_repository_impl.py
  62.                     subscription_repository_impl.py
  63.             external_apis/
  64.                 __init__.py
  65.                 payment_gateway.py
  66.                 email_service.py
  67.                 sms_service.py
  68.             auth/
  69.                 __init__.py
  70.                 jwt_handler.py
  71.                 password_hasher.py
  72.             logging/
  73.                 __init__.py
  74.                 logger.py
  75.                 handlers.py
  76.             caching/
  77.                 __init__.py
  78.                 redis_client.py
  79.         interfaces/
  80.             __init__.py
  81.             web/
  82.                 __init__.py
  83.                 auth/
  84.                     __init__.py
  85.                     routes.py
  86.                     forms.py
  87.                     templates/
  88.                 admin/
  89.                     __init__.py
  90.                     routes.py
  91.                     forms.py
  92.                     templates/
  93.                 dashboard/
  94.                     __init__.py
  95.                     routes.py
  96.                     forms.py
  97.                     templates/
  98.                 billing/
  99.                     __init__.py
  100.                     routes.py
  101.                     forms.py
  102.                     templates/
  103.                 static/
  104.                     css/
  105.                     js/
  106.                     img/
  107.             api/
  108.                 __init__.py
  109.                 v1/
  110.                     __init__.py
  111.                     auth/
  112.                         __init__.py
  113.                         routes.py
  114.                         schemas.py
  115.                     users/
  116.                         __init__.py
  117.                         routes.py
  118.                         schemas.py
  119.                     billing/
  120.                         __init__.py
  121.                         routes.py
  122.                         schemas.py
  123.                     analytics/
  124.                         __init__.py
  125.                         routes.py
  126.                         schemas.py
  127.             cli/
  128.                 __init__.py
  129.                 user_commands.py
  130.                 billing_commands.py
  131.                 analytics_commands.py
  132.         tests/
  133.             __init__.py
  134.             unit/
  135.                 __init__.py
  136.                 test_models/
  137.                 test_services/
  138.                 test_repositories/
  139.             integration/
  140.                 __init__.py
  141.                 test_apis/
  142.                 test_database/
  143.                 test_external_services/
  144.             e2e/
  145.                 __init__.py
  146.                 test_user_flows.py
  147.                 test_billing_flows.py
  148.             fixtures/
  149.                 __init__.py
  150.                 users.json
  151.                 subscriptions.json
  152.     config/
  153.         __init__.py
  154.         default.py
  155.         development.py
  156.         staging.py
  157.         production.py
  158.         testing.py
  159.     migrations/
  160.     requirements/
  161.         base.txt
  162.         development.txt
  163.         production.txt
  164.     scripts/
  165.         __init__.py
  166.         setup.py
  167.         migrate.py
  168.         backup.py
  169.     docker/
  170.         __init__.py
  171.         web/
  172.             Dockerfile
  173.         worker/
  174.             Dockerfile
  175.         db/
  176.             Dockerfile
  177.         redis/
  178.             Dockerfile
  179.     .env.example
  180.     .gitignore
  181.     docker-compose.yml
  182.     docker-compose.staging.yml
  183.     docker-compose.prod.yml
  184.     requirements.txt
  185.     run.py
复制代码

app/domain/identity/models/user.py:
  1. from app.domain.shared.kernel.entities import Entity
  2. from app.domain.shared.kernel.value_objects import Email, PasswordHash
  3. from app.domain.identity.models.role import Role
  4. from app.domain.identity.models.tenant import Tenant
  5. from dataclasses import dataclass
  6. from datetime import datetime
  7. from typing import List, Optional
  8. @dataclass
  9. class User(Entity):
  10.     email: Email
  11.     password_hash: PasswordHash
  12.     first_name: str
  13.     last_name: str
  14.     is_active: bool = True
  15.     is_verified: bool = False
  16.     created_at: datetime = None
  17.     updated_at: datetime = None
  18.     roles: List[Role] = None
  19.     tenant: Optional[Tenant] = None
  20.    
  21.     def __post_init__(self):
  22.         if self.created_at is None:
  23.             self.created_at = datetime.utcnow()
  24.         if self.updated_at is None:
  25.             self.updated_at = datetime.utcnow()
  26.         if self.roles is None:
  27.             self.roles = []
  28.    
  29.     def has_role(self, role_name: str) -> bool:
  30.         return any(role.name == role_name for role in self.roles)
  31.    
  32.     def add_role(self, role: Role) -> None:
  33.         if not self.has_role(role.name):
  34.             self.roles.append(role)
  35.             self.updated_at = datetime.utcnow()
  36.    
  37.     def remove_role(self, role_name: str) -> bool:
  38.         for i, role in enumerate(self.roles):
  39.             if role.name == role_name:
  40.                 self.roles.pop(i)
  41.                 self.updated_at = datetime.utcnow()
  42.                 return True
  43.         return False
  44.    
  45.     def set_password(self, password: str) -> None:
  46.         self.password_hash = PasswordHash.from_password(password)
  47.         self.updated_at = datetime.utcnow()
  48.    
  49.     def check_password(self, password: str) -> bool:
  50.         return self.password_hash.verify(password)
  51.    
  52.     def activate(self) -> None:
  53.         if not self.is_active:
  54.             self.is_active = True
  55.             self.updated_at = datetime.utcnow()
  56.    
  57.     def deactivate(self) -> None:
  58.         if self.is_active:
  59.             self.is_active = False
  60.             self.updated_at = datetime.utcnow()
  61.    
  62.     def verify(self) -> None:
  63.         if not self.is_verified:
  64.             self.is_verified = True
  65.             self.updated_at = datetime.utcnow()
复制代码

app/infrastructure/persistence/repositories/user_repository_impl.py:
  1. from sqlalchemy.orm import Session
  2. from app.domain.identity.models.user import User
  3. from app.domain.identity.repositories.user_repository import UserRepository
  4. from app.infrastructure.persistence.database import get_db
  5. from typing import Optional, List
  6. class UserRepositoryImpl(UserRepository):
  7.     def __init__(self, session: Session = None):
  8.         self._session = session or get_db()
  9.    
  10.     def save(self, user: User) -> User:
  11.         self._session.add(user)
  12.         self._session.commit()
  13.         self._session.refresh(user)
  14.         return user
  15.    
  16.     def find_by_id(self, user_id: int) -> Optional[User]:
  17.         return self._session.query(User).filter(User.id == user_id).first()
  18.    
  19.     def find_by_email(self, email: str) -> Optional[User]:
  20.         return self._session.query(User).filter(User.email == email).first()
  21.    
  22.     def find_all(self) -> List[User]:
  23.         return self._session.query(User).all()
  24.    
  25.     def find_by_tenant(self, tenant_id: int) -> List[User]:
  26.         return self._session.query(User).filter(User.tenant_id == tenant_id).all()
  27.    
  28.     def delete(self, user: User) -> bool:
  29.         try:
  30.             self._session.delete(user)
  31.             self._session.commit()
  32.             return True
  33.         except Exception:
  34.             self._session.rollback()
  35.             return False
  36.    
  37.     def exists_by_email(self, email: str) -> bool:
  38.         return self._session.query(User).filter(User.email == email).count() > 0
复制代码

app/interfaces/api/v1/auth/routes.py:
  1. from flask import Blueprint, request, jsonify
  2. from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity
  3. from app.domain.identity.services.auth_service import AuthService
  4. from app.interfaces.api.v1.auth.schemas import LoginSchema, RegisterSchema
  5. from marshmallow import ValidationError
  6. bp = Blueprint('auth_api', __name__, url_prefix='/api/v1/auth')
  7. @bp.route('/login', methods=['POST'])
  8. def login():
  9.     schema = LoginSchema()
  10.     try:
  11.         data = schema.load(request.get_json())
  12.     except ValidationError as err:
  13.         return jsonify({"errors": err.messages}), 400
  14.    
  15.     auth_service = AuthService()
  16.     user = auth_service.authenticate(data['email'], data['password'])
  17.    
  18.     if not user:
  19.         return jsonify({"error": "Invalid credentials"}), 401
  20.    
  21.     if not user.is_active:
  22.         return jsonify({"error": "Account is disabled"}), 401
  23.    
  24.     access_token = create_access_token(identity=user.id)
  25.     refresh_token = create_refresh_token(identity=user.id)
  26.    
  27.     return jsonify({
  28.         "access_token": access_token,
  29.         "refresh_token": refresh_token,
  30.         "user": {
  31.             "id": user.id,
  32.             "email": user.email,
  33.             "first_name": user.first_name,
  34.             "last_name": user.last_name,
  35.             "roles": [role.name for role in user.roles]
  36.         }
  37.     })
  38. @bp.route('/register', methods=['POST'])
  39. def register():
  40.     schema = RegisterSchema()
  41.     try:
  42.         data = schema.load(request.get_json())
  43.     except ValidationError as err:
  44.         return jsonify({"errors": err.messages}), 400
  45.    
  46.     auth_service = AuthService()
  47.    
  48.     if auth_service.user_exists(data['email']):
  49.         return jsonify({"error": "Email already registered"}), 409
  50.    
  51.     user = auth_service.register_user(
  52.         email=data['email'],
  53.         password=data['password'],
  54.         first_name=data['first_name'],
  55.         last_name=data['last_name']
  56.     )
  57.    
  58.     access_token = create_access_token(identity=user.id)
  59.     refresh_token = create_refresh_token(identity=user.id)
  60.    
  61.     return jsonify({
  62.         "access_token": access_token,
  63.         "refresh_token": refresh_token,
  64.         "user": {
  65.             "id": user.id,
  66.             "email": user.email,
  67.             "first_name": user.first_name,
  68.             "last_name": user.last_name,
  69.             "roles": [role.name for role in user.roles]
  70.         }
  71.     }), 201
  72. @bp.route('/refresh', methods=['POST'])
  73. @jwt_required(refresh=True)
  74. def refresh():
  75.     current_user_id = get_jwt_identity()
  76.     new_token = create_access_token(identity=current_user_id)
  77.     return jsonify({"access_token": new_token})
  78. @bp.route('/me', methods=['GET'])
  79. @jwt_required()
  80. def get_current_user():
  81.     current_user_id = get_jwt_identity()
  82.     auth_service = AuthService()
  83.     user = auth_service.get_user_by_id(current_user_id)
  84.    
  85.     if not user:
  86.         return jsonify({"error": "User not found"}), 404
  87.    
  88.     return jsonify({
  89.         "id": user.id,
  90.         "email": user.email,
  91.         "first_name": user.first_name,
  92.         "last_name": user.last_name,
  93.         "is_active": user.is_active,
  94.         "is_verified": user.is_verified,
  95.         "roles": [role.name for role in user.roles]
  96.     })
复制代码

6. 最佳实践和常见陷阱

6.1 文件和目录命名约定

• 使用小写字母和下划线命名文件和目录(例如user_service.py而不是userService.py)
• 模型文件使用单数形式(例如user.py而不是users.py)
• 蓝图目录使用复数形式(例如users/而不是user/)
• 测试文件以test_前缀开头
• 配置类使用大写字母开头(例如DevelopmentConfig)

6.2 循环导入问题及解决方案

在Flask项目中,循环导入是一个常见问题。例如,models.py可能需要导入app来定义数据库实例,而app/__init__.py可能需要导入models来注册蓝图。

解决方案1:延迟导入
  1. # app/__init__.py
  2. from flask import Flask
  3. from flask_sqlalchemy import SQLAlchemy
  4. db = SQLAlchemy()
  5. def create_app():
  6.     app = Flask(__name__)
  7.     app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
  8.     db.init_app(app)
  9.    
  10.     # 延迟导入以避免循环导入
  11.     from app.routes import main
  12.     app.register_blueprint(main.bp)
  13.    
  14.     return app
复制代码

解决方案2:使用应用工厂模式

应用工厂模式是解决循环导入的最佳方式,它将应用的创建过程封装在一个函数中,允许在需要时导入模块。

解决方案3:使用第三方扩展

像Flask-SQLAlchemy这样的扩展提供了延迟初始化的能力,可以帮助避免循环导入问题。

6.3 配置管理最佳实践

• 使用环境变量存储敏感信息
• 为不同环境(开发、测试、生产)创建不同的配置类
• 使用.env文件管理本地开发环境变量
• 考虑使用配置管理工具如python-dotenv或dynaconf

示例.env文件:
  1. FLASK_APP=run.py
  2. FLASK_ENV=development
  3. SECRET_KEY=your-secret-key-here
  4. DATABASE_URL=sqlite:///app.db
  5. MAIL_USERNAME=your-email@gmail.com
  6. MAIL_PASSWORD=your-app-password
复制代码

6.4 依赖管理

• 使用requirements.txt或Pipfile管理依赖
• 将依赖分为基础依赖和开发依赖
• 考虑使用pip-tools来固定依赖版本
• 对于大型项目,考虑使用Poetry进行依赖管理

示例requirements/base.txt:
  1. Flask==2.0.1
  2. Flask-SQLAlchemy==2.5.1
  3. Flask-Migrate==3.1.0
  4. Flask-Login==0.5.0
  5. Flask-WTF==0.15.1
  6. email-validator==1.1.3
  7. python-dotenv==0.19.0
复制代码

示例requirements/development.txt:
  1. -r base.txt
  2. pytest==6.2.5
  3. pytest-cov==2.12.1
  4. black==21.9b0
  5. flake8==3.9.2
  6. mypy==0.910
复制代码

7. 随着项目增长的演进策略

7.1 从简单结构逐步演进到复杂结构

阶段1:单文件应用

• 适用于原型和极小项目
• 所有代码在一个文件中

阶段2:基础包结构

• 将代码拆分为多个文件
• 引入基本的MVC模式

阶段3:应用工厂和蓝图

• 引入应用工厂模式
• 使用蓝图组织功能模块
• 分离配置和主应用逻辑

阶段4:服务层架构

• 引入服务层处理业务逻辑
• 分离模型和路由
• 使用依赖注入

阶段5:领域驱动设计

• 引入领域模型和值对象
• 按限界上下文组织代码
• 分离领域逻辑和基础设施

阶段6:微服务准备

• 识别可独立部署的服务
• 共享库提取
• API网关引入

7.2 重构技巧和工具

重构技巧:

1. 逐步重构:一次只重构一个小部分,确保应用仍然可以运行
2. 测试先行:在重构前编写测试,确保功能不受影响
3. 抽象层引入:逐步引入抽象层,减少直接依赖
4. 依赖注入:使用依赖注入减少模块间的耦合

重构工具:

1. IDE重构工具:PyCharm、VS Code等IDE提供的重构功能
2. 静态类型检查:使用mypy等工具确保类型安全
3. 代码分析工具:使用pylint、flake8等工具分析代码质量
4. 测试覆盖率工具:使用pytest-cov等工具确保测试覆盖

重构示例:从简单路由到服务层

原始代码:
  1. # routes.py
  2. from flask import render_template, request, redirect, url_for
  3. from app import app, db
  4. from app.models import User
  5. @app.route('/users')
  6. def list_users():
  7.     users = User.query.all()
  8.     return render_template('users/list.html', users=users)
  9. @app.route('/users/<int:user_id>')
  10. def user_detail(user_id):
  11.     user = User.query.get_or_404(user_id)
  12.     return render_template('users/detail.html', user=user)
  13. @app.route('/users/create', methods=['GET', 'POST'])
  14. def create_user():
  15.     if request.method == 'POST':
  16.         user = User(
  17.             username=request.form['username'],
  18.             email=request.form['email']
  19.         )
  20.         db.session.add(user)
  21.         db.session.commit()
  22.         return redirect(url_for('list_users'))
  23.     return render_template('users/create.html')
复制代码

重构后:
  1. # routes.py
  2. from flask import render_template, request, redirect, url_for
  3. from app import app
  4. from app.services.user_service import UserService
  5. from app.forms.user_form import UserForm
  6. user_service = UserService()
  7. @app.route('/users')
  8. def list_users():
  9.     users = user_service.get_all_users()
  10.     return render_template('users/list.html', users=users)
  11. @app.route('/users/<int:user_id>')
  12. def user_detail(user_id):
  13.     user = user_service.get_user_by_id(user_id)
  14.     return render_template('users/detail.html', user=user)
  15. @app.route('/users/create', methods=['GET', 'POST'])
  16. def create_user():
  17.     form = UserForm()
  18.     if form.validate_on_submit():
  19.         user_service.create_user(
  20.             username=form.username.data,
  21.             email=form.email.data
  22.         )
  23.         return redirect(url_for('list_users'))
  24.     return render_template('users/create.html', form=form)
  25. # services/user_service.py
  26. from app.models import User
  27. from app import db
  28. class UserService:
  29.     def get_all_users(self):
  30.         return User.query.all()
  31.    
  32.     def get_user_by_id(self, user_id):
  33.         return User.query.get_or_404(user_id)
  34.    
  35.     def create_user(self, username, email):
  36.         user = User(username=username, email=email)
  37.         db.session.add(user)
  38.         db.session.commit()
  39.         return user
复制代码

8. 结论

Flask的灵活性是其最大的优势之一,但也意味着开发者需要自行决定如何组织项目结构。从简单的单文件应用到复杂的企业级系统,项目结构的组织方式应当随着应用规模和复杂度的增长而演进。

本文详细介绍了从基础到高级的Flask项目目录组织方式,包括:

1. 基础项目结构,适合小型应用和快速原型
2. 中等规模项目的组织方式,引入应用工厂模式和蓝图
3. 大型企业级应用的高级模式,包括分层架构和领域驱动设计
4. 实际案例分析,展示不同规模项目的具体实现
5. 最佳实践和常见陷阱,帮助开发者避免常见问题
6. 项目演进策略,指导如何随着项目增长逐步优化结构

选择合适的项目结构不仅能提高开发效率,还能确保代码的可维护性和可扩展性。记住,没有一种结构适合所有项目,关键是根据项目规模、团队大小和业务需求选择最合适的组织方式。

最后,保持代码整洁、遵循最佳实践、编写充分的测试,这些习惯比任何特定的项目结构都更为重要。随着经验的积累,开发者将能够根据具体情况灵活调整项目结构,打造出真正可维护、可扩展的Flask应用架构。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

手机版|联系我们|小黑屋|TG频道|RSS |网站地图

Powered by Pixtech

© 2025-2026 Pixtech Team.

>