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

feat: New Paginated generic to be used as a wrapped for paginated results #642

Merged
merged 13 commits into from
Nov 9, 2024

Conversation

bellini666
Copy link
Member

@bellini666 bellini666 commented Oct 13, 2024

Fix #408

  • Create Paginated generic type
  • Integrate Paginated with StrawberryDjangoField
  • Ensure it is working with the optimizer
  • Ensure it is working with permissions
  • Update the documentation

Summary by Sourcery

Introduce a new Paginated generic type for handling paginated results, integrate it with StrawberryDjangoField, and update documentation and tests to support this new feature.

New Features:

  • Introduce a new Paginated generic type to handle paginated results in a more structured way.

Enhancements:

  • Integrate the Paginated generic with StrawberryDjangoField to support pagination in GraphQL queries.
  • Ensure compatibility of the Paginated generic with existing optimizers and permission systems.

Documentation:

  • Update documentation to include usage instructions and examples for the new Paginated generic type.

Tests:

  • Add tests to verify the functionality of the Paginated generic type, including its integration with permissions and nested queries.

@bellini666 bellini666 self-assigned this Oct 13, 2024
@codecov-commenter
Copy link

codecov-commenter commented Oct 13, 2024

Codecov Report

Attention: Patch coverage is 95.73171% with 7 lines in your changes missing coverage. Please review.

Project coverage is 88.99%. Comparing base (59c76ef) to head (21d82d6).

Files with missing lines Patch % Lines
strawberry_django/pagination.py 94.44% 4 Missing ⚠️
strawberry_django/fields/field.py 93.87% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #642      +/-   ##
==========================================
+ Coverage   88.77%   88.99%   +0.21%     
==========================================
  Files          41       41              
  Lines        3607     3724     +117     
==========================================
+ Hits         3202     3314     +112     
- Misses        405      410       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

sourcery-ai bot commented Oct 19, 2024

Reviewer's Guide by Sourcery

This pull request introduces a new generic type Paginated for handling paginated results in Strawberry Django. It integrates this new type with existing functionality, updates the optimizer and permissions handling, and adds comprehensive tests. The changes aim to provide a more flexible and efficient way to handle pagination in GraphQL queries.

User journey diagram for querying paginated results

journey
    title User Journey for Querying Paginated Results
    section Query Fruits
      User -> GraphQL API: Request fruits with pagination
      GraphQL API -> Database: Fetch fruits with pagination
      Database -> GraphQL API: Return paginated fruits
      GraphQL API -> User: Return paginated fruits with totalCount and pageInfo
    section Query Colors with Nested Fruits
      User -> GraphQL API: Request colors with nested fruits pagination
      GraphQL API -> Database: Fetch colors and nested fruits with pagination
      Database -> GraphQL API: Return paginated colors and nested fruits
      GraphQL API -> User: Return paginated colors with nested fruits, totalCount, and pageInfo
Loading

Class diagram for the new Paginated generic type

classDiagram
    class Paginated {
        +Optional~QuerySet~ queryset
        +OffsetPaginationInput pagination
        +PaginatedInfo page_info()
        +int total_count()
        +list~NodeType~ results()
        +int get_total_count()
        +Optional~QuerySet~ get_paginated_queryset()
    }
    class OffsetPaginationInput {
        +int offset
        +int limit
    }
    class PaginatedInfo {
        +int limit
        +int offset
    }
    Paginated --> OffsetPaginationInput
    Paginated --> PaginatedInfo
Loading

File-Level Changes

Change Details Files
Introduced a new Paginated generic type for handling paginated results
  • Created Paginated and PaginatedInfo types in strawberry_django/pagination.py
  • Added methods to handle pagination, including get_total_count and get_paginated_queryset
  • Updated apply function to handle cases where pagination.limit is negative
strawberry_django/pagination.py
Integrated Paginated type with existing Strawberry Django functionality
  • Updated StrawberryDjangoFieldBase to handle Paginated types
  • Modified get_model_hints to work with Paginated types
  • Added is_paginated property to StrawberryDjangoFieldBase
strawberry_django/fields/base.py
strawberry_django/optimizer.py
Updated optimizer to work with Paginated type
  • Added handling for Paginated type in _optimize_prefetch_queryset
  • Created _get_model_hints_from_paginated function
strawberry_django/optimizer.py
Updated permissions handling to work with Paginated type
  • Modified handle_no_permission to return an empty Paginated object when necessary
strawberry_django/permissions.py
Added comprehensive tests for the new Paginated functionality
  • Created test cases for Paginated schema generation
  • Added tests for pagination queries, including nested queries
  • Implemented both synchronous and asynchronous test cases
tests/test_paginated_type.py
Updated documentation to reflect new pagination functionality
  • Added explanation and examples for using the new Paginated type
  • Updated existing pagination documentation to include information about Paginated
docs/guide/pagination.md

Assessment against linked issues

Issue Objective Addressed Explanation
#408 Implement a totalCount attribute for non-relay pagination queries
#408 Create a custom pagination logic by overriding the StrawberryDjangoField class
#408 Ensure the new pagination approach is compatible with the existing application built on non-relay nodes

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @bellini666 - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Great addition of the Paginated generic type. The implementation is solid, well-integrated with existing components, and thoroughly tested. The extensive test coverage is particularly commendable.
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟡 Documentation: 1 issue found

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

docs/guide/pagination.md Outdated Show resolved Hide resolved
tests/test_paginated_type.py Show resolved Hide resolved
strawberry_django/pagination.py Outdated Show resolved Hide resolved
strawberry_django/pagination.py Outdated Show resolved Hide resolved
strawberry_django/pagination.py Show resolved Hide resolved
Copy link
Member

@patrick91 patrick91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is cool, I left some initial comments 😊

docs/guide/pagination.md Outdated Show resolved Hide resolved
docs/guide/pagination.md Outdated Show resolved Hide resolved
docs/guide/pagination.md Outdated Show resolved Hide resolved
Comment on lines +142 to +155
@strawberry_django.field
def average_price(self) -> Decimal:
if self.queryset is None:
return Decimal(0)

return self.queryset.aggregate(Avg("price"))["price__avg"]

@strawberry_django.field
def paginated_average_price(self) -> Decimal:
paginated_queryset = self.get_paginated_queryset()
if paginated_queryset is None:
return Decimal(0)

return paginated_queryset.aggregate(Avg("price"))["price__avg"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obs. this also works for relay connections (I've done some similar stuff in the past like this :)

docs/guide/pagination.md Outdated Show resolved Hide resolved
@@ -574,6 +574,15 @@ def _optimize_prefetch_queryset(
else:
mark_optimized = False

if isinstance(field.type, type) and issubclass(field.type, Paginated):
pagination = field_kwargs.get("pagination")
qs = apply_window_pagination(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to customise the queryset? should we document it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm I haven't thought about that... good idea! Make it similar to the Connection

Copy link
Member Author

@bellini666 bellini666 Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored everything to be used similarly to how the Connection works, with an extension.

It is in this commit: b5a226d (#642)

strawberry_django/optimizer.py Outdated Show resolved Hide resolved
strawberry_django/pagination.py Outdated Show resolved Hide resolved
strawberry_django/pagination.py Outdated Show resolved Hide resolved
strawberry_django/pagination.py Outdated Show resolved Hide resolved

@strawberry.type
class Query:
@straberry.offset_paginated(OffsetPaginated[Fruit], order=order)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here @bellini666 but this is really gorgeous, overall, I'm loving this feature.

return queryset
```

This would produce the following schema:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a huge fan of adding the resulting schema to the docs for each of these things, it's been a big point of frustration for me with the docs not seeing how it should come out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I'm planing on doing some improvements to the docs soon and I'll do more of this!

@thclark
Copy link
Contributor

thclark commented Nov 6, 2024

@bellini666 and @patrick91 I'd love to see this released, anything I can do to help expedite?

Copy link
Member

@patrick91 patrick91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing stuff, the docs are 👌


### Default limit for pagination

The default limit for pagination is set to `100`. This can be changed in the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙇‍♂️

@bellini666 bellini666 merged commit 6d8fec3 into main Nov 9, 2024
24 checks passed
@bellini666 bellini666 deleted the pagination branch November 9, 2024 22:17
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 this pull request may close these issues.

Adding totalCount in non-relay pagination: suggestion regarding approaches
4 participants