-
-
Save mbrochh/f92594ab8188393bd83c892ef2af25e6 to your computer and use it in GitHub Desktop.
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator | |
# First we create a little helper function, becase we will potentially have many PaginatedTypes | |
# and we will potentially want to turn many querysets into paginated results: | |
def get_paginator(qs, page_size, page, paginated_type, **kwargs): | |
p = Paginator(qs, page_size) | |
try: | |
page_obj = p.page(page) | |
except PageNotAnInteger: | |
page_obj = p.page(1) | |
except EmptyPage: | |
page_obj = p.page(p.num_pages) | |
return paginated_type( | |
page=page_obj.number, | |
pages=p.num_pages, | |
has_next=page_obj.has_next(), | |
has_prev=page_obj.has_previous(), | |
objects=page_obj.object_list, | |
**kwargs | |
) |
from theartling.utils import get_paginator | |
from . import models | |
# Let's assume you have some ObjectType for one of your models: | |
class ProductType(DjangoObjectType): | |
class Meta: | |
model = models.Product | |
# Now we create a corresponding PaginatedType for that object type: | |
class ProductPaginatedType(graphene.ObjectType): | |
page = graphene.Int() | |
pages = graphene.Int() | |
has_next = graphene.Boolean() | |
has_prev = graphene.Boolean() | |
objects = graphene.List(ProductType) | |
class Query(object): | |
products = graphene.Field(ProductPaginatedType, page=graphene.Int()) | |
# Now, in your resolver functions, you just query your objects and turn the queryset into the PaginatedType using the helper function: | |
def resolve_products(self, info, page): | |
page_size = 10 | |
qs = models.Product.objects.all() | |
return get_paginator(qs, page_size, page, ProductPaginatedType) |
// In your frontend, you just query your endpoint and request all the fields from the PaginatedType: | |
const gql = ` | |
{ | |
products(page: 1) { | |
page | |
pages | |
has_next | |
has_prev | |
objects { | |
id | |
name | |
slug | |
whatever | |
} | |
} | |
} | |
` |
Hey its a great option only I have 1 question with this implementation we can filter by Title or whatever?
Thank you for sharing that, the thing is that how can we refactor these?! because in this case there will be a lot of repeated codes, I tried to refactor paginate class but it caused error
Good job, but can i ask you, what is CustomNode in these snippets?
@Zagrebelin forget about CustomNode
... when I started out with graphene my understanding of GraphQL was quite poor and I found some hack online that made sure that the GraphQL ID is equal to the Django PK, instead of being some weird hash value. I think this is now the default behaviour for graphene in Django, I'm not using this CustomNode thing any more in my more recent projects.
(I have updated the gist and removed that line)
@mbrochh this is really nice. How would you make PaginatedType reusable though? I mean instead of creating a PaginatedType for each DjangoObjectType, how do you think the DRY principle can be honoured? I'm trying to figure something out
@Cimmanuel so far I haven't bothered to figure out a reusable way 🙈
@mbrochh
Ouch!
Here's what I did so far:
class PaginatedType(graphene.ObjectType):
page = graphene.Int()
pages = graphene.Int()
has_next = graphene.Boolean()
has_prev = graphene.Boolean()
class ProductPaginatedType(PaginatedType):
objects = graphene.List(ProductType)
I did this just to remove redundancy and make it a little neat. I feel there's more that can be done. Please let me know when you figure something out. Thanks!
how to handle this solution when you have filters in a class level filterset_class in the Query.
Basically doing this you would have to do all filters by hand and then paginate them as I haven't figured it out how to fetch in resolve methods what the filters have filtered before.
Actually, I've got lots of filters in the all app with filterset_class, so it shouldn't be nice removing all of them to do it in the resolvers side.
Any idea?
Hello,
Thanks for this code. It working fine. I try to set up this pagination with DjangoFilterConnectionField but I get an error.
I try to set the pagination before the filter end. Do you have an idea on how to do setup this pagination with DjangoFilterConnectionField ?
thanks alot for this
It's hasNext and hasPrev in the request frontend.