From 2255f9edcd16c5e94d46188ce087540b4cc0322f Mon Sep 17 00:00:00 2001 From: linuskmr Date: Sat, 25 Dec 2021 19:24:43 +0100 Subject: [PATCH] Add foreign keys and link for many-to-many relationship --- backend/src/models/links.py | 8 ++++++++ backend/src/models/project.py | 20 ++++++-------------- backend/src/models/record.py | 16 +++++++++++----- backend/src/models/tag.py | 11 ++++++----- backend/src/routes/records.py | 7 ++----- 5 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 backend/src/models/links.py diff --git a/backend/src/models/links.py b/backend/src/models/links.py new file mode 100644 index 0000000..1a652cd --- /dev/null +++ b/backend/src/models/links.py @@ -0,0 +1,8 @@ +from typing import Optional + +from sqlmodel import SQLModel, Field + + +class RecordTagLink(SQLModel, table=True): + record_id: Optional[int] = Field(default=None, foreign_key="record.id", primary_key=True) + tag_id: Optional[int] = Field(default=None, foreign_key="tag.id", primary_key=True) diff --git a/backend/src/models/project.py b/backend/src/models/project.py index ee972cc..6b835a4 100644 --- a/backend/src/models/project.py +++ b/backend/src/models/project.py @@ -1,13 +1,14 @@ from datetime import datetime, timedelta from typing import Optional -from sqlmodel import SQLModel, Field +from sqlmodel import SQLModel, Field, Relationship class ProjectBase(SQLModel): """Superclass model which all project classes have in common.""" - name: str # = Field(description="Name of the project", example="Learning") + name: str + creation_date: datetime = Field(default=datetime.now()) class ProjectCreate(ProjectBase): @@ -19,21 +20,12 @@ class Project(ProjectBase, table=True): """Model used inside the database.""" id: Optional[int] = Field(default=None, primary_key=True) - creation_date: datetime = Field(default=datetime.now()) + records: list["Record"] = Relationship(back_populates="project") class ProjectRead(ProjectBase): """Model used when querying information about a module.""" id: int - creation_date: datetime # = Field(description="Project creation date", example=datetime.now()) - # duration: timedelta # = Field(description="Total tracked duration", example=timedelta(days=3, hours=5, - # minutes=47)) - # records: int # = Field(description="Total number of trackings/records", example=42) - - # class Config: # TODO: Adding this config may be done with a decorator - # """pydantic config""" - # json_encoders = { - # # Serialize timedeltas as ISO 8601 and not as float seconds, which is the default - # timedelta: timedelta_isoformat, - # } + creation_date: datetime + records: list[int] diff --git a/backend/src/models/record.py b/backend/src/models/record.py index 6c55bc6..1315ebb 100644 --- a/backend/src/models/record.py +++ b/backend/src/models/record.py @@ -1,30 +1,36 @@ from datetime import datetime from typing import Optional -from sqlmodel import SQLModel, Field +from sqlmodel import SQLModel, Field, Relationship + +from src.models.links import RecordTagLink class RecordBase(SQLModel): """Superclass model that all record classes have in common.""" - project: int = Field(foreign_key="project.id") start: datetime = Field(default=datetime.now()) end: Optional[datetime] = Field(default=None) - tags: list[str] class RecordCreate(RecordBase): """Model used when a user creates a record.""" - pass + + project: int + tags: list[int] = Field(default=list()) class Record(RecordBase, table=True): """Model used inside the database.""" id: Optional[int] = Field(default=None, primary_key=True) + project: Optional["Project"] = Relationship(back_populates="records") + tags: list["Tag"] = Relationship(back_populates="records", link_model=RecordTagLink) class RecordRead(RecordBase): """Model used when a user queries a record.""" - pass + id: int + project: int + tags: list[int] diff --git a/backend/src/models/tag.py b/backend/src/models/tag.py index 4c993c9..2302bf5 100644 --- a/backend/src/models/tag.py +++ b/backend/src/models/tag.py @@ -1,13 +1,16 @@ from datetime import datetime, timedelta from typing import Optional -from sqlmodel import SQLModel, Field +from sqlmodel import SQLModel, Field, Relationship + +from src.models.links import RecordTagLink class TagBase(SQLModel): """Superclass model which all tag classes have in common.""" - name: str # = Field(description="Name of the tag", example="Listening Lectures") + name: str + records: list["Record"] = Relationship(back_populates="tags", link_model=RecordTagLink) class TagCreate(TagBase): @@ -24,6 +27,4 @@ class TagRead(TagBase): """Model used when querying information about a tag.""" id: int - name: str # = Field(description="Name of the tag", example="Listening Lectures") - # duration: timedelta # = Field(description="Total tracked duration", example=timedelta(days=3, hours=5, - # minutes=47)) + name: str diff --git a/backend/src/routes/records.py b/backend/src/routes/records.py index eb07855..8c86469 100644 --- a/backend/src/routes/records.py +++ b/backend/src/routes/records.py @@ -1,6 +1,3 @@ -import logging -from datetime import datetime, timedelta - from fastapi import APIRouter, status, Path, Depends, HTTPException from sqlmodel import select, Session @@ -14,14 +11,14 @@ router = APIRouter(prefix="/records", tags=["Records"]) async def all_records( *, session: Session = Depends(database.get_session) -) -> list[RecordRead]: +) -> list[Record]: """Returns a list of all records.""" records = session.exec(select(Record)).all() return records -@router.post("/start", status_code=status.HTTP_201_CREATED) +@router.post("/", status_code=status.HTTP_201_CREATED) async def add_record( *, record: RecordCreate,