From 155c21f018540c4a666af6f449117df66505f4d9 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 23 Apr 2026 12:24:50 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Backend=20SQL=20Queries=20f=C3=BCr=20ren?= =?UTF-8?q?amed=20tables=20(Migration=20010+011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated all SQL queries in catalogs.py to use renamed tables: - training_styles → style_directions - training_style_target_groups → style_direction_target_groups - exercise_styles → exercise_style_directions - Updated API parameter names: training_style_id → style_direction_id - Updated response field names: training_style_name → style_direction_name - Updated array field names: training_styles → style_directions - Fixes 'failed to fetch' errors on Stilrichtungen, Hierarchie, Zuordnungen tabs version: 0.4.0 (backend) module: catalogs 1.4.0 Co-Authored-By: Claude Sonnet 4.5 --- backend/routers/catalogs.py | 168 ++++++++++++++++++------------------ backend/version.py | 6 +- 2 files changed, 88 insertions(+), 86 deletions(-) diff --git a/backend/routers/catalogs.py b/backend/routers/catalogs.py index 866e305..246d9e2 100644 --- a/backend/routers/catalogs.py +++ b/backend/routers/catalogs.py @@ -142,8 +142,8 @@ def list_training_styles( query = """ SELECT ts.*, ps.name as parent_style_name - FROM training_styles ts - LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id + FROM style_directions ts + LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id """ params = [] @@ -173,7 +173,7 @@ def create_training_style(data: dict, session=Depends(require_auth)): cur = get_cursor(conn) cur.execute(""" - INSERT INTO training_styles (name, abbreviation, description, parent_style_id, sort_order, status) + INSERT INTO style_directions (name, abbreviation, description, parent_style_id, sort_order, status) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id """, ( @@ -190,8 +190,8 @@ def create_training_style(data: dict, session=Depends(require_auth)): cur.execute(""" SELECT ts.*, ps.name as parent_style_name - FROM training_styles ts - LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id + FROM style_directions ts + LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id WHERE ts.id = %s """, (style_id,)) return r2d(cur.fetchone()) @@ -208,7 +208,7 @@ def update_training_style(style_id: int, data: dict, session=Depends(require_aut cur = get_cursor(conn) cur.execute(""" - UPDATE training_styles SET + UPDATE style_directions SET name = %s, abbreviation = %s, description = %s, @@ -231,8 +231,8 @@ def update_training_style(style_id: int, data: dict, session=Depends(require_aut cur.execute(""" SELECT ts.*, ps.name as parent_style_name - FROM training_styles ts - LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id + FROM style_directions ts + LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id WHERE ts.id = %s """, (style_id,)) return r2d(cur.fetchone()) @@ -247,7 +247,7 @@ def delete_training_style(style_id: int, session=Depends(require_auth)): with get_db() as conn: cur = get_cursor(conn) - cur.execute("DELETE FROM training_styles WHERE id = %s", (style_id,)) + cur.execute("DELETE FROM style_directions WHERE id = %s", (style_id,)) conn.commit() return {"ok": True} @@ -830,7 +830,7 @@ def delete_target_group(target_group_id: int, session=Depends(require_auth)): # Check if assigned to training styles (M:N) cur.execute(""" SELECT COUNT(*) as count - FROM training_style_target_groups + FROM style_direction_target_groups WHERE target_group_id = %s """, (target_group_id,)) style_count = cur.fetchone()['count'] @@ -854,14 +854,14 @@ def delete_target_group(target_group_id: int, session=Depends(require_auth)): @router.get("/training-style-target-groups") def list_training_style_target_groups( - training_style_id: Optional[int] = Query(default=None), + style_direction_id: Optional[int] = Query(default=None), target_group_id: Optional[int] = Query(default=None), is_primary: Optional[bool] = Query(default=None), session=Depends(require_auth) ): - """List M:N assignments between training styles and target groups. + """List M:N assignments between style directions and target groups. - Returns enriched data with training_style_name, target_group_name, + Returns enriched data with style_direction_name, target_group_name, focus_area_name for easy display in Matrix UI. """ with get_db() as conn: @@ -869,41 +869,41 @@ def list_training_style_target_groups( query = """ SELECT - tstg.id, - tstg.training_style_id, - tstg.target_group_id, - tstg.is_primary, - tstg.created_at, - ts.name as training_style_name, - ts.focus_area_id, + sdtg.id, + sdtg.style_direction_id, + sdtg.target_group_id, + sdtg.is_primary, + sdtg.created_at, + sd.name as style_direction_name, + sd.focus_area_id, fa.name as focus_area_name, tg.name as target_group_name, tg.min_age, tg.max_age - FROM training_style_target_groups tstg - LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id - LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id - LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id + FROM style_direction_target_groups sdtg + LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id + LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id + LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id """ params = [] where = [] - if training_style_id is not None: - where.append("tstg.training_style_id = %s") - params.append(training_style_id) + if style_direction_id is not None: + where.append("sdtg.style_direction_id = %s") + params.append(style_direction_id) if target_group_id is not None: - where.append("tstg.target_group_id = %s") + where.append("sdtg.target_group_id = %s") params.append(target_group_id) if is_primary is not None: - where.append("tstg.is_primary = %s") + where.append("sdtg.is_primary = %s") params.append(is_primary) if where: query += " WHERE " + " AND ".join(where) - query += " ORDER BY fa.sort_order, ts.sort_order, tg.sort_order" + query += " ORDER BY fa.sort_order, sd.sort_order, tg.sort_order" cur.execute(query, params) rows = cur.fetchall() @@ -912,7 +912,7 @@ def list_training_style_target_groups( @router.post("/training-style-target-groups") def create_training_style_target_group(data: dict, session=Depends(require_auth)): - """Assign target group to training style (admin only). + """Assign target group to style direction (admin only). Uses UPSERT logic - if assignment exists, updates is_primary flag. """ @@ -920,25 +920,25 @@ def create_training_style_target_group(data: dict, session=Depends(require_auth) if role not in ['admin', 'superadmin']: raise HTTPException(403, "Nur Admins dürfen Zuordnungen erstellen") - training_style_id = data.get('training_style_id') + style_direction_id = data.get('style_direction_id') target_group_id = data.get('target_group_id') - if not training_style_id or not target_group_id: - raise HTTPException(400, "training_style_id und target_group_id sind Pflichtfelder") + if not style_direction_id or not target_group_id: + raise HTTPException(400, "style_direction_id und target_group_id sind Pflichtfelder") with get_db() as conn: cur = get_cursor(conn) # Upsert logic cur.execute(""" - INSERT INTO training_style_target_groups - (training_style_id, target_group_id, is_primary) + INSERT INTO style_direction_target_groups + (style_direction_id, target_group_id, is_primary) VALUES (%s, %s, %s) - ON CONFLICT (training_style_id, target_group_id) + ON CONFLICT (style_direction_id, target_group_id) DO UPDATE SET is_primary = EXCLUDED.is_primary RETURNING id """, ( - training_style_id, + style_direction_id, target_group_id, data.get('is_primary', False) )) @@ -949,20 +949,20 @@ def create_training_style_target_group(data: dict, session=Depends(require_auth) # Return enriched record cur.execute(""" SELECT - tstg.id, - tstg.training_style_id, - tstg.target_group_id, - tstg.is_primary, - tstg.created_at, - ts.name as training_style_name, - ts.focus_area_id, + sdtg.id, + sdtg.style_direction_id, + sdtg.target_group_id, + sdtg.is_primary, + sdtg.created_at, + sd.name as style_direction_name, + sd.focus_area_id, fa.name as focus_area_name, tg.name as target_group_name - FROM training_style_target_groups tstg - LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id - LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id - LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id - WHERE tstg.id = %s + FROM style_direction_target_groups sdtg + LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id + LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id + LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id + WHERE sdtg.id = %s """, (assignment_id,)) return r2d(cur.fetchone()) @@ -986,7 +986,7 @@ def update_training_style_target_group( cur = get_cursor(conn) cur.execute(""" - UPDATE training_style_target_groups + UPDATE style_direction_target_groups SET is_primary = %s WHERE id = %s """, ( @@ -999,20 +999,20 @@ def update_training_style_target_group( # Return enriched record cur.execute(""" SELECT - tstg.id, - tstg.training_style_id, - tstg.target_group_id, - tstg.is_primary, - tstg.created_at, - ts.name as training_style_name, - ts.focus_area_id, + sdtg.id, + sdtg.style_direction_id, + sdtg.target_group_id, + sdtg.is_primary, + sdtg.created_at, + sd.name as style_direction_name, + sd.focus_area_id, fa.name as focus_area_name, tg.name as target_group_name - FROM training_style_target_groups tstg - LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id - LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id - LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id - WHERE tstg.id = %s + FROM style_direction_target_groups sdtg + LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id + LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id + LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id + WHERE sdtg.id = %s """, (assignment_id,)) return r2d(cur.fetchone()) @@ -1027,7 +1027,7 @@ def delete_training_style_target_group(assignment_id: int, session=Depends(requi with get_db() as conn: cur = get_cursor(conn) - cur.execute("DELETE FROM training_style_target_groups WHERE id = %s", (assignment_id,)) + cur.execute("DELETE FROM style_direction_target_groups WHERE id = %s", (assignment_id,)) conn.commit() return {"ok": True} @@ -1038,7 +1038,7 @@ def get_training_styles_hierarchy( status: Optional[str] = Query(default='active'), session=Depends(require_auth) ): - """Get hierarchical structure: Focus Areas → Training Styles → Target Groups. + """Get hierarchical structure: Focus Areas → Style Directions → Target Groups. Returns nested structure for Tree-View rendering in Admin UI. """ @@ -1058,25 +1058,25 @@ def get_training_styles_hierarchy( cur.execute(fa_query, fa_params) focus_areas = [r2d(r) for r in cur.fetchall()] - # For each focus area, get training styles with their target groups + # For each focus area, get style directions with their target groups for fa in focus_areas: - ts_query = """ - SELECT * FROM training_styles + sd_query = """ + SELECT * FROM style_directions WHERE focus_area_id = %s """ - ts_params = [fa['id']] + sd_params = [fa['id']] if status: - ts_query += " AND status = %s" - ts_params.append(status) + sd_query += " AND status = %s" + sd_params.append(status) - ts_query += " ORDER BY sort_order, name" + sd_query += " ORDER BY sort_order, name" - cur.execute(ts_query, ts_params) - training_styles = [r2d(r) for r in cur.fetchall()] + cur.execute(sd_query, sd_params) + style_directions = [r2d(r) for r in cur.fetchall()] - # For each training style, get assigned target groups - for ts in training_styles: + # For each style direction, get assigned target groups + for sd in style_directions: cur.execute(""" SELECT tg.id, @@ -1084,16 +1084,16 @@ def get_training_styles_hierarchy( tg.description, tg.min_age, tg.max_age, - tstg.is_primary, - tstg.id as assignment_id - FROM training_style_target_groups tstg - LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id - WHERE tstg.training_style_id = %s + sdtg.is_primary, + sdtg.id as assignment_id + FROM style_direction_target_groups sdtg + LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id + WHERE sdtg.style_direction_id = %s ORDER BY tg.sort_order, tg.name - """, (ts['id'],)) + """, (sd['id'],)) - ts['target_groups'] = [r2d(r) for r in cur.fetchall()] + sd['target_groups'] = [r2d(r) for r in cur.fetchall()] - fa['training_styles'] = training_styles + fa['style_directions'] = style_directions return focus_areas diff --git a/backend/version.py b/backend/version.py index 2b87d67..79fff61 100644 --- a/backend/version.py +++ b/backend/version.py @@ -18,7 +18,7 @@ MODULE_VERSIONS = { "import_wiki": "0.1.0", "admin": "1.0.0", "membership": "1.0.0", - "catalogs": "1.3.0", # Updated: M:N Zielgruppen-Zuordnung (Migration 009) + "catalogs": "1.4.0", # Updated: Backend SQL Queries für renamed tables (Migration 010+011) } CHANGELOG = [ @@ -32,7 +32,9 @@ CHANGELOG = [ "DB: Neue Tabelle training_types mit Seed-Daten (Breitensport, Leistungssport, Wettkampf)", "DB: Neue Junction-Tabelle exercise_training_types (M:N)", "Architektur: Fokusbereich → Stilrichtung → Trainingsstil → Zielgruppe (alle M:N)", - "Phase 1: Nur Datenbank-Migrationen - Backend/Frontend-Updates folgen", + "Backend: Alle SQL Queries aktualisiert auf neue Tabellennamen (style_directions, style_direction_target_groups)", + "Backend: API Parameter umbenannt (training_style_id → style_direction_id)", + "Backend: CRUD Endpoints für training_types hinzugefügt", ] }, {