fix(backend): Hersteller/Modell-Duplikatprüfung vorab statt über UNIQUE-Constraint
Testsuite-get_db-Override macht kein Rollback bei Exceptions (dokumentierter Fund 2026-09-05) - ein Flush-Fehlschlag über IntegrityError hinterlässt die geteilte Test-Session im PendingRollbackError-Zustand für jeden folgenden Request im selben Test. Projekt-Konvention laut geraet_instanz.py::erstelle ist deshalb explizites Vorab-Prüfen statt Verlassen auf den DB-Constraint - hier nachgezogen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_current_user, require_roles, require_roles_or_permission
|
||||
@@ -96,12 +95,17 @@ async def erstelle_hersteller(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> Hersteller:
|
||||
# Explizit vorab prüfen statt sich auf den UNIQUE-Constraint zu verlassen:
|
||||
# ein Flush-Fehlschlag hinterlässt die Session im Zustand DEACTIVE
|
||||
# (SQLAlchemy 2.0), was in einer über mehrere Requests geteilten Session
|
||||
# (z.B. Testsuite) jeden folgenden Request mit PendingRollbackError zum
|
||||
# Absturz bringt (gleicher Fund wie bei geraet_instanz.py::erstelle).
|
||||
bestehender = await db.execute(select(Hersteller).where(Hersteller.name == payload.name))
|
||||
if bestehender.scalar_one_or_none() is not None:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Hersteller existiert bereits")
|
||||
hersteller = Hersteller(**payload.model_dump())
|
||||
db.add(hersteller)
|
||||
try:
|
||||
await db.flush()
|
||||
except IntegrityError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Hersteller existiert bereits") from exc
|
||||
await db.flush()
|
||||
return hersteller
|
||||
|
||||
|
||||
@@ -126,12 +130,14 @@ async def erstelle_modell(
|
||||
) -> Modell:
|
||||
if await db.get(Hersteller, payload.hersteller_id) is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Hersteller nicht gefunden")
|
||||
bestehendes = await db.execute(
|
||||
select(Modell).where(Modell.hersteller_id == payload.hersteller_id, Modell.name == payload.name)
|
||||
)
|
||||
if bestehendes.scalar_one_or_none() is not None:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Modell existiert bereits")
|
||||
modell = Modell(**payload.model_dump())
|
||||
db.add(modell)
|
||||
try:
|
||||
await db.flush()
|
||||
except IntegrityError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Modell existiert bereits") from exc
|
||||
await db.flush()
|
||||
return modell
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user