本文へ移動
cccskills
無料GitHub で公開

sqlalchemy-patterns

Framework-agnostic SQLAlchemy 2.0 core shared by fastapi-plugin and flask-plugin: declarative mapped classes with Mapped/mapped_column, column type selection, relationships with explicit lazy loading and cascades, 2.0-style select() querying, transaction/flush discipline, and Alembic-agnostic migration metadata rules. Framework plugins layer their delta skills (async sessions for FastAPI, Flask-SQLAlchemy integration for Flask) on top of this skill. Use this skill to: - Write SQLAlchemy 2.0 declarative models with Mapped[T] annotations and mapped_column(). - Pick correct column types (String(N), Numeric, DateTime(timezone=True), Uuid, Enum). - Define relationships with explicit lazy loading strategy and cascade settings. - Query with 2.0-style select() statements and manage flush vs commit boundaries. - Keep model metadata visible to Alembic autogenerate. Do NOT use this skill for: - FastAPI async engine/session lifecycle — see fastapi-plugin:sqlalchemy-patterns. - Flask-SQLAlchemy extension setup and Flask-Migrate — see flask-plugin:sqlalchemy-patterns. - Python idioms — see python-foundation:python-conventions.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

SQLAlchemy 2.0 Patterns (framework-agnostic core)

Detection

Read pyproject.toml or requirements.txt before writing any model code:

grep -E "sqlalchemy" pyproject.toml requirements.txt
  • SQLAlchemy 2.0+: use Mapped/mapped_column syntax (shown throughout this skill). This is the assumed baseline.
  • SQLAlchemy 1.x: use Column()/relationship() style. Mark a comment in the code noting the legacy version; do not silently mix styles.

Always prefer 2.0 style for new code. When working in an existing project using 1.x style throughout, match the existing style. Never mix 1.x Column() and 2.0 mapped_column() in the same model.


Declarative base and mapped classes

Define one Base class per project. All models inherit from it. (Framework plugins may supply the base for you — e.g., Flask-SQLAlchemy's db.Model — but the mapped-column rules below are identical.)

from datetime import datetime
from decimal import Decimal
from typing import Optional

from sqlalchemy import DateTime, ForeignKey, Numeric, String, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
    display_name: Mapped[str] = mapped_column(String(100))
    hashed_password: Mapped[str] = mapped_column(String(255))
    is_active: Mapped[bool] = mapped_column(default=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )

    orders: Mapped[list["Order"]] = relationship(
        "Order", back_populates="user", lazy="selectin"
    )

    def __repr__(self) -> str:
        return f"<User id={self.id} email={self.email!r}>"

Key rules:

  • Mapped[T] without Optional means NOT NULL. Mapped[Optional[T]] means nullable.
  • mapped_column() without a SQLAlchemy type uses Python type inference — always provide the type explicitly (e.g., String(255)) so the migration specialist can finalize column lengths, precision, and constraints correctly.
  • Use server_default=func.now() for database-side default timestamps, not Python-side default=datetime.utcnow (Python-side defaults are not reflected in the DB schema, and utcnow is deprecated).

Column type guidance

Python typeSQLAlchemy column typeNotes
strString(N)Always set length; never bare String
DecimalNumeric(precision, scale)Never Float for money or precise values
datetimeDateTime(timezone=True)Always set timezone=True
intInteger or BigIntegerUse BigInteger for large tables (users, events)
boolBoolean
UUIDUuid (SA 2.0+) or String(36)Uuid stores as native UUID on PostgreSQL
enumEnum(MyEnum, native_enum=False)native_enum=False for DB portability
floatFloatOnly for non-monetary approximations (lat/lon, scores)

Querying patterns (2.0 style)

Always use 2.0-style select() statements executed through the session. The session/execution style (sync db.session vs async AsyncSession) comes from the framework delta skill; the statement construction is identical.

from sqlalchemy import select
from sqlalchemy.orm import selectinload

select(User).where(User.id == user_id)                      # single row by predicate
select(User).where(User.email == email)                     # lookup by unique column
select(User).offset(skip).limit(limit)                      # pagination
select(User).options(selectinload(User.orders)).where(...)  # explicit eager load

Result handling rules:

  • Use scalar_one_or_none() for single-row queries.
  • Use scalars().all() for multi-row queries.
  • Use session.get(Model, pk) for primary-key lookups where the session API supports it.

Transaction/flush discipline:

  • Use flush() inside a unit of work to get the generated PK without committing.
  • Commit once per request/unit of work at the boundary the framework defines (FastAPI: the get_db() dependency on yield exit; Flask: the view/service layer at the end of the request) — never scattered through helper functions.

Relationships

Define relationships with explicit lazy and cascade settings. Never rely on defaults.

from sqlalchemy.orm import Mapped, mapped_column, relationship


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)

    orders: Mapped[list["Order"]] = relationship(
        "Order",
        back_populates="user",
        lazy="selectin",
        cascade="all, delete-orphan",
    )

    profile: Mapped[Optional["UserProfile"]] = relationship(
        "UserProfile",
        back_populates="user",
        lazy="selectin",
        uselist=False,
        cascade="all, delete-orphan",
    )


class Order(Base):
    __tablename__ = "orders"

    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))

    user: Mapped["User"] = relationship("User", back_populates="orders", lazy="selectin")
    lines: Mapped[list["OrderLine"]] = relationship(
        "OrderLine",
        back_populates="order",
        lazy="selectin",
        cascade="all, delete-orphan",
    )

Structural rules:

  • Always pair both sides with back_populates.
  • Use uselist=False for the scalar side of one-to-one relations.
  • Set ondelete on the ForeignKey (e.g., "CASCADE") so the constraint matches the ORM cascade.

Lazy loading strategy guide:

  • lazy="selectin" — loads the related collection with a separate SELECT IN query. Best for small-to-medium collections that are always needed. Safe in both sync and async contexts.
  • lazy="select" — separate SELECT on first attribute access (SQLAlchemy default). Only safe in synchronous contexts; in async it triggers implicit IO and fails.
  • lazy="joined" — loads the relation with a JOIN in the same query. Best for one-to-one relations or small, always-needed collections.
  • lazy="raise" — raises if accessed without explicit eager loading, forcing selectinload()/joinedload() at query time. Best for large or rarely-needed collections to prevent N+1.
  • lazy="subquery" — sync-only; not supported by async drivers.
  • lazy="dynamic" — deprecated in SQLAlchemy 2.0. Do not use. Replace with explicit select() queries.

Which strategies are permitted per framework is refined by the framework delta skill (async contexts forbid select/subquery).

To prevent N+1 queries on list endpoints, apply eager-load options at query time:

from sqlalchemy.orm import joinedload, selectinload

select(User).options(selectinload(User.orders))

Migration metadata rules (Alembic-agnostic)

Both FastAPI (raw Alembic) and Flask (Flask-Migrate, which wraps Alembic) rely on --autogenerate reading the metadata object (Base.metadata / db.metadata).

  • Import all models before target_metadata is set. Any model module not imported is invisible to autogenerate and its tables are silently missed.
  • Centralize model imports in one module (e.g., app/db/base.py or app/models/__init__.py) so a single import guarantees complete metadata.
  • The metadata object is the source of truth for --autogenerate; explicit column types (lengths, precision, timezone=True) are what make the generated migrations correct.
  • The migration specialist agent runs migration commands; the architect agent only defines models.

Framework-specific wiring (async env.py for FastAPI, flask db init scaffolding for Flask) lives in the respective delta skill.


Anti-patterns (shared)

Anti-patternProblemCorrect approach
String without lengthAlembic autogenerate produces VARCHAR with no length; some databases use TEXT or reject itAlways String(N)
Float for monetary valuesIEEE 754 rounding errors on financial calculationsNumeric(precision, scale)
lazy="dynamic"Deprecated in SQLAlchemy 2.0; raises a warningUse lazy="selectin"/lazy="select" (per framework) or explicit selectinload()
default=datetime.utcnowPython-side default — not reflected in DB schema; utcnow is deprecatedserver_default=func.now() with DateTime(timezone=True)
Importing models only in routersMigration tooling never sees them; --autogenerate misses tablesImport all models in the central metadata module
Mixing Column() and mapped_column()Produces inconsistent metadata; confuses toolingOne style per project; prefer mapped_column() for new code
Scattered commit() calls in helpersMakes unit testing harder; unclear transaction boundariesCommit once per unit of work at the framework-defined boundary; flush() for PKs

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Angular 18-21 project structure, standalone components vs NgModule, control flow (@if/@for/@switch + *ngIf/*ngFor legacy), decorators, dependency injection (inject() function), lifecycle hooks, pipes, Angular Universal SSR pointer. Use this skill to: - Detect project style (standalone vs NgModule) and apply matching patterns. - Pick correct decorators and DI approach. - Use modern control flow (@if/@for/@switch) in Angular 17+ projects. - Apply `inject()` function over constructor injection where appropriate. - Wire bootstrap correctly (bootstrapApplication for standalone, AppModule for legacy). Do NOT use this skill for: - State management (see angular-state-and-rx). - Routing (see angular-routing). - Forms (see angular-forms). - Testing (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular forms: Reactive Forms (preferred — typed FormGroup/FormControl since Angular 14, FormBuilder, custom + async validators, FormArray, multi-step) and Template-driven (`[(ngModel)]` + FormsModule). Validation strategies, server error mapping, accessibility. Use this skill to: - Build Reactive Forms with typed FormGroup/FormControl. - Use FormBuilder для concise syntax. - Implement custom synchronous and async validators. - Wire FormArray for dynamic field lists. - Map server errors back to form fields. - Pick Reactive vs Template-driven (prefer Reactive). Do NOT use this skill for: - General conventions (see angular-conventions). - State management beyond forms (see angular-state-and-rx). - Routing (see angular-routing). - Testing forms (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular Router (built-in `@angular/router`) — route configuration for standalone and NgModule projects, functional guards (Angular 14.1+), lazy loading, route resolvers, typed params via signals/observables, programmatic navigation, route data and meta. Use this skill to: - Configure routes (standalone-style or NgModule-style). - Use functional guards (canActivate as function, preferred over class-based in 17+). - Lazy-load components or feature modules. - Implement auth guards via route meta + functional guards. - Read params/queries via `inject(ActivatedRoute)` + signals or RxJS. Do NOT use this skill for: - General conventions (see angular-conventions). - State management (see angular-state-and-rx). - Forms (see angular-forms). - Testing routes (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

State management for Angular 18-21: signals (signal/computed/effect), services-as-state, NgRx Store + Effects + Selectors, NgRx Component Store, NgRx Signals (newer signal-based store). RxJS essentials — operators, async pipe, takeUntilDestroyed, signal/observable interop. Use this skill to: - Pick the right state tool (signals / services / NgRx variant / vue-query equivalent). - Use signals correctly (signal/computed/effect — when each). - Build a Pinia-style service-as-state singleton. - Set up NgRx Store + Effects + Selectors. - Use RxJS without leaking subscriptions (async pipe, takeUntilDestroyed, Subject patterns). - Bridge signals ↔ observables via toSignal / toObservable. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing state (see angular-routing). - Form state (see angular-forms). - Testing state (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Testing Angular 18-21: TestBed, component harnesses (@angular/cdk/testing), Karma+Jasmine (default historical) vs Jest (jest-preset-angular, modern), Angular Testing Library (RTL-style). HttpClient mocking via HttpTestingController. NgRx Effects testing. Cypress / Playwright e2e. Use this skill to: - Detect runner (Karma+Jasmine vs Jest) and configure correctly. - Write component tests with TestBed. - Use component harnesses for Material / custom UI components. - Mock HttpClient via provideHttpClientTesting + HttpTestingController. - Test signal-based inputs with componentRef.setInput(). - Test NgRx Effects with provideMockActions. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing patterns broadly (see angular-routing — covers testing routes briefly). - Form patterns broadly (see angular-forms).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Shared conventions for every SDLC development-phase architect agent: hard rules, code quality bar, workflow steps (superpowers invocation, spec reading, codebase exploration, verification), and the report/compact-summary contract. Architects load this skill first, then apply their stack-specific instructions on top.

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

AratKruglik のスキルをすべて見る

このスキルの問題を報告する