From 5c977bdf2fad907b05160ac4d3b75bf2c928483b Mon Sep 17 00:00:00 2001 From: itskovacs Date: Sun, 2 Nov 2025 00:14:47 +0100 Subject: [PATCH] :sparkles: TripItem attachments --- backend/trip/models/models.py | 15 +++++++++++++++ backend/trip/routers/trips.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/backend/trip/models/models.py b/backend/trip/models/models.py index b6d28d3..6e1ff30 100644 --- a/backend/trip/models/models.py +++ b/backend/trip/models/models.py @@ -506,6 +506,11 @@ class TripDayRead(TripDayBase): ) +class TripItemAttachmentLink(SQLModel, table=True): + item_id: int = Field(foreign_key="tripitem.id", ondelete="CASCADE", primary_key=True, index=True) + attachment_id: int = Field(foreign_key="tripattachment.id", ondelete="CASCADE", primary_key=True) + + class TripItemBase(SQLModel): time: Annotated[ str, @@ -540,12 +545,17 @@ class TripItem(TripItemBase, table=True): paid_by: int | None = Field(default=None, foreign_key="user.username", ondelete="SET NULL") + attachments: list["TripAttachment"] = Relationship( + back_populates="items", link_model=TripItemAttachmentLink + ) + class TripItemCreate(TripItemBase): place: int | None = None status: TripItemStatusEnum | None = None image: str | None = None paid_by: str | None = None + attachment_ids: list[int] = [] class TripItemUpdate(TripItemBase): @@ -556,6 +566,7 @@ class TripItemUpdate(TripItemBase): status: TripItemStatusEnum | None = None image: str | None = None paid_by: str | None = None + attachment_ids: list[int] = [] class TripItemRead(TripItemBase): @@ -566,6 +577,7 @@ class TripItemRead(TripItemBase): image: str | None image_id: int | None paid_by: str | None + attachments: list["TripAttachmentRead"] @classmethod def serialize(cls, obj: TripItem) -> "TripItemRead": @@ -584,6 +596,7 @@ class TripItemRead(TripItemBase): image_id=obj.image_id, gpx=obj.gpx, paid_by=obj.paid_by, + attachments=[TripAttachmentRead.serialize(att) for att in obj.attachments], ) @@ -675,6 +688,8 @@ class TripAttachment(TripAttachmentBase, table=True): trip_id: int = Field(foreign_key="trip.id", ondelete="CASCADE", index=True) trip: Trip | None = Relationship(back_populates="attachments") + items: list["TripItem"] = Relationship(back_populates="attachments", link_model=TripItemAttachmentLink) + @event.listens_for(TripAttachment, "after_delete") def mark_attachment_for_deletion(mapper, connection, target: TripAttachment): diff --git a/backend/trip/routers/trips.py b/backend/trip/routers/trips.py index 961acf1..64ea514 100644 --- a/backend/trip/routers/trips.py +++ b/backend/trip/routers/trips.py @@ -404,6 +404,18 @@ def create_tripitem( new_item.paid_by = item.paid_by + if item.attachment_ids: + attachments = session.exec( + select(TripAttachment) + .where(TripAttachment.id.in_(item.attachment_ids)) + .where(TripAttachment.trip_id == trip_id) + ).all() + + if len(attachments) != len(item.attachment_ids): + raise HTTPException(status_code=400, detail="One or more attachments not found in trip") + + new_item.attachments = list(attachments) + session.add(new_item) session.commit() session.refresh(new_item) @@ -490,6 +502,22 @@ def update_tripitem( else: db_item.paid_by = None + attachment_ids = item_data.pop("attachment_ids", None) + if attachment_ids is not None: # Could be empty [], so 'in' + if attachment_ids: + attachments = session.exec( + select(TripAttachment) + .where(TripAttachment.id.in_(attachment_ids)) + .where(TripAttachment.trip_id == trip_id) + ).all() + + if len(attachments) != len(attachment_ids): + raise HTTPException(status_code=400, detail="One or more attachments not found in trip") + + db_item.attachments = list(attachments) + else: + db_item.attachments = [] + for key, value in item_data.items(): setattr(db_item, key, value)