Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom pagination for multiple response types? #940

Open
SonQBChau opened this issue Nov 20, 2023 · 4 comments · May be fixed by #1148
Open

Custom pagination for multiple response types? #940

SonQBChau opened this issue Nov 20, 2023 · 4 comments · May be fixed by #1148
Assignees

Comments

@SonQBChau
Copy link

How can I implement custom pagination for multiple response types? I've successfully applied custom pagination with response=List[MySchema], but encountered issues with a structure like response={200:List[MySchema], 400: Dict[str,str]}. Here's what I've attempted so far:

class CustomPagination(PaginationBase):
    class Input(Schema):
        page: int = Field(None, ge=1)
        page_size: int = Field(None, ge=1)

    class Output(Schema):
        items: List[Any] = []
        count: Optional[int]
        next_page: Optional[int]
        previous_page: Optional[int]

    def paginate_queryset(self, queryset, pagination: Input, **params):
        DEFAULT_PAGE = 1
        DEFAULT_PAGE_SIZE = 100
        pagination.page = pagination.page or DEFAULT_PAGE
        pagination.page_size = pagination.page_size or DEFAULT_PAGE_SIZE

        offset = (pagination.page - 1) * pagination.page_size

        # trying to catch bad requests here
	if isinstance(queryset, tuple) and queryset[0] == HTTPStatus.BAD_REQUEST:
            return queryset
            
        total_items = queryset.count()
        items = list(queryset[offset : offset + pagination.page_size])

        next_page = (
            pagination.page + 1 if offset + pagination.page_size < total_items else None
        )
        previous_page = pagination.page - 1 if pagination.page > 1 else None

        return {
            "items": items,
            "count": len(items),
            "next_page": next_page,
            "previous_page": previous_page,
        }


class CustomRouterPaginated(RouterPaginated):
    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.pagination_class = CustomPagination
        
router = CustomRouterPaginated()
@router.get("/books", response={200:List[BookSchema], 400: Dict[str,str]})
def list_books(request, filters: BookFilterSchema = Query(...)):
    books = Book.objects.all()
    books = filters.filter(books)
    # custom logic with filter, return error if not satisfied
	    return HTTPStatus.BAD_REQUEST, error_message
    return books
@vitalik
Copy link
Owner

vitalik commented Nov 21, 2023

Hi @SonQBChau

yeah.. the pagination router does not support multiple codes (for now I guess)

what you can do at this moment - define response as just list and throw http error with message:

@router.get("/books", response=List[BookSchema]). # <-----
def list_books(request, filters: BookFilterSchema = Query(...)):
    ...
    if error:
        raise HttpError(400, 'Invalid filters') # <-----
    return books

@vitalik vitalik self-assigned this Nov 21, 2023
@SonQBChau
Copy link
Author

Thank you for the reply. The implemented filter has conditional requirements; for instance, book_id requires author_id, but not the other way around. Although I can't mark both as required, I try to avoid raising errors and instead offer a graceful message. I can work around it, I'm just curious if there's an easy way to handle this situation.

@vitalik
Copy link
Owner

vitalik commented Nov 22, 2023

I will keep this open - maybe we simply find first 200 response for paginator (if it is marked as collection)

@benjaoming
Copy link
Contributor

I have a current use case, in which the return schema will vary depending on authorization level. We can use the HTTP response codes to reflect this, but then the pagination needs to work for 2xx response codes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants