4
votes

I am building a GraphQL API using the python packages Flask, SQLAlchemy, Graphene and Graphene-SQLAlchemy. I have followed the SQLAlchemy + Flask Tutorial. I am able to execute queries and mutations to create records. Now I would like to know what is the best way to update an existing record.

Here is my current script schema.py:

from graphene_sqlalchemy import SQLAlchemyObjectType
from database.batch import BatchOwner as BatchOwnerModel
import api_utils  # Custom methods to create records in database
import graphene


class BatchOwner(SQLAlchemyObjectType):
    """Batch owners."""
    class Meta:
        model = BatchOwnerModel
        interfaces = (graphene.relay.Node,)


class CreateBatchOwner(graphene.Mutation):
    """Create batch owner."""
    class Arguments:
        name = graphene.String()

    # Class attributes
    ok = graphene.Boolean()
    batch_owner = graphene.Field(lambda: BatchOwner)

    def mutate(self, info, name):
        record = {'name': name}
        api_utils.create('BatchOwner', record) # Custom methods to create records in database
        batch_owner = BatchOwner(name=name)
        ok = True
        return CreateBatchOwner(batch_owner=batch_owner, ok=ok)


class Query(graphene.ObjectType):
    """Query endpoint for GraphQL API."""
    node = graphene.relay.Node.Field()
    batch_owner = graphene.relay.Node.Field(BatchOwner)
    batch_owners = SQLAlchemyConnectionField(BatchOwner)


class Mutation(graphene.ObjectType):
    """Mutation endpoint for GraphQL API."""
    create_batch_owner = CreateBatchOwner.Field()


schema = graphene.Schema(query=Query, mutation=Mutation)

Remarks:

  • My object BatchOwner has only 2 attributes (Id, name)
  • To be able to update the BatchOwner name, I assume I need to provide the database Id (not the relay global Id) as an input argument of some update method
  • But when I query for a BatchOwner from my client, Graphene only returns me the global Id which is base64 encoded (example: QmF0Y2hPd25lcjox, which correspond to BatchOwner:1)

Example of response:

{
    "data": {
        "batchOwners": {
            "edges": [
                {
                    "node": {
                        "id": "QmF0Y2hPd25lcjox",
                        "name": "Alexis"
                    }
                }
            ]
        }
    }
}

The solution I am thinking of at the moment would be:

  • Create an update mutation which takes the global Id as an argument
  • Decode the global Id (how?)
  • Use the database Id retrieved from the decoded global Id to query on the database and update the corresponding record

Is there a better way to do this?

1

1 Answers

6
votes

I have found a solution using the method from_global_id (documented here)

from graphql_relay.node.node import from_global_id

I added the following class to schema.py:

class UpdateBatchOwner(graphene.Mutation):
    """Update batch owner."""
    class Arguments:
        id = graphene.String()
        name = graphene.String()

    # Class attributes
    ok = graphene.Boolean()
    batch_owner = graphene.Field(lambda: BatchOwner)

    def mutate(self, info, id, name):
        id = from_global_id(id)
        record = {'id': id[1], 'name': name}
        api_utils.update('BatchOwner', record)
        batch_owner = BatchOwner(id=id, name=name)
        ok = True
        return UpdateBatchOwner(batch_owner=batch_owner, ok=ok)

And I updated the Mutation class:

class Mutation(graphene.ObjectType):
    """Mutation endpoint for GraphQL API."""
    create_batch_owner = CreateBatchOwner.Field()
    update_batch_owner = UpdateBatchOwner.Field()

I'm wondering if there is a more straight forward way to do this?