From 83311a6079c5153ffb185002945ed22845c5ff20 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 8 May 2026 03:01:42 +0200 Subject: [PATCH] Fix circular reference recursion in JSON serialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dataclass models have parent back-references (TrackLayout→Circuit→ Locality→Country) causing infinite recursion in dataclasses.asdict(). Add to_dict() methods that exclude parent refs, use them in API endpoints. --- cdn-api.py | 10 +++++----- models/circuit.py | 8 ++++++++ models/country.py | 7 +++++++ models/locality.py | 7 +++++++ models/track_layout.py | 7 +++++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/cdn-api.py b/cdn-api.py index 3b8cf89..e1c0841 100644 --- a/cdn-api.py +++ b/cdn-api.py @@ -67,7 +67,7 @@ async def root(): async def get_countries(): """Get list of all available countries with their slugs""" return { - "countries": circuit_service.get_countries_list() + "countries": [c.to_dict() for c in circuit_service.get_countries_list()] } @app.get("/circuits/{country_slug}") @@ -76,7 +76,7 @@ async def get_cities(country_slug: str): cities = circuit_service.get_localities_list(country_slug) if not cities: raise HTTPException(status_code=404, detail="Country not found") - return {"cities": cities} + return {"cities": [c.to_dict() for c in cities]} @app.get("/circuits/{country_slug}/{city_slug}") async def get_circuits(country_slug: str, city_slug: str): @@ -84,7 +84,7 @@ async def get_circuits(country_slug: str, city_slug: str): circuits = circuit_service.get_circuits_list(country_slug, city_slug) if not circuits: raise HTTPException(status_code=404, detail="City not found") - return {"circuits": circuits} + return {"circuits": [c.to_dict() for c in circuits]} @app.get("/circuits/{country_slug}/{city_slug}/{circuit_slug}") async def get_circuit_details(country_slug: str, city_slug: str, circuit_slug: str): @@ -92,7 +92,7 @@ async def get_circuit_details(country_slug: str, city_slug: str, circuit_slug: s circuit_details = circuit_service.get_circuit_details(country_slug, city_slug, circuit_slug) if not circuit_details: raise HTTPException(status_code=404, detail="Circuit not found") - return circuit_details + return circuit_details.to_dict() @app.get("/circuits/{country_slug}/{city_slug}/{circuit_slug}/layout/{layout_slug}") async def get_layout_details(country_slug: str, city_slug: str, circuit_slug: str, layout_slug: str): @@ -100,7 +100,7 @@ async def get_layout_details(country_slug: str, city_slug: str, circuit_slug: st layout_details = circuit_service.get_layout_details(country_slug, city_slug, circuit_slug, layout_slug) if not layout_details: raise HTTPException(status_code=404, detail="Layout not found") - return layout_details + return layout_details.to_dict() @app.get("/tracks/{country_slug}/{city_slug}/{circuit_slug}/year/{year}/{image_format}") async def get_track_by_year( diff --git a/models/circuit.py b/models/circuit.py index 4d4d5b2..78a9aba 100644 --- a/models/circuit.py +++ b/models/circuit.py @@ -19,6 +19,14 @@ class Circuit: locality: 'Locality' layouts: dict[str, TrackLayout] + def to_dict(self) -> dict: + return { + "slug": self.slug, + "name": self.name, + "urls": {"wikipedia": self.urls.wikipedia, "fandom": self.urls.fandom}, + "layouts": {k: v.to_dict() for k, v in self.layouts.items()}, + } + @classmethod def from_dict(cls, locality: 'Locality', slug: str, data: dict) -> 'Circuit': circuit = cls( diff --git a/models/country.py b/models/country.py index 57b79d9..e51fb60 100644 --- a/models/country.py +++ b/models/country.py @@ -7,6 +7,13 @@ class Country: name: str localities: dict[str, Locality] + def to_dict(self) -> dict: + return { + "slug": self.slug, + "name": self.name, + "localities": {k: v.to_dict() for k, v in self.localities.items()}, + } + @classmethod def from_dict(cls, slug: str, data: dict): country = cls( diff --git a/models/locality.py b/models/locality.py index 90ac434..dde574d 100644 --- a/models/locality.py +++ b/models/locality.py @@ -12,6 +12,13 @@ class Locality: country: 'Country' circuits: dict[str, Circuit] + def to_dict(self) -> dict: + return { + "slug": self.slug, + "name": self.name, + "circuits": {k: v.to_dict() for k, v in self.circuits.items()}, + } + @classmethod def from_dict(cls, country: 'Country', slug: str, data: dict): locality = cls( diff --git a/models/track_layout.py b/models/track_layout.py index b4ca33f..62b706b 100644 --- a/models/track_layout.py +++ b/models/track_layout.py @@ -27,6 +27,13 @@ class TrackLayout: circuit=circuit ) + def to_dict(self) -> dict: + return { + "slug": self.slug, + "description": self.description, + "imageUrl": self.imageUrl, + } + @property def coordinates(self) -> list[tuple[float, float]]: return self._geo_data.features[0].geometry.coordinates