-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- login both with username and email - add actions interface for all CRUD operations - create routers for all entities
- Loading branch information
Showing
9 changed files
with
352 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
from typing import Any, Generic, Optional, Type, TypeVar, cast | ||
|
||
from sqlalchemy.future import select | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
from fastapi import Depends, HTTPException | ||
|
||
from fastapi.encoders import jsonable_encoder | ||
from pydantic import BaseModel | ||
|
||
from src.database import Base, get_async_session | ||
from src.auth.utils import map_to_datetime | ||
|
||
import src.actions.schemas as schema | ||
import src.polls.models as models | ||
|
||
ModelType = TypeVar("ModelType", bound=Base) | ||
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) | ||
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) | ||
|
||
|
||
class BaseActions(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): | ||
def __init__(self, model: Type[ModelType]): | ||
""" | ||
Base class that can be extended by other action classes. | ||
Provides basic CRUD and listing operations. | ||
:param model: The SQLAlchemy model | ||
:type model: Type[ModelType] | ||
""" | ||
self.model = model | ||
|
||
async def get_all( | ||
self, *, skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_async_session) | ||
) -> list[ModelType]: | ||
async with db as session: | ||
query = select(self.model).offset(skip).limit(limit) | ||
result = await session.execute(query) | ||
return cast(list[ModelType], result.scalars().all()) | ||
|
||
async def get(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) | ||
result = await session.execute(query) | ||
obj = result.scalars().first() | ||
if obj: | ||
return obj | ||
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) | ||
db.add(db_obj) | ||
await db.commit() | ||
await db.refresh(db_obj) | ||
return db_obj | ||
|
||
async def update( | ||
self, | ||
*, | ||
db_obj: ModelType, | ||
obj_in: UpdateSchemaType | dict[str, Any], | ||
db: AsyncSession = Depends(get_async_session) | ||
) -> ModelType: | ||
obj_data = jsonable_encoder(db_obj) | ||
if isinstance(obj_in, dict): | ||
update_data = obj_in | ||
else: | ||
update_data = obj_in.dict(exclude_unset=True) | ||
|
||
for field in obj_data: | ||
if field in update_data: | ||
setattr(db_obj, field, update_data[field]) | ||
|
||
db.add(db_obj) | ||
await db.commit() | ||
await db.refresh(db_obj) | ||
return db_obj | ||
|
||
async def remove(self, *, id: int, db: AsyncSession = Depends(get_async_session)) -> ModelType: | ||
query = select(self.model).where(self.model.id == id) | ||
result = await db.execute(query) | ||
obj = result.scalars().first() | ||
if obj: | ||
await db.delete(obj) | ||
await db.commit() | ||
return obj | ||
else: | ||
raise HTTPException(status_code=404, detail=f"Object with ID {id} not found.") | ||
|
||
|
||
class PollActions(BaseActions[models.Poll, schema.PostCreate, schema.PostUpdate]): | ||
"""Poll actions with basic CRUD operations""" | ||
|
||
pass | ||
|
||
|
||
class QuestionActions(BaseActions[models.Question, schema.PostCreate, schema.PostUpdate]): | ||
"""Question actions with basic CRUD operations""" | ||
|
||
pass | ||
|
||
|
||
class ChoiceActions(BaseActions[models.Choice, schema.PostCreate, schema.PostUpdate]): | ||
"""Choice actions with basic CRUD operations""" | ||
|
||
pass | ||
|
||
|
||
class VoteActions(BaseActions[models.Vote, schema.PostCreate, schema.PostUpdate]): | ||
"""Vote actions with basic CRUD operations""" | ||
|
||
pass | ||
|
||
|
||
poll_action = PollActions(models.Poll) | ||
question_action = QuestionActions(models.Question) | ||
choice_action = QuestionActions(models.Choice) | ||
vote_action = QuestionActions(models.Vote) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Optional | ||
|
||
from pydantic import BaseModel | ||
# TODO: correct schemas for each entity | ||
|
||
|
||
class HTTPError(BaseModel): | ||
detail: str | ||
|
||
|
||
class PostBase(BaseModel): | ||
title: Optional[str] = None | ||
body: Optional[str] = None | ||
|
||
|
||
class PostCreate(PostBase): | ||
title: str | ||
body: str | ||
|
||
|
||
class PostUpdate(PostBase): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from typing import Optional | ||
from fastapi_users_db_sqlalchemy import SQLAlchemyUserDatabase | ||
from sqlalchemy import select | ||
|
||
from src.auth.schemas import UserRead | ||
|
||
|
||
class CustomUserDatabase(SQLAlchemyUserDatabase): | ||
""" | ||
Класс для работы с базой данных пользователей, расширенный методом поиска по username. | ||
""" | ||
|
||
def __init__(self, session, user_table) -> None: | ||
super().__init__(session, user_table) | ||
|
||
async def get_by_username(self, username: str) -> Optional[UserRead]: | ||
query = select(self.user_table).where(self.user_table.username == username) | ||
result = await self.session.execute(query) | ||
user = result.scalars().first() | ||
return user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,27 @@ | ||
import datetime | ||
from typing import Any | ||
|
||
from fastapi import Depends | ||
from fastapi_users_db_sqlalchemy import SQLAlchemyUserDatabase | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
|
||
from src.auth.models import User | ||
from src.database import get_async_session | ||
|
||
from src.auth.db import CustomUserDatabase | ||
|
||
|
||
async def get_user_db(session: AsyncSession = Depends(get_async_session)): | ||
yield SQLAlchemyUserDatabase(session, User) | ||
yield CustomUserDatabase(session, User) | ||
|
||
|
||
def map_to_datetime(obj: dict[str, Any]) -> dict[str, Any]: | ||
def str_to_datetime(ts: str) -> datetime.datetime: | ||
return datetime.datetime.fromisoformat(ts) | ||
|
||
for k, v in obj.items(): | ||
if "date" in k: | ||
obj[k] = str_to_datetime(v) | ||
break | ||
|
||
return obj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.