Skip to content

Commit

Permalink
feat: get requests and orm relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
lilpuzeen committed Mar 13, 2024
1 parent 251a229 commit fed92ce
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 8 deletions.
41 changes: 41 additions & 0 deletions src/actions/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from sqlalchemy.orm import selectinload

from src.database import Base, get_async_session
from src.auth.utils import map_to_datetime
Expand Down Expand Up @@ -47,6 +48,46 @@ async def get(self, id: int, db: AsyncSession = Depends(get_async_session)) -> O
else:
raise HTTPException(status_code=404, detail=f"Object with ID {id} not found.")

async def get_poll_with_questions(self, id: int, db: AsyncSession = Depends(get_async_session)) -> Optional[ModelType]:
async with db as session:
query = (
select(self.model)
.filter(self.model.id == id)
.options(
selectinload(models.Poll.questions)
.selectinload(models.Question.choices)
)
)
result = await session.execute(query)
obj = result.scalars().first()
if obj:
obj_data = jsonable_encoder(obj)

for question in obj_data["questions"]:
question.pop("poll_id", None)
return obj_data
else:
raise HTTPException(status_code=404, detail=f"Object with ID {id} not found.")

async def get_question_with_choices(self, id: int, db: AsyncSession = Depends(get_async_session)) -> Optional[ModelType]:
async with db as session:
query = (
select(self.model)
.filter(self.model.id == id)
.options(
selectinload(models.Question.choices)
)
)
result = await session.execute(query)
obj = result.scalars().first()
if obj:
obj_data = jsonable_encoder(obj)
question_data = obj_data
question_data.pop("poll_id", None)
return question_data
else:
raise HTTPException(status_code=404, detail=f"Object with ID {id} not found.")

async def create(self, *, obj_in: CreateSchemaType, db: AsyncSession = Depends(get_async_session)) -> ModelType:
obj_in_data = map_to_datetime(jsonable_encoder(obj_in))
db_obj = self.model(**obj_in_data)
Expand Down
2 changes: 1 addition & 1 deletion src/auth/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class CustomUserDatabase(SQLAlchemyUserDatabase):
"""
Класс для работы с базой данных пользователей, расширенный методом поиска по username.
Extend the SQLAlchemyUserDatabase to add custom methods.
"""

def __init__(self, session, user_table) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/auth/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self, user_db: BaseUserDatabase, *args, **kwargs):
super().__init__(user_db, *args, **kwargs)

async def on_after_register(self, user: User, request: Optional[Request] = None):
# TODO: add email verification
# TODO: add email confirmation
print(f"User {user.username} has registered.")

async def authenticate(
Expand Down
5 changes: 5 additions & 0 deletions src/auth/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTable
from sqlalchemy import (Boolean, Column, Integer,
String)
from sqlalchemy.orm import relationship

from src.database import Base


Expand All @@ -14,3 +16,6 @@ class User(SQLAlchemyBaseUserTable[int], Base):
is_active: bool = Column(Boolean, default=True, nullable=False)
is_superuser: bool = Column(Boolean, default=False, nullable=False)
is_verified: bool = Column(Boolean, default=False, nullable=False)

polls = relationship("Poll", back_populates="users")
votes = relationship("Vote", back_populates="user")
4 changes: 2 additions & 2 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@
app.include_router(router_votes)


if __name__ == '__main__':
uvicorn.run(app)
# if __name__ == '__main__':
# uvicorn.run(app)
16 changes: 15 additions & 1 deletion src/polls/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey
from sqlalchemy.orm import relationship

from src.database import Base

import datetime

# TODO: Response models

class Poll(Base):
__tablename__ = "poll"

Expand All @@ -14,6 +16,9 @@ class Poll(Base):
start_date = Column("start_date", TIMESTAMP, default=datetime.datetime.now())
end_date = Column("end_date", TIMESTAMP, nullable=False)

questions = relationship("Question", back_populates="poll")
users = relationship("User", back_populates="polls")


class Question(Base):
__tablename__ = "question"
Expand All @@ -22,6 +27,9 @@ class Question(Base):
poll_id = Column("poll_id", Integer, ForeignKey("poll.id"))
question_text = Column("question_text", String, nullable=False)

poll = relationship("Poll", back_populates="questions")
choices = relationship("Choice", back_populates="questions")


class Choice(Base):
__tablename__ = "choice"
Expand All @@ -30,6 +38,9 @@ class Choice(Base):
question_id = Column("question_id", Integer, ForeignKey("question.id"))
choice_text = Column("choice_text", String, nullable=False)

questions = relationship("Question", back_populates="choices")
votes = relationship("Vote", back_populates="choice")


class Vote(Base):
__tablename__ = "vote"
Expand All @@ -38,3 +49,6 @@ class Vote(Base):
choice_id = Column("choice_id", Integer, ForeignKey("choice.id"))
user_id = Column("user_id", Integer, ForeignKey("user.id"))
vote_timestamp = Column("vote_timestamp", TIMESTAMP)

choice = relationship("Choice", back_populates="votes")
user = relationship("User", back_populates="votes")
10 changes: 8 additions & 2 deletions src/polls/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ async def create_poll(
return await poll_action.create(db=session, obj_in=new_poll_instance)


@router_polls.get("/{poll_id}", response_model=schema.ReadPoll)
@router_polls.get("/{poll_id}")
async def get_poll(poll_id: int, session: AsyncSession = Depends(get_async_session)):
pass
return await poll_action.get_poll_with_questions(db=session, id=poll_id)


@router_questions.post("/{poll_id}")
Expand All @@ -79,6 +79,11 @@ async def create_question(
return await question_action.create(db=session, obj_in=new_question_instance)


@router_questions.get("/{question_id}")
async def get_question(question_id: int, session: AsyncSession = Depends(get_async_session)):
return await question_action.get_question_with_choices(db=session, id=question_id)


@router_choices.post("/{question_id}")
async def create_choice(
question_id: int,
Expand All @@ -93,6 +98,7 @@ async def create_choice(
return await choice_action.create(db=session, obj_in=new_choice_instance)


# TODO: handle multiple votes from the same user on the same choice
@router_votes.post("/{choice_id}")
async def create_vote(
choice_id: int,
Expand Down
3 changes: 2 additions & 1 deletion src/polls/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from pydantic import BaseModel, constr, FutureDatetime

# TODO: Response models


class CreatePoll(BaseModel):
id: int
Expand Down Expand Up @@ -38,4 +40,3 @@ class ReadPoll(BaseModel):
title: str
description: str
questions: list[ReadQuestion]

0 comments on commit fed92ce

Please sign in to comment.