jmunsch
jmunsch

Reputation: 24119

Return any data from a query using GraphQL, Graphene and Python

I am receiving the following error:

{
  "errors": [
    {
      "message": "Unknown argument \"project_id\" on field" +
                 \"get_project_detail_summary\" of type \"Query\".",
      "locations": [
        {
          "line": 2,
          "column": 30
        }
      ]
    }
  ]
}

With the following query:

query GetProjectDetailSummary($project_id: Int) {
  get_project_detail_summary(project_id: $project_id) {
    comments {
      ... on ManagerCommentNode {
        id
        text
        created
      }
      ... on VendorCommentNode {
        id
        text
        created
      }
      ... on TenantCommentNode {
        id
        text
        created
      }
    }
  }
}

With the following backend code, How can I get to the breakpoint?, or how do I send back custom data given a number?:

class CommentsUnion(graphene.types.union.Union):
    class Meta:
        name = 'CommentsUnion'
        types = (ManagerCommentNode, VendorCommentNode, TenantCommentNode, )


class ProjectSummaryInput(graphene.InputObjectType):
    project_id = graphene.Int()


class ProjectSummaryNode(graphene.ObjectType):
    Input = ProjectSummaryInput

    project_id = graphene.Int()
    comments = graphene.List(CommentsUnion)

    @classmethod
    def resolve_comments(self, *args, **kwargs):
        import pdb;pdb.set_trace()
        return ProjectSummary.select_related('comments').objects.filter(comments__created__lt=dt)

class Query(graphene.ObjectType):
    get_project_detail_summary = Field(ProjectSummaryNode)

Upvotes: 1

Views: 3753

Answers (1)

jmunsch
jmunsch

Reputation: 24119

In regards to the error.

Be sure to add a kwarg ( e.g. project_id in this example, it is the reason for the "unknown argument on field" error ) to the graphene.Field for get_project_detail_summary.

Like so:

class Query(graphene.ObjectType):
    get_project_detail_summary = Field(ProjectSummaryNode, # see below for example
                                       project_id=graphene.Int() # kwarg here
                                      )

    def resolve_get_project_detail_summary(self, info, **kwargs):
        return ProjectSummary.objects.get(id=kwargs.get('project_id'))

In regards to returning any data.

This is a way ( returning a graphene.String ), but it untypes the response by putting everything inside a String:


from django.core.serializers.json import DjangoJSONEncoder

class ProjectSummaryRecentUpdatesNode(graphene.ObjectType):
    Input = ProjectSummaryInput
    recent_updates = graphene.String()

    def resolve_recent_updates(self, resolve, **kwargs):
        instance = Project.objects.get(id=resolve.variable_values.get("project_id"))
        things = instance.things.all()
        # these are all from different models, and the list is a bit longer than this.
        querysets = (
            ("scheduled", get_scheduled(things, resolve, **kwargs)),
            ("completed", get_completed(things, resolve, **kwargs)),
            ("invoices", get_invoices(things, resolve, **kwargs)),
            ("expenditures", get_expenditures(things, resolve, **kwargs)),
            ("comments", get_comments(things, resolve, **kwargs)),
            ("files", get_files(things, resolve, **kwargs)),
        )
        recent_updates = []
        for update_type, qs in querysets:
            for item in qs:
                item.update(
                    {
                        "recent_update_type": update_type
                    }
                )
                recent_updates.append(item)
        return json.dumps(recent_updates, cls=DjangoJSONEncoder)

And on the frontend, with an import { Query } from "react-apollo"; element we can JSON.parse the field ... which has "any" (json serialized) data inside of it:

<Query
    query={GET_PROJECT_DETAIL_SUMMARY_RECENT_UPDATES}
    fetchPolicy="network-only"
    variables={{ project_id: this.props.projectId }}
>
    {({ loading, error, data }: QueryResult) => {
        if (
            data &&
            data.get_project_detail_summary_recent_updates &&
            data.get_project_detail_summary_recent_updates.recent_updates
        ) {
            console.log(JSON.parse(data.get_project_detail_summary_recent_updates.recent_updates))
        }    
    }}
</Query>

Side note:

If there isn't a large list of data types create a Union like object which has all of the fields needed from different models, or an actual Union, which seems like the correct way, as then types are not lost:

class ProjectSummaryNode(graphene.ObjectType):
    # from FileNode graphene.ObjectType class
    file__id = graphene.Int()
    file__brief_description = graphene.String()
    file_count = graphene.Int()
    last_file_created = graphene.String()
    last_file = graphene.String()

    # from UploaderNode graphene.ObjectType class
    uploader__first_name = graphene.String()
    uploader__last_name = graphene.String()

    # from CommentNode graphene.ObjectType class
    comment = graphene.String()

    class Meta:
        name = "ProjectSummaryNode"
        # example of Union, the above fields would be commented out, as they are part of 
        # the different graphene.ObjectType classes, and this line below would be uncommented
        # types = ( FileNode, UploaderNode, CommentNode, )

Still open to suggestions on a better way to do this.

Upvotes: 1

Related Questions