Search code examples
djangographqlgraphene-pythondjango-annotate

Return annotated queryset against graphql queries on django graphene


class Project(models.Model):
    name = models.CharField(max_length=189)

class Customer(models.Model):
    name = models.CharField(max_length=189)
    is_deleted = models.BooleanField(default=False)
    project = models.ForeignKey(Project, related_name="customers")

class Message(models.Model):
    message = models.TextField()
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="messages")
    created_at = models.DateTimeField(auto_now_add=True)

I am using the following queryset to get all customers under a certain project ordered by who had messaged last.

qs = Customer.objects.filter(messages__isnull=False) \
.annotate(last_message=Max('messages__created_at')).order_by('-last_message')

Now I want to use a basic graphene query (NOT relay) to get a project and the customers associated with this project according to the annotated queryset. I may also have a second use-case where I would need to filter the project.customers.all() queryset according to a field in the Customer table (e.g. the customers which have is_deleted=False).

Currently in my graphql schema i have,

class ProjectNode(DjangoObjectType):
    class Meta:
        model = Project

class CustomerNode(DjangoObjectType):
    class Meta:
        model = Customer

class Query(graphene.ObjectType):
    project = graphene.Field(ProjectNode, id=graphene.Int(), token=graphene.String(), )
    top_chat_customers = graphene.Field(CustomerNode, project_id=graphene.Int())

     def resolve_project(self, info, **kwargs):
        pk = kwargs["id"]
        return Project.objects.filter(id=pk).first()

    def resolve_top_chat_customers(self, info, **kwargs):
        project = Project.objects.filter(id=kwargs["project_id"]).first()
        return Customer.objects.filter(project=project, messages__isnull=False) \
.annotate(last_message=Max('messages__created_at')).order_by('-last_message')

Here when I try to get the list of customers separately by providing the project Id, It shows error: "Received incompatible instance..."

Any ideas on how I should get the list of customers from within the project node and as a separate top_chat_customers query?

Any help is appreciated. Thanks!


Solution

  • The method resolve_top_chat_customers returns an iterable queryset, and not an individual Customer object, so in Query you need to specify that you're returning a list:

    top_chat_customers = graphene.List(CustomerNode, project_id=graphene.Int())
    

    Also, any annotated fields from the query aren't going to be part of the schema automatically. If you want to see them you'd need to add them explicitly:

    class CustomerNode(DjangoObjectType):
        last_message = graphene.String()
    
        class Meta:
            model = Customer
    
        def resolve_last_message(self, info):
            # Returns last message only if the object was annotated
            return getattr(self, 'last_message', None)