skillby NEXTAltair

lorairo-repository-pattern

SQLAlchemy repository pattern implementation for LoRAIro database operations with type-safe transactions, session management, and ORM best practices

Installs: 0
Used in: 1 repos
Updated: 2d ago
$npx ai-builder add skill NEXTAltair/lorairo-repository-pattern

Installs to .claude/skills/lorairo-repository-pattern/

# LoRAIro Repository Pattern Skill

このSkillは、LoRAIroプロジェクトにおけるSQLAlchemyリポジトリパターンの実装ガイドを提供します。

## 使用タイミング

- 新しいデータベースアクセス層の実装
- 既存リポジトリの拡張・リファクタリング
- データベーストランザクション処理の実装
- ORM クエリの最適化

## LoRAIroのRepository Pattern

### 基本構造

```python
from typing import Optional
from sqlalchemy.orm import Session
from src.lorairo.database.schema import Image

class ImageRepository:
    """画像データアクセスリポジトリ"""

    def __init__(self, session_factory):
        """
        Args:
            session_factory: SQLAlchemy session factory(scoped_session)
        """
        self.session_factory = session_factory

    def get_by_id(self, image_id: int) -> Optional[Image]:
        """IDで画像を取得"""
        with self.session_factory() as session:
            return session.query(Image).filter(Image.id == image_id).first()

    def get_all(self) -> list[Image]:
        """全画像を取得"""
        with self.session_factory() as session:
            return session.query(Image).all()

    def add(self, image: Image) -> Image:
        """新規画像を追加"""
        with self.session_factory() as session:
            session.add(image)
            session.commit()
            session.refresh(image)  # IDなどを更新
            return image

    def update(self, image: Image) -> Image:
        """画像を更新"""
        with self.session_factory() as session:
            session.merge(image)
            session.commit()
            return image

    def delete(self, image_id: int) -> bool:
        """画像を削除"""
        with self.session_factory() as session:
            image = session.query(Image).filter(Image.id == image_id).first()
            if image:
                session.delete(image)
                session.commit()
                return True
            return False
```

## 重要な実装パターン

### 1. Session管理

```python
# ✅ Good: with文による自動管理
with self.session_factory() as session:
    result = session.query(Image).all()
    return result  # with終了時に自動commit/rollback

# ❌ Bad: 手動Session管理
session = self.session_factory()
try:
    result = session.query(Image).all()
    session.commit()
    return result
finally:
    session.close()  # 冗長で エラープローン
```

### 2. トランザクション管理

```python
def batch_add_images(self, images: list[Image]) -> list[Image]:
    """複数画像を一括追加(単一トランザクション)"""
    with self.session_factory() as session:
        session.add_all(images)
        session.commit()
        # 全てのimagesにIDが設定される
        return images

def complex_operation(self, data: dict) -> bool:
    """複雑な複数テーブル操作"""
    with self.session_factory() as session:
        # 複数の操作を1トランザクションで
        image = Image(**data['image'])
        session.add(image)

        annotation = Annotation(image_id=image.id, **data['annotation'])
        session.add(annotation)

        session.commit()  # 全て成功時のみcommit
        return True
```

### 3. 型安全なクエリ

```python
from typing import Optional
from dataclasses import dataclass

@dataclass
class SearchCriteria:
    """検索条件(型安全)"""
    tags: Optional[list[str]] = None
    min_score: Optional[float] = None
    max_score: Optional[float] = None

class ImageRepository:
    def search(self, criteria: SearchCriteria) -> list[Image]:
        """型安全な検索"""
        with self.session_factory() as session:
            query = session.query(Image)

            if criteria.tags:
                # タグ条件
                query = query.filter(Image.tags.contains(criteria.tags))

            if criteria.min_score is not None:
                query = query.filter(Image.score >= criteria.min_score)

            if criteria.max_score is not None:
                query = query.filter(Image.score <= criteria.max_score)

            return query.all()
```

### 4. エラーハンドリング

```python
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from loguru import logger

def add_image_safe(self, image: Image) -> Optional[Image]:
    """安全な画像追加(エラーハンドリング付き)"""
    try:
        with self.session_factory() as session:
            session.add(image)
            session.commit()
            session.refresh(image)
            return image
    except IntegrityError as e:
        logger.error(f"Integrity error adding image: {e}")
        return None
    except SQLAlchemyError as e:
        logger.error(f"Database error adding image: {e}")
        return None
```

## LoRAIro固有のガイドライン

### ファイル配置
- **Repository**: `src/lorairo/database/db_repository.py`
- **Schema**: `src/lorairo/database/schema.py`
- **Manager**: `src/lorairo/database/db_manager.py`
- **Core**: `src/lorairo/database/db_core.py`

### 命名規則
- Repository class: `{Entity}Repository`(例: `ImageRepository`)
- Methods: CRUD操作 → `get_*`, `add`, `update`, `delete`
- Methods: 検索操作 → `search`, `find_*`, `filter_*`

### テスト戦略
```python
import pytest
from src.lorairo.database.db_core import create_test_engine
from src.lorairo.database.db_repository import ImageRepository

@pytest.fixture
def test_repository():
    """テスト用リポジトリ"""
    engine = create_test_engine()
    session_factory = scoped_session(sessionmaker(bind=engine))
    yield ImageRepository(session_factory)
    session_factory.remove()

def test_add_image(test_repository):
    """画像追加テスト"""
    image = Image(path="/test/image.jpg", phash="abc123")
    result = test_repository.add(image)

    assert result.id is not None
    assert result.path == "/test/image.jpg"
```

## ベストプラクティス

### DO ✅
- **with文使用**: Session管理を自動化
- **型ヒント**: 全メソッドに型ヒント
- **単一責任**: 1 Repositoryは1 Entity
- **トランザクション統一**: 関連操作は1トランザクション
- **ロギング**: エラー時は必ずログ

### DON'T ❌
- **Session手動管理**: try-finally は避ける
- **ビジネスロジック混入**: Repositoryは純粋なデータアクセスのみ
- **N+1 クエリ**: eager loading(`joinedload`)を使用
- **文字列SQL**: ORM メソッドを使用
- **グローバルSession**: 常にsession_factoryから取得

## 参考リソース
- 既存実装: `src/lorairo/database/db_repository.py`
- スキーマ定義: `src/lorairo/database/schema.py`
- テスト例: `tests/database/test_db_repository.py`

Quick Install

$npx ai-builder add skill NEXTAltair/lorairo-repository-pattern

Details

Type
skill
Slug
NEXTAltair/lorairo-repository-pattern
Created
6d ago