feat: add support for media asset tags and conditional database handling
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 1s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 24s

- Introduced a function to check for the presence of the 'tags' column in the media_assets table, ensuring compatibility with database migrations.
- Updated media asset listing and patching functions to conditionally include tags based on the presence of the 'tags' column.
- Enhanced error handling for operations involving tags, providing clear feedback when the required database migration has not been performed.
- Improved search functionality to account for the presence of tags, optimizing queries based on available database schema.
This commit is contained in:
Lars 2026-05-07 16:20:51 +02:00
parent 1bc7ea95fb
commit 084ac38c2e

View File

@ -102,6 +102,27 @@ _MEDIA_KIND_FILTERS = frozenset({"all", "image", "video", "pdf", "other"})
_MAX_TAGS = 40
_MAX_TAG_LEN = 48
_MEDIA_ASSETS_TAGS_COLUMN: Optional[bool] = None
def _media_assets_tags_column_present(cur: Any) -> bool:
"""True nach Migration 046; verhindert 500 wenn Code neuer ist als das Schema."""
global _MEDIA_ASSETS_TAGS_COLUMN
if _MEDIA_ASSETS_TAGS_COLUMN is not None:
return _MEDIA_ASSETS_TAGS_COLUMN
cur.execute(
"""
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'media_assets'
AND column_name = 'tags'
LIMIT 1
"""
)
_MEDIA_ASSETS_TAGS_COLUMN = cur.fetchone() is not None
return _MEDIA_ASSETS_TAGS_COLUMN
def _normalize_media_tags(raw: Union[list[str], None]) -> list[str]:
"""PostgreSQL text[]; Einträge gekürzt und ohne Dubletten (case-insensitive)."""
@ -479,6 +500,9 @@ def list_media_assets(
if uploaded_by is not None and not show_uploader:
raise HTTPException(status_code=403, detail="Uploader-Filter nicht erlaubt")
has_tags_col = _media_assets_tags_column_present(cur)
tags_select = "ma.tags," if has_tags_col else "ARRAY[]::text[] AS tags,"
uploaded_sql = ""
uploaded_params: list[Any] = []
if uploaded_by is not None:
@ -489,12 +513,19 @@ def list_media_assets(
search_params: list[Any] = []
if needle:
like = f"%{needle}%"
search_params = [like, like, like, like]
search_sql = (
" AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s"
" OR COALESCE(ma.copyright_notice, '') ILIKE %s"
" OR EXISTS (SELECT 1 FROM unnest(ma.tags) AS t WHERE t::text ILIKE %s))"
)
if has_tags_col:
search_params = [like, like, like, like]
search_sql = (
" AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s"
" OR COALESCE(ma.copyright_notice, '') ILIKE %s"
" OR EXISTS (SELECT 1 FROM unnest(ma.tags) AS t WHERE t::text ILIKE %s))"
)
else:
search_params = [like, like, like]
search_sql = (
" AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s"
" OR COALESCE(ma.copyright_notice, '') ILIKE %s)"
)
params: list[Any] = (
[is_adm, profile_id, profile_id]
@ -507,7 +538,7 @@ def list_media_assets(
cur.execute(
f"""SELECT ma.id, ma.mime_type, ma.byte_size, ma.original_filename, ma.visibility, ma.club_id,
ma.uploaded_by_profile_id, ma.lifecycle_state, ma.created_at, ma.sha256,
ma.copyright_notice, ma.storage_key, ma.tags,
ma.copyright_notice, ma.storage_key, {tags_select}
pr.name AS uploader_name,
pr.email AS uploader_email,
cl.name AS club_name
@ -682,6 +713,15 @@ def bulk_media_patch(
asset = r2d(row)
assert_can_edit_media_asset_metadata(cur, tenant, asset)
if "tags" in patch_fields and not _media_assets_tags_column_present(cur):
failed.append(
{
"id": asset_id,
"detail": "Schlagwörter (tags) erfordern DB-Migration 046.",
}
)
continue
eff = _effective_media_patch_fields(patch_fields, asset)
next_vis = str(eff.get("visibility", asset["visibility"])).strip().lower()
next_cid = eff["club_id"] if "club_id" in eff else asset.get("club_id")
@ -753,6 +793,12 @@ def patch_media_asset(
asset = r2d(row)
assert_can_edit_media_asset_metadata(cur, tenant, asset)
if "tags" in data and not _media_assets_tags_column_present(cur):
raise HTTPException(
status_code=503,
detail="Schlagwörter (tags) erfordern die Datenbank-Migration 046. Bitte Migration ausführen.",
)
eff = _effective_media_patch_fields(data, asset)
next_vis = str(eff.get("visibility", asset["visibility"])).strip().lower()
next_cid = eff["club_id"] if "club_id" in eff else asset.get("club_id")
@ -789,11 +835,22 @@ def patch_media_asset(
tuple(vals),
)
conn.commit()
cur.execute(
"""SELECT id, mime_type, byte_size, original_filename, visibility, club_id,
uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice, tags
FROM media_assets WHERE id = %s""",
(asset_id,),
)
has_tags = _media_assets_tags_column_present(cur)
if has_tags:
cur.execute(
"""SELECT id, mime_type, byte_size, original_filename, visibility, club_id,
uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice, tags
FROM media_assets WHERE id = %s""",
(asset_id,),
)
else:
cur.execute(
"""SELECT id, mime_type, byte_size, original_filename, visibility, club_id,
uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice
FROM media_assets WHERE id = %s""",
(asset_id,),
)
out = r2d(cur.fetchone())
return out
if not has_tags:
out["tags"] = []
return out