Skip to content

Instantly share code, notes, and snippets.

@mbrochh
Last active September 24, 2023 10:45
Show Gist options
  • Save mbrochh/f92594ab8188393bd83c892ef2af25e6 to your computer and use it in GitHub Desktop.
Save mbrochh/f92594ab8188393bd83c892ef2af25e6 to your computer and use it in GitHub Desktop.
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
}
}
}
`
@Cimmanuel
Copy link

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
Copy link

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

@cyrilmarceau
Copy link

cyrilmarceau commented Jan 24, 2022

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 ?

@Lyrics2000
Copy link

thanks alot for this

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