Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using pagination with Django, graphene and Apollo
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
}
}
}
`
@PhilippeFerreiraDeSousa

This comment has been minimized.

Copy link

@PhilippeFerreiraDeSousa PhilippeFerreiraDeSousa commented Feb 11, 2018

It's hasNext and hasPrev in the request frontend.

@carlossanchezcorrea

This comment has been minimized.

Copy link

@carlossanchezcorrea carlossanchezcorrea commented Jul 14, 2018

Hey its a great option only I have 1 question with this implementation we can filter by Title or whatever?

@knavels

This comment has been minimized.

Copy link

@knavels knavels commented Jan 31, 2020

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

@Zagrebelin

This comment has been minimized.

Copy link

@Zagrebelin Zagrebelin commented Jun 24, 2020

Good job, but can i ask you, what is CustomNode in these snippets?

@mbrochh

This comment has been minimized.

Copy link
Owner Author

@mbrochh mbrochh commented Jun 24, 2020

@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)

@Cimmanuel

This comment has been minimized.

Copy link

@Cimmanuel Cimmanuel commented Jul 6, 2020

@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

@mbrochh

This comment has been minimized.

Copy link
Owner Author

@mbrochh mbrochh commented Jul 6, 2020

@Cimmanuel so far I haven't bothered to figure out a reusable way 🙈

@Cimmanuel

This comment has been minimized.

Copy link

@Cimmanuel Cimmanuel commented Jul 6, 2020

@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!

@alfredrumss

This comment has been minimized.

Copy link

@alfredrumss alfredrumss commented Oct 7, 2020

@mbrochh @Cimmanuel

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment