FlaskでAPIを作成するためのディレクトリ構成とモジュール管理のベストプラクティス
この記事では、Flaskを使ってAPIを開発する際のベストプラクティスに基づくディレクトリ構成例について解説します。
ディレクトリ構成
まず、Flaskアプリケーションの推奨ディレクトリ構成を示します:
my_flask_app/
│
├── app/
│ ├── __init__.py
│ ├── main/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── services.py
│ │ ├── models.py
│ │ ├── utils.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── services.py
│ │ ├── models.py
│ │ ├── utils.py
│ ├── config.py
│
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ ├── test_auth.py
│
├── venv/
│
├── .env
├── requirements.txt
└── run.py
各ファイルの詳細と役割
run.py
このファイルはアプリケーションのエントリーポイントです。Flaskアプリケーションを起動します。
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
app/__init__.py
Flaskアプリケーションのファクトリ関数を定義し、モジュールごとのブループリントを登録します。
from flask import Flask
def create_app():
app = Flask(__name__)
# Configuration
app.config.from_pyfile('config.py')
# Register Blueprints
from .main import main as main_blueprint
app.register_blueprint(main_blueprint, url_prefix='/main')
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app
app/config.py
アプリケーションの設定を管理します。例えば、データベースやセキュリティキーの設定などを行います。
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
app/main/__init__.py
メインモジュールの初期化ファイルです。ブループリントを作成し、ルートをインポートします。
from flask import Blueprint
main = Blueprint('main', __name__)
from . import routes
app/main/routes.py
メインモジュールのルートを定義します。ここでは、エンドポイントの定義を行い、サービスロジックを呼び出します。
from flask import jsonify, request
from . import main
from .services import create_user_service, get_user_service
@main.route('/')
def index():
return jsonify({'message': 'Welcome to the Main Module!'})
@main.route('/users', methods=['POST'])
def create_user():
data = request.json
user = create_user_service(data)
return jsonify(user), 201
@main.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = get_user_service(user_id)
if user:
return jsonify(user)
else:
return jsonify({'message': 'User not found'}), 404
app/main/services.py
メインモジュールのビジネスロジックを定義します。ここでは、ユーザーの作成と取得のロジックを実装しています。
from .models import User
from . import db
def create_user_service(data):
new_user = User(username=data['username'], email=data['email'])
db.session.add(new_user)
db.session.commit()
return {'id': new_user.id, 'username': new_user.username, 'email': new_user.email}
def get_user_service(user_id):
user = User.query.get(user_id)
if user:
return {'id': user.id, 'username': user.username, 'email': user.email}
return None
app/main/models.py
データベースモデルを定義します。ここでは、ユーザーモデルを定義しています。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
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}>'
app/main/utils.py
ユーティリティ関数を定義します。一般的な処理やヘルパー関数はこちらに記述します。
def format_date(date):
return date.strftime('%Y-%m-%d')
app/auth/__init__.py
認証モジュールの初期化ファイルです。
from flask import Blueprint
auth = Blueprint('auth', __name__)
from . import routes
app/auth/routes.py
認証モジュールのルートを定義します。
from flask import jsonify, request
from . import auth
from .services import login_service, register_service
@auth.route('/register', methods=['POST'])
def register():
data = request.json
user = register_service(data)
return jsonify(user), 201
@auth.route('/login', methods=['POST'])
def login():
data = request.json
response = login_service(data)
return jsonify(response)
app/auth/services.py
認証モジュールのビジネスロジックを定義します。ここでは、ユーザーの登録とログインのロジックを実装しています。
from werkzeug.security import generate_password_hash, check_password_hash
from .models import User
from . import db
def register_service(data):
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = User(username=data['username'], password_hash=hashed_password)
db.session.add(new_user)
db.session.commit()
return {'id': new_user.id, 'username': new_user.username}
def login_service(data):
user = User.query.filter_by(username=data['username']).first()
if user and check_password_hash(user.password_hash, data['password']):
return {'message': 'Login successful'}
return {'message': 'Invalid credentials'}, 401
app/auth/models.py
認証に関連するデータベースモデルを定義します。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
def __repr__(self):
return f'<User {self.username}>'
app/auth/utils.py
認
証モジュールのユーティリティ関数を定義します。
from werkzeug.security import generate_password_hash, check_password_hash
def hash_password(password):
return generate_password_hash(password)
def verify_password(password_hash, password):
return check_password_hash(password_hash, password)
テストディレクトリ構成
テストを含めたディレクトリ構成の例です:
my_flask_app/
│
├── app/
│ ├── __init__.py
│ ├── main/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── services.py
│ │ ├── models.py
│ │ ├── utils.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── services.py
│ │ ├── models.py
│ │ ├── utils.py
│ ├── config.py
│
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ ├── test_auth.py
│
├── venv/
│
├── .env
├── requirements.txt
└── run.py
tests/__init__.py
テストモジュールの初期化ファイルです。
# tests/__init__.py
tests/test_main.py
メインモジュールのテストを定義します。
import unittest
from app import create_app
from app.main.models import db
class MainTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
self.app.config['TESTING'] = True
self.client = self.app.test_client()
with self.app.app_context():
db.create_all()
def tearDown(self):
with self.app.app_context():
db.session.remove()
db.drop_all()
def test_index(self):
response = self.client.get('/main/')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Welcome to the Main Module!', response.data)
def test_create_user(self):
response = self.client.post('/main/users', json={
'username': 'testuser',
'email': 'test@example.com'
})
self.assertEqual(response.status_code, 201)
self.assertIn(b'testuser', response.data)
def test_get_user(self):
response = self.client.post('/main/users', json={
'username': 'testuser',
'email': 'test@example.com'
})
user_id = response.get_json()['id']
response = self.client.get(f'/main/users/{user_id}')
self.assertEqual(response.status_code, 200)
self.assertIn(b'testuser', response.data)
if __name__ == '__main__':
unittest.main()
tests/test_auth.py
認証モジュールのテストを定義します。
import unittest
from app import create_app
from app.auth.models import db
class AuthTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
self.app.config['TESTING'] = True
self.client = self.app.test_client()
with self.app.app_context():
db.create_all()
def tearDown(self):
with self.app.app_context():
db.session.remove()
db.drop_all()
def test_register(self):
response = self.client.post('/auth/register', json={
'username': 'testuser',
'password': 'password'
})
self.assertEqual(response.status_code, 201)
self.assertIn(b'testuser', response.data)
def test_login(self):
self.client.post('/auth/register', json={
'username': 'testuser',
'password': 'password'
})
response = self.client.post('/auth/login', json={
'username': 'testuser',
'password': 'password'
})
self.assertEqual(response.status_code, 200)
self.assertIn(b'Login successful', response.data)
if __name__ == '__main__':
unittest.main()
環境設定ファイルと依存関係
.env
環境変数を定義します。例えば、秘密鍵やデータベースURLを設定します。
SECRET_KEY=your_secret_key
DATABASE_URL=sqlite:///app.db
requirements.txt
必要なパッケージをリストします。
Flask
Flask-SQLAlchemy
python-dotenv
まとめ
このディレクトリ構成とモジュール分割に従うことで、エンドポイントは簡潔に保たれ、ビジネスロジックやデータベースモデルは独立して管理されるため、プロジェクトの拡張や変更が容易になります。
Discussion