From c43636731b4272e9c23094422a2a6e299f107d62 Mon Sep 17 00:00:00 2001 From: Kurbanov Bulat Date: Sat, 22 Jan 2022 21:49:08 +0300 Subject: [PATCH] Fix joins and subqueries --- fastapi_book_server/app/services/author.py | 3 +- fastapi_book_server/app/services/book.py | 3 +- fastapi_book_server/app/services/common.py | 4 +- fastapi_book_server/app/services/sequence.py | 2 +- .../app/services/translator.py | 3 +- fastapi_book_server/app/views/author.py | 34 +++++++++++++---- fastapi_book_server/app/views/book.py | 38 ++++++++++++++----- fastapi_book_server/app/views/sequence.py | 5 +-- fastapi_book_server/app/views/translation.py | 6 +-- 9 files changed, 69 insertions(+), 29 deletions(-) diff --git a/fastapi_book_server/app/services/author.py b/fastapi_book_server/app/services/author.py index c9fdf6c..76709b2 100644 --- a/fastapi_book_server/app/services/author.py +++ b/fastapi_book_server/app/services/author.py @@ -47,7 +47,8 @@ SELECT ARRAY( class AuthorTGRMSearchService(TRGMSearchService): MODEL_CLASS = Author - PREFETCH_RELATED = ["source", "annotations"] + SELECT_RELATED = ["source"] + PREFETCH_RELATED = ["annotations"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY diff --git a/fastapi_book_server/app/services/book.py b/fastapi_book_server/app/services/book.py index 8fc7cae..c4ac534 100644 --- a/fastapi_book_server/app/services/book.py +++ b/fastapi_book_server/app/services/book.py @@ -24,7 +24,8 @@ SELECT ARRAY( class BookTGRMSearchService(TRGMSearchService): MODEL_CLASS = BookDB - PREFETCH_RELATED = ["source", "authors", "translators", "annotations"] + SELECT_RELATED = ["source"] + PREFETCH_RELATED = ["authors", "translators", "annotations"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY diff --git a/fastapi_book_server/app/services/common.py b/fastapi_book_server/app/services/common.py index 68ae023..e7b948c 100644 --- a/fastapi_book_server/app/services/common.py +++ b/fastapi_book_server/app/services/common.py @@ -70,7 +70,7 @@ class TRGMSearchService(Generic[T]): @classmethod @property def cache_prefix(cls) -> str: - return cls.CUSTOM_CACHE_PREFIX or cls.model.__class__.__name__ + return cls.CUSTOM_CACHE_PREFIX or cls.model.Meta.tablename @classmethod def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str: @@ -90,7 +90,7 @@ class TRGMSearchService(Generic[T]): data = await redis.get(key) if data is None: - return data + return None return orjson.loads(data) except aioredis.RedisError as e: diff --git a/fastapi_book_server/app/services/sequence.py b/fastapi_book_server/app/services/sequence.py index 1096e69..e5fa5c0 100644 --- a/fastapi_book_server/app/services/sequence.py +++ b/fastapi_book_server/app/services/sequence.py @@ -36,7 +36,7 @@ SELECT ARRAY ( class SequenceTGRMSearchService(TRGMSearchService): MODEL_CLASS = Sequence - PREFETCH_RELATED = ["source"] + SELECT_RELATED = ["source"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY diff --git a/fastapi_book_server/app/services/translator.py b/fastapi_book_server/app/services/translator.py index 951a8c0..b93fd1b 100644 --- a/fastapi_book_server/app/services/translator.py +++ b/fastapi_book_server/app/services/translator.py @@ -48,5 +48,6 @@ SELECT ARRAY( class TranslatorTGRMSearchService(TRGMSearchService): MODEL_CLASS = Author CUSTOM_CACHE_PREFIX = "translator" - PREFETCH_RELATED = ["source", "annotations"] + SELECT_RELATED = ["source"] + PREFETCH_RELATED = ["annotations"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY diff --git a/fastapi_book_server/app/views/author.py b/fastapi_book_server/app/views/author.py index d61367c..757a023 100644 --- a/fastapi_book_server/app/views/author.py +++ b/fastapi_book_server/app/views/author.py @@ -27,34 +27,49 @@ author_router = APIRouter( ) -PREFETCH_RELATED = ["source", "annotations"] +SELECT_RELATED_FIELDS = ["source"] +PREFETCH_RELATED_FIELDS = ["annotations"] @author_router.get( "/", response_model=CustomPage[Author], dependencies=[Depends(Params)] ) async def get_authors(): - return await paginate(AuthorDB.objects.prefetch_related(PREFETCH_RELATED)) + return await paginate( + AuthorDB.objects.select_related(SELECT_RELATED_FIELDS).prefetch_related( + PREFETCH_RELATED_FIELDS + ) + ) @author_router.post("/", response_model=Author, dependencies=[Depends(Params)]) async def create_author(data: CreateAuthor): author = await AuthorDB.objects.create(**data.dict()) - return await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get(id=author.id) + return ( + await AuthorDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get(id=author.id) + ) @author_router.get("/random", response_model=Author) async def get_random_author(allowed_langs: list[str] = Depends(get_allowed_langs)): author_id = await GetRandomAuthorService.get_random_id(allowed_langs) - return await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get(id=author_id) + return ( + await AuthorDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get(id=author_id) + ) @author_router.get("/{id}", response_model=Author) async def get_author(id: int): - author = await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get_or_none( - id=id + author = ( + await AuthorDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get_or_none(id=id) ) if author is None: @@ -92,7 +107,8 @@ async def get_author_books( id: int, allowed_langs: list[str] = Depends(get_allowed_langs) ): return await paginate( - BookDB.objects.select_related(["source", "annotations", "translators"]) + BookDB.objects.select_related(["source"]) + .prefetch_related(["annotations", "translators"]) .filter(authors__id=id, lang__in=allowed_langs, is_deleted=False) .order_by("title") ) @@ -121,7 +137,9 @@ async def get_translated_books( id: int, allowed_langs: list[str] = Depends(get_allowed_langs) ): return await paginate( - BookDB.objects.select_related(["source", "annotations", "authors"]).filter( + BookDB.objects.select_related(["source"]) + .prefetch_related(["annotations", "authors"]) + .filter( translators__id=id, lang__in=allowed_langs, is_deleted=False, diff --git a/fastapi_book_server/app/views/book.py b/fastapi_book_server/app/views/book.py index 276c836..70938de 100644 --- a/fastapi_book_server/app/views/book.py +++ b/fastapi_book_server/app/views/book.py @@ -29,8 +29,8 @@ book_router = APIRouter( dependencies=[Depends(check_token)], ) - -SELECT_RELATED_FIELDS = ["source", "authors", "translators", "annotations"] +SELECT_RELATED_FIELDS = ["source"] +PREFETCH_RELATED_FIELDS = ["authors", "translators", "annotations"] @book_router.get( @@ -38,7 +38,9 @@ SELECT_RELATED_FIELDS = ["source", "authors", "translators", "annotations"] ) async def get_books(book_filter: dict = Depends(get_book_filter)): return await paginate( - BookDB.objects.select_related(SELECT_RELATED_FIELDS).filter(**book_filter) + BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .filter(**book_filter) ) @@ -46,19 +48,31 @@ async def get_books(book_filter: dict = Depends(get_book_filter)): async def create_book(data: Union[CreateBook, CreateRemoteBook]): book = await BookCreator.create(data) - return await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get(id=book.id) + return ( + await BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get(id=book.id) + ) @book_router.get("/random", response_model=BookDetail) async def get_random_book(allowed_langs: list[str] = Depends(get_allowed_langs)): book_id = await GetRandomBookService.get_random_id(allowed_langs) - return await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get(id=book_id) + return ( + await BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get(id=book_id) + ) @book_router.get("/{id}", response_model=BookDetail) async def get_book(id: int): - book = await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get_or_none(id=id) + book = ( + await BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get_or_none(id=id) + ) if book is None: raise HTTPException(status.HTTP_404_NOT_FOUND) @@ -68,8 +82,10 @@ async def get_book(id: int): @book_router.get("/remote/{source_id}/{remote_id}", response_model=Book) async def get_remote_book(source_id: int, remote_id: int): - book = await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get_or_none( - source=source_id, remote_id=remote_id + book = ( + await BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get_or_none(source=source_id, remote_id=remote_id) ) if book is None: @@ -80,7 +96,11 @@ async def get_remote_book(source_id: int, remote_id: int): @book_router.put("/{id}", response_model=Book) async def update_book(id: int, data: UpdateBook): - book = await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get_or_none(id=id) + book = ( + await BookDB.objects.select_related(SELECT_RELATED_FIELDS) + .prefetch_related(PREFETCH_RELATED_FIELDS) + .get_or_none(id=id) + ) if book is None: raise HTTPException(status.HTTP_404_NOT_FOUND) diff --git a/fastapi_book_server/app/views/sequence.py b/fastapi_book_server/app/views/sequence.py index 73e4ee2..1ae9ab0 100644 --- a/fastapi_book_server/app/views/sequence.py +++ b/fastapi_book_server/app/views/sequence.py @@ -47,9 +47,8 @@ async def get_sequence_books( id: int, allowed_langs: list[str] = Depends(get_allowed_langs) ): return await paginate( - BookDB.objects.select_related( - ["source", "annotations", "authors", "translators"] - ) + BookDB.objects.select_related(["source"]) + .prefetch_related(["annotations", "authors", "translators"]) .filter(sequences__id=id, lang__in=allowed_langs, is_deleted=False) .order_by("sequences__booksequences__position") ) diff --git a/fastapi_book_server/app/views/translation.py b/fastapi_book_server/app/views/translation.py index 6202aa8..b73dda5 100644 --- a/fastapi_book_server/app/views/translation.py +++ b/fastapi_book_server/app/views/translation.py @@ -27,21 +27,21 @@ translation_router = APIRouter( "/", response_model=CustomPage[Translation], dependencies=[Depends(Params)] ) async def get_translations(): - return await paginate(TranslationDB.objects.prefetch_related(["book", "author"])) + return await paginate(TranslationDB.objects.select_related(["book", "author"])) @translation_router.post("/", response_model=Translation) async def create_translation(data: Union[CreateTranslation, CreateRemoteTranslation]): translation = await TranslationCreator.create(data) - return await TranslationDB.objects.prefetch_related(["book", "author"]).get( + return await TranslationDB.objects.select_related(["book", "author"]).get( id=translation.id ) @translation_router.delete("/{id}", response_model=Translation) async def delete_translation(id: int): - translation = await TranslationDB.objects.prefetch_related( + translation = await TranslationDB.objects.select_related( ["book", "author"] ).get_or_none(id=id)