From 631733f301cf9a9c903988e508edadc1355e5208 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 10:19:35 +0300 Subject: [PATCH 01/13] exclude unset filed --- backend/tests/test_schemas/test_movie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/tests/test_schemas/test_movie.py b/backend/tests/test_schemas/test_movie.py index 87ce9f3..7dddd6c 100644 --- a/backend/tests/test_schemas/test_movie.py +++ b/backend/tests/test_schemas/test_movie.py @@ -81,7 +81,7 @@ def test_movie_update_from_update_schema() -> None: description="Test Movie Update Description", watch_link="https://abc.example.com", ) - for field, value in movie_update: + for field, value in movie_update.model_dump(exclude_unset=True).items(): setattr(movie, field, value) assert movie_update.title == movie.title From 038829b2b606b1f15c2d17db91ef4af88672999e Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 10:19:56 +0300 Subject: [PATCH 02/13] dispose session after all --- backend/tests/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index c246f97..d199685 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -32,6 +32,8 @@ async def init_db(): await connection.run_sync(Base.metadata.create_all) yield + await engine_test.dispose() + if os.path.exists(DB_PATH): os.remove(DB_PATH) From 5311bd067b4d5583a22323285e1b7edbe7a975cf Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:05:16 +0300 Subject: [PATCH 03/13] change UserRead schema --- backend/backlog_app/schemas/helper.py | 3 +++ backend/backlog_app/schemas/user.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 backend/backlog_app/schemas/helper.py diff --git a/backend/backlog_app/schemas/helper.py b/backend/backlog_app/schemas/helper.py new file mode 100644 index 0000000..2ea3225 --- /dev/null +++ b/backend/backlog_app/schemas/helper.py @@ -0,0 +1,3 @@ +def to_camel_case(snake_str: str) -> str: + parts = snake_str.split('_') + return parts[0] + ''.join(word.capitalize() for word in parts[1:]) diff --git a/backend/backlog_app/schemas/user.py b/backend/backlog_app/schemas/user.py index 43165e0..17a4318 100644 --- a/backend/backlog_app/schemas/user.py +++ b/backend/backlog_app/schemas/user.py @@ -1,12 +1,25 @@ -import uuid from typing import Annotated +from uuid import UUID from annotated_types import Len from fastapi_users import schemas +from pydantic import BaseModel, EmailStr, ConfigDict +from .helper import to_camel_case -class UserRead(schemas.BaseUser[uuid.UUID]): - pass + +class UserRead(BaseModel): + id: UUID + email: EmailStr + + @property + def username(self): + return self.email.split('@')[0] + + model_config = ConfigDict( + from_attributes=True, + alias_generator=to_camel_case + ) class UserCreate(schemas.BaseUserCreate): From ecb83748a6ae490fb8689896cd8d0816289c4f94 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:05:37 +0300 Subject: [PATCH 04/13] use new alias generator for MovieBase --- backend/backlog_app/schemas/movie.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/backlog_app/schemas/movie.py b/backend/backlog_app/schemas/movie.py index e9ee8ec..8eef52a 100644 --- a/backend/backlog_app/schemas/movie.py +++ b/backend/backlog_app/schemas/movie.py @@ -2,8 +2,9 @@ from typing import Annotated from annotated_types import Len -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, ConfigDict +from .helper import to_camel_case from .user import UserRead @@ -16,7 +17,11 @@ class MovieBase(BaseModel): kp_id: int | None = None imdb_id: int | None = None - model_config = {"from_attributes": True} + model_config = ConfigDict( + from_attributes=True, + alias_generator=to_camel_case, + validate_by_name=True, + ) class MovieCreate(MovieBase): @@ -37,9 +42,12 @@ class MovieUpdate(MovieBase): class MovieRead(MovieBase): id: int - user: str + user: UserRead description: Annotated[str, Len(min_length=20, max_length=1000)] | None year: int | None watched: bool rating: float | None created_at: datetime + +class MovieList(BaseModel): + movies: list[MovieRead] From f46e7957d5440d6def95f69c77e9eb8fc70fa057 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:05:53 +0300 Subject: [PATCH 05/13] change lazy load for user --- backend/backlog_app/models/movie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/backlog_app/models/movie.py b/backend/backlog_app/models/movie.py index 6cce5b5..d3b7222 100644 --- a/backend/backlog_app/models/movie.py +++ b/backend/backlog_app/models/movie.py @@ -74,4 +74,4 @@ class Movie(Base): user_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("user.id"), nullable=False ) - user: Mapped["User"] = relationship("User", back_populates="movies") + user: Mapped["User"] = relationship("User", back_populates="movies", lazy="joined") From 2f50941040493e6dc064e0131f7e4c99d95986b1 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:06:17 +0300 Subject: [PATCH 06/13] refactor returning --- backend/backlog_app/api/crud.py | 74 +++++++-------------------------- 1 file changed, 14 insertions(+), 60 deletions(-) diff --git a/backend/backlog_app/api/crud.py b/backend/backlog_app/api/crud.py index 3e5c776..1c59303 100644 --- a/backend/backlog_app/api/crud.py +++ b/backend/backlog_app/api/crud.py @@ -1,4 +1,5 @@ import logging +from uuid import UUID from fastapi import HTTPException from sqlalchemy import or_ @@ -8,7 +9,7 @@ from backlog_app.models import User from backlog_app.models.movie import Movie -from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate +from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate, MovieList logger = logging.getLogger(__name__) @@ -16,29 +17,17 @@ async def create_movie( db: AsyncSession, movie_in: MovieCreate, user: User ) -> MovieRead: - movie = Movie(**movie_in.model_dump(), user=user) + movie = Movie(**movie_in.model_dump(), user_id=user.id) db.add(movie) await db.commit() await db.refresh(movie) logger.info("Movie <%s> has been created.", movie.id) - return MovieRead( - id=movie.id, - title=movie.title, - description=movie.description, - year=movie.year, - rating=movie.rating, - kp_id=movie.kp_id, - imdb_id=movie.imdb_id, - watch_link=movie.watch_link, - watched=movie.watched, - created_at=movie.created_at, - user=user.email.split("@")[0], - ) + return MovieRead.model_validate(movie) -async def get_movies(db: AsyncSession, user_id: str | None = None): +async def get_movies(db: AsyncSession, user_id: str | None = None) -> MovieList: query = select(Movie).options(selectinload(Movie.user)) if user_id: @@ -49,26 +38,13 @@ async def get_movies(db: AsyncSession, user_id: str | None = None): result = await db.execute(query) movies = result.scalars().all() - return [ - MovieRead( - id=m.id, - title=m.title, - description=m.description, - year=m.year, - rating=m.rating, - imdb_id=m.imdb_id, - watch_link=m.watch_link, - watched=m.watched, - created_at=m.created_at, - kp_id=m.kp_id, - user=m.user.email.split("@")[0], - ) - for m in movies - ] + logger.debug("Size of movies list: %s", len(movies)) + + return MovieList.model_validate({"movies": movies}) async def get_movie_by_id( - db: AsyncSession, movie_id: int, user_id: int | None = None + db: AsyncSession, movie_id: int, user_id: UUID | None = None ) -> MovieRead | None: query = select(Movie).options(selectinload(Movie.user)).where(Movie.id == movie_id) @@ -83,19 +59,7 @@ async def get_movie_by_id( logger.info("Movie has been found.") - return MovieRead( - id=movie.id, - title=movie.title, - description=movie.description, - year=movie.year, - rating=movie.rating, - imdb_id=movie.imdb_id, - watch_link=movie.watch_link, - watched=movie.watched, - created_at=movie.created_at, - kp_id=movie.kp_id, - user=movie.user.email.split("@")[0], - ) + return MovieRead.model_validate(movie) async def update_movie( @@ -122,19 +86,7 @@ async def update_movie( logger.info("Movie has been updated.") - return MovieRead( - id=movie.id, - title=movie.title, - description=movie.description, - year=movie.year, - rating=movie.rating, - kp_id=movie.kp_id, - imdb_id=movie.imdb_id, - watch_link=movie.watch_link, - watched=movie.watched, - user=movie.user.email.split("@")[0], - created_at=movie.created_at.isoformat(), - ) + return MovieRead.model_validate(movie) async def partial_update_movie( @@ -157,7 +109,7 @@ async def delete_movie( db: AsyncSession, movie_id: int, user: User, -) -> None: +) -> MovieRead: result = await db.execute(select(Movie).where(Movie.id == movie_id)) movie = result.scalars().first() if not movie: @@ -170,6 +122,8 @@ async def delete_movie( logger.info("Movie <%s> has been deleted.", movie_id) + return MovieRead.model_validate(movie) + def check_movie_ownership(movie: Movie, user: User) -> None: """Проверяет, может ли пользователь изменять фильм""" From 3ea5cb59845ae63b7e6953c2110afe5730027e87 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:06:31 +0300 Subject: [PATCH 07/13] change response model --- backend/backlog_app/api/view/movie_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/backlog_app/api/view/movie_view.py b/backend/backlog_app/api/view/movie_view.py index 8a8cc2d..ba42f85 100644 --- a/backend/backlog_app/api/view/movie_view.py +++ b/backend/backlog_app/api/view/movie_view.py @@ -8,7 +8,7 @@ current_active_user, ) from backlog_app.models.users import User -from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate +from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate, MovieList from backlog_app.storages.database import get_async_session router = APIRouter(prefix="/movies", tags=["Movies"]) @@ -23,7 +23,7 @@ async def add_movie( return await crud.create_movie(db, movie_create, user=user) -@router.get("/", response_model=List[MovieRead]) +@router.get("/", response_model=MovieList) async def get_movie_list( db: Annotated[AsyncSession, Depends(get_async_session)], user: Annotated[User, Depends(current_active_user)], From cc2479bf18ac20c6e46b541c41a960ea2447ff16 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:15:59 +0300 Subject: [PATCH 08/13] edit test data after changes --- backend/tests/test_schemas/test_movie.py | 22 ++++++++++++++++++---- backend/tests/test_schemas/test_users.py | 3 --- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/backend/tests/test_schemas/test_movie.py b/backend/tests/test_schemas/test_movie.py index 7dddd6c..83ddf6f 100644 --- a/backend/tests/test_schemas/test_movie.py +++ b/backend/tests/test_schemas/test_movie.py @@ -1,9 +1,10 @@ from datetime import datetime - +from uuid import uuid4 import pytest from pydantic import ValidationError from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate +from backlog_app.schemas.user import UserRead def test_movie_can_be_create_from_create_schema() -> None: @@ -13,12 +14,16 @@ def test_movie_can_be_create_from_create_schema() -> None: watch_link="https://example.com", rating=6.7, ) + user = UserRead( + id=uuid4(), + email="test@example.com" + ) movie = MovieRead( **movie_in.model_dump(), id=0, watched=False, created_at=datetime.now(), - user="test", + user=user, ) assert movie_in.title == movie.title @@ -50,12 +55,16 @@ def test_movie_create_max_value( title=title, description=description, ) + user = UserRead( + id=uuid4(), + email="test@example.com" + ) movie = MovieRead( **movie_in.model_dump(), id=0, watched=False, created_at=datetime.now(), - user="test", + user=user, ) assert movie.title == title @@ -63,9 +72,14 @@ def test_movie_create_max_value( def test_movie_update_from_update_schema() -> None: + user = UserRead( + id=uuid4(), + email="test@example.com" + ) + movie = MovieRead( id=0, - user="test", + user=user, title="Test Movie", description="Test Movie Description", watch_link="https://example.com", diff --git a/backend/tests/test_schemas/test_users.py b/backend/tests/test_schemas/test_users.py index f226ae9..017438c 100644 --- a/backend/tests/test_schemas/test_users.py +++ b/backend/tests/test_schemas/test_users.py @@ -45,6 +45,3 @@ def test_user_read(): user = UserRead(**data) assert user.id == user_id assert user.email == data["email"] - assert user.is_active is True - assert user.is_superuser is False - assert user.is_verified is True From f9b69d627360b84311ce6e61ade2961eca0002b3 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Wed, 11 Feb 2026 15:19:05 +0300 Subject: [PATCH 09/13] add decorator for adding username to UserRead schema --- backend/backlog_app/schemas/user.py | 5 +++-- backend/tests/test_schemas/test_users.py | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/backlog_app/schemas/user.py b/backend/backlog_app/schemas/user.py index 17a4318..b0253ac 100644 --- a/backend/backlog_app/schemas/user.py +++ b/backend/backlog_app/schemas/user.py @@ -3,7 +3,7 @@ from annotated_types import Len from fastapi_users import schemas -from pydantic import BaseModel, EmailStr, ConfigDict +from pydantic import BaseModel, EmailStr, ConfigDict, computed_field from .helper import to_camel_case @@ -12,8 +12,9 @@ class UserRead(BaseModel): id: UUID email: EmailStr + @computed_field @property - def username(self): + def username(self) -> str: return self.email.split('@')[0] model_config = ConfigDict( diff --git a/backend/tests/test_schemas/test_users.py b/backend/tests/test_schemas/test_users.py index 017438c..687a24c 100644 --- a/backend/tests/test_schemas/test_users.py +++ b/backend/tests/test_schemas/test_users.py @@ -38,10 +38,8 @@ def test_user_read(): data = { "id": user_id, "email": "read@example.com", - "is_active": True, - "is_superuser": False, - "is_verified": True, } user = UserRead(**data) assert user.id == user_id assert user.email == data["email"] + assert user.username == "read" From b238fbbfbfe69d4e6811ec13f1ea2c883f8251fc Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Tue, 17 Feb 2026 15:40:30 +0300 Subject: [PATCH 10/13] move published field to base movie schema --- backend/backlog_app/schemas/movie.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/backlog_app/schemas/movie.py b/backend/backlog_app/schemas/movie.py index 8eef52a..19908d2 100644 --- a/backend/backlog_app/schemas/movie.py +++ b/backend/backlog_app/schemas/movie.py @@ -16,6 +16,7 @@ class MovieBase(BaseModel): watch_link: str | None = None kp_id: int | None = None imdb_id: int | None = None + published: bool = False model_config = ConfigDict( from_attributes=True, @@ -28,7 +29,6 @@ class MovieCreate(MovieBase): description: Annotated[str, Len(min_length=20, max_length=1000)] | None = None year: int | None = None rating: float | None = Field(default=None, ge=1.0, le=10.0) - published: bool = False class MovieUpdate(MovieBase): @@ -37,7 +37,6 @@ class MovieUpdate(MovieBase): year: int | None = None watched: bool | None = None rating: float | None = Field(default=None, ge=1.0, le=10.0) - published: bool = False class MovieRead(MovieBase): From b7582f9127f1a4702092c9fe3d4074b871e14364 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Tue, 17 Feb 2026 15:41:09 +0300 Subject: [PATCH 11/13] editing components after adding an alias --- frontend/src/components/MovieForm.vue | 80 +++++++++++++-------------- frontend/src/components/MovieItem.vue | 16 +++--- frontend/src/components/MovieList.vue | 2 +- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/frontend/src/components/MovieForm.vue b/frontend/src/components/MovieForm.vue index bdfc523..e077841 100644 --- a/frontend/src/components/MovieForm.vue +++ b/frontend/src/components/MovieForm.vue @@ -79,14 +79,14 @@ Watch Link (optional) -

{{ errors.watch_link }}

+

{{ errors.watchLink }}

@@ -95,15 +95,15 @@ Kinopoisk ID (optional) -

{{ errors.kp_id }}

+

{{ errors.kpId }}

@@ -112,15 +112,15 @@ IMDB ID (optional) -

{{ errors.imdb_id }}

+

{{ errors.imdbId }}

@@ -227,9 +227,9 @@ export default { description: '', year: null, rating: null, - watch_link: '', - kp_id: null, - imdb_id: null, + watchLink: '', + kpId: null, + imdbId: null, watched: false, published: false, editingId: null, @@ -258,9 +258,9 @@ export default { description: '', year: '', rating: '', - watch_link: '', - kp_id: '', - imdb_id: '', + watchLink: '', + kpId: '', + imdbId: '', } let isValid = true @@ -291,24 +291,24 @@ export default { } // URL validation (optional, but if provided must be valid) - if (this.watch_link && this.watch_link.trim() !== '') { + if (this.watchLink && this.watchLink.trim() !== '') { try { - new URL(this.watch_link) + new URL(this.watchLink) } catch (e) { - this.errors.watch_link = 'Please enter a valid URL (e.g., https://example.com)' + this.errors.watchLink = 'Please enter a valid URL (e.g., https://example.com)' isValid = false } } // KP ID validation (optional, but if provided must be positive) - if (this.kp_id !== null && this.kp_id !== '' && this.kp_id < 0) { - this.errors.kp_id = 'Kinopoisk ID must be a positive number' + if (this.kpId !== null && this.kpId !== '' && this.kpId < 0) { + this.errors.kpId = 'Kinopoisk ID must be a positive number' isValid = false } // IMDB ID validation (optional, but if provided must be positive) - if (this.imdb_id !== null && this.imdb_id !== '' && this.imdb_id < 0) { - this.errors.imdb_id = 'IMDB ID must be a positive number' + if (this.imdbId !== null && this.imdbId !== '' && this.imdbId < 0) { + this.errors.imdbId = 'IMDB ID must be a positive number' isValid = false } @@ -328,9 +328,9 @@ export default { description: this.description.trim() || null, year: this.year || null, rating: this.rating || null, - watch_link: this.watch_link.trim() || null, - kp_id: this.kp_id || null, - imdb_id: this.imdb_id || null, + watch_link: this.watchLink.trim() || null, + kp_id: this.kpId || null, + imdb_id: this.imdbId || null, watched: this.watched, published: this.published } @@ -365,9 +365,9 @@ export default { description: this.description.trim() || null, year: this.year || null, rating: this.rating || null, - watch_link: this.watch_link.trim() || null, - kp_id: this.kp_id || null, - imdb_id: this.imdb_id || null, + watch_link: this.watchLink.trim() || null, + kp_id: this.kpId || null, + imdb_id: this.imdbId || null, watched: this.watched, published: this.published } @@ -393,9 +393,9 @@ export default { this.description = '' this.year = null this.rating = null - this.watch_link = '' - this.kp_id = null - this.imdb_id = null + this.watchLink = '' + this.kpId = null + this.imdbId = null this.watched = false this.published = false this.editingId = null @@ -404,9 +404,9 @@ export default { description: '', year: '', rating: '', - watch_link: '', - kp_id: '', - imdb_id: '' + watchLink: '', + kpId: '', + imdbId: '' } this.errorMessage = '' }, @@ -416,9 +416,9 @@ export default { this.description = movie.description || '' this.year = movie.year || null this.rating = movie.rating || null - this.watch_link = movie.watch_link || '' - this.kp_id = movie.kp_id || null - this.imdb_id = movie.imdb_id || null + this.watchLink = movie.watchLink || '' + this.kpId = movie.kpId || null + this.imdbId = movie.imdbId || null this.watched = movie.watched || false this.published = movie.published || false this.editingId = movie.id diff --git a/frontend/src/components/MovieItem.vue b/frontend/src/components/MovieItem.vue index 9ec74e6..c999d7b 100644 --- a/frontend/src/components/MovieItem.vue +++ b/frontend/src/components/MovieItem.vue @@ -26,7 +26,7 @@
👤 - {{ movie.user }} + {{ movie.user.username }}
@@ -37,34 +37,34 @@ -
+ -
+ -
+
🔗 diff --git a/frontend/src/components/MovieList.vue b/frontend/src/components/MovieList.vue index b3fb34a..6e91388 100644 --- a/frontend/src/components/MovieList.vue +++ b/frontend/src/components/MovieList.vue @@ -114,7 +114,7 @@ export default { only_mine: this.onlyMine } const res = await axios.get('/api/movies/', { params }) - this.movies = res.data + this.movies = res.data["movies"] } catch (error) { console.error('Error fetching movies:', error) this.error = error.response?.data?.detail || 'Failed to load movies. Please check your connection.' From 1b9eedec913a15f828fb5bf4dbab681ae73f94cd Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Tue, 17 Feb 2026 15:55:09 +0300 Subject: [PATCH 12/13] fixed: If a user's authorization expires, they send a lot of requests. --- frontend/src/services/auth.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/frontend/src/services/auth.js b/frontend/src/services/auth.js index 187c5b3..69a3b09 100644 --- a/frontend/src/services/auth.js +++ b/frontend/src/services/auth.js @@ -20,20 +20,20 @@ class AuthService { } async requestVerifyToken(email) { - const response = await axios.post('/api/auth/request-verify-token', { - email, - }) + const response = await axios.post('/api/auth/request-verify-token', { + email, + }) - return response.data -} + return response.data + } async verifyEmail(token) { - const response = await axios.post('/api/auth/verify', { - token, - }) + const response = await axios.post('/api/auth/verify', { + token, + }) - return response.data -} + return response.data + } async login(email, password) { const formData = new URLSearchParams() @@ -118,6 +118,8 @@ class AuthService { } setupAxiosInterceptor() { + let isLoggingOut = false + axios.interceptors.request.use( config => { if (this.token) { @@ -130,9 +132,10 @@ class AuthService { axios.interceptors.response.use( response => response, - error => { - if (error.response?.status === 401) { - this.logout() + async error => { + if (error.response?.status === 401 && !isLoggingOut) { + isLoggingOut = true + await this.logout() window.location.href = '/login' } return Promise.reject(error) From d8c74102ca0afe47bcbe59a6e69792fadc35754e Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Tue, 17 Feb 2026 16:00:17 +0300 Subject: [PATCH 13/13] lint fix --- backend/backlog_app/api/crud.py | 2 +- backend/backlog_app/api/view/movie_view.py | 2 +- backend/backlog_app/schemas/helper.py | 4 ++-- backend/backlog_app/schemas/movie.py | 3 ++- backend/backlog_app/schemas/user.py | 9 +++------ backend/tests/test_schemas/test_movie.py | 16 ++++------------ 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/backend/backlog_app/api/crud.py b/backend/backlog_app/api/crud.py index 1c59303..fed21a3 100644 --- a/backend/backlog_app/api/crud.py +++ b/backend/backlog_app/api/crud.py @@ -9,7 +9,7 @@ from backlog_app.models import User from backlog_app.models.movie import Movie -from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate, MovieList +from backlog_app.schemas.movie import MovieCreate, MovieList, MovieRead, MovieUpdate logger = logging.getLogger(__name__) diff --git a/backend/backlog_app/api/view/movie_view.py b/backend/backlog_app/api/view/movie_view.py index ba42f85..c103f87 100644 --- a/backend/backlog_app/api/view/movie_view.py +++ b/backend/backlog_app/api/view/movie_view.py @@ -8,7 +8,7 @@ current_active_user, ) from backlog_app.models.users import User -from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate, MovieList +from backlog_app.schemas.movie import MovieCreate, MovieList, MovieRead, MovieUpdate from backlog_app.storages.database import get_async_session router = APIRouter(prefix="/movies", tags=["Movies"]) diff --git a/backend/backlog_app/schemas/helper.py b/backend/backlog_app/schemas/helper.py index 2ea3225..2e21f6b 100644 --- a/backend/backlog_app/schemas/helper.py +++ b/backend/backlog_app/schemas/helper.py @@ -1,3 +1,3 @@ def to_camel_case(snake_str: str) -> str: - parts = snake_str.split('_') - return parts[0] + ''.join(word.capitalize() for word in parts[1:]) + parts = snake_str.split("_") + return parts[0] + "".join(word.capitalize() for word in parts[1:]) diff --git a/backend/backlog_app/schemas/movie.py b/backend/backlog_app/schemas/movie.py index 19908d2..414b172 100644 --- a/backend/backlog_app/schemas/movie.py +++ b/backend/backlog_app/schemas/movie.py @@ -2,7 +2,7 @@ from typing import Annotated from annotated_types import Len -from pydantic import BaseModel, Field, ConfigDict +from pydantic import BaseModel, ConfigDict, Field from .helper import to_camel_case from .user import UserRead @@ -48,5 +48,6 @@ class MovieRead(MovieBase): rating: float | None created_at: datetime + class MovieList(BaseModel): movies: list[MovieRead] diff --git a/backend/backlog_app/schemas/user.py b/backend/backlog_app/schemas/user.py index b0253ac..75f312b 100644 --- a/backend/backlog_app/schemas/user.py +++ b/backend/backlog_app/schemas/user.py @@ -3,7 +3,7 @@ from annotated_types import Len from fastapi_users import schemas -from pydantic import BaseModel, EmailStr, ConfigDict, computed_field +from pydantic import BaseModel, ConfigDict, EmailStr, computed_field from .helper import to_camel_case @@ -15,12 +15,9 @@ class UserRead(BaseModel): @computed_field @property def username(self) -> str: - return self.email.split('@')[0] + return self.email.split("@")[0] - model_config = ConfigDict( - from_attributes=True, - alias_generator=to_camel_case - ) + model_config = ConfigDict(from_attributes=True, alias_generator=to_camel_case) class UserCreate(schemas.BaseUserCreate): diff --git a/backend/tests/test_schemas/test_movie.py b/backend/tests/test_schemas/test_movie.py index 83ddf6f..15a666f 100644 --- a/backend/tests/test_schemas/test_movie.py +++ b/backend/tests/test_schemas/test_movie.py @@ -1,5 +1,6 @@ from datetime import datetime from uuid import uuid4 + import pytest from pydantic import ValidationError @@ -14,10 +15,7 @@ def test_movie_can_be_create_from_create_schema() -> None: watch_link="https://example.com", rating=6.7, ) - user = UserRead( - id=uuid4(), - email="test@example.com" - ) + user = UserRead(id=uuid4(), email="test@example.com") movie = MovieRead( **movie_in.model_dump(), id=0, @@ -55,10 +53,7 @@ def test_movie_create_max_value( title=title, description=description, ) - user = UserRead( - id=uuid4(), - email="test@example.com" - ) + user = UserRead(id=uuid4(), email="test@example.com") movie = MovieRead( **movie_in.model_dump(), id=0, @@ -72,10 +67,7 @@ def test_movie_create_max_value( def test_movie_update_from_update_schema() -> None: - user = UserRead( - id=uuid4(), - email="test@example.com" - ) + user = UserRead(id=uuid4(), email="test@example.com") movie = MovieRead( id=0,