project/
├── app/
│ ├── __init__.py # Application factory
│ ├── models/ # Modèles de données
│ ├── views/ # Routes et vues
│ ├── templates/ # Templates Jinja2
│ ├── static/ # Assets statiques
│ └── utils/ # Utilitaires
├── tests/ # Tests unitaires
├── config.py # Configuration
├── requirements.txt # Dépendances
└── run.py # Point d'entrée
# app/__init__.py
def create_app(config_name='default'):
app = Flask(__name__)
# Charger la configuration
app.config.from_object(config[config_name])
# Initialiser les extensions
db.init_app(app)
login_manager.init_app(app)
# Enregistrer les blueprints
from .views import main
app.register_blueprint(main)
return app
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
# Dans les formulaires
class MonFormulaire(FlaskForm):
# Le token CSRF est automatiquement ajouté
from flask_talisman import Talisman
Talisman(app,
force_https=True,
content_security_policy={
'default-src': "'self'",
'script-src': "'self'"
})
class ConfigError(Exception):
"""Erreur de configuration."""
pass
@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return render_template('500.html'), 500
# Logging
import logging
logger = logging.getLogger(__name__)
@app.before_first_request
def setup_logging():
if not app.debug:
# Configuration du logging
handler = logging.FileHandler('app.log')
handler.setLevel(logging.INFO)
app.logger.addHandler(handler)
import pytest
from app import create_app, db
@pytest.fixture
def app():
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'redis'})
@cache.memoize(timeout=300)
def get_user(user_id):
return User.query.get(user_id)
# Eager loading
posts = Post.query.options(
joinedload('author')
).all()