Graphene-django: Rest Framework SerializerMutation Error - NoneType is not callable

Created on 29 Aug 2017  路  11Comments  路  Source: graphql-python/graphene-django

Hi,
I'm using the SerializerMutation class to attempt to create mutations based off of my existing Rest Framework serializers. By taking advantage of the ModelSerializer in DRF, I hoped to avoid having to create all the mutations from scratch with validation and whatnot.

However, the use of the SerializerMutation class isn't sufficiently explained in the docs. I did this

class ItemMutation(SerializerMutation):
    class Meta:
        serializer_class = ItemSerializer

class Mutation(AbstractType):
    #...other mutations...
    createItem = ItemMutation.Field()

from a simple DRF ModelSerializer

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'id',
            'name',
            'description',
            'website',
        )

But Graphiql shows me the following error whenever I try to use the mutation

mutation newItem($data: ItemMutationInput!) {
  createItem(input: $data) {
    name,
    description
  }
}

{
  "data": {
    "name": "Blah",
    "description": "Blah blah blah",
    "website": "https://blah.blah.blah"
  }
}
{
  "errors": [
    {
      "message": "'NoneType' object is not callable",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ]
    }
  ],
  "data": {
    "createItem": null
  }
}

Also, Rest Framework's serializers can often have multiple actions (mutations) like create, delete, update, etc and the use of SerializerMutation appears to just produce a single mutation - presumably the "create" action. How do I handle the other actions?

Most helpful comment

@abdulhaq-e there was no release of 1.x with SerializerMutation. It was added in July and the most recent 1.x release was 1.3 in April.

For anyone who is interested I opened #261 to try to get SerializerMutation working again for 2.x

All 11 comments

The only way I have found SerializerMutation to work is for serializer_class to not be a ModelSerializer.

That looks something like this:

class ItemMutation(SerializerMutation):
    class Meta:
        serializer_class = ItemSerializer

class Mutation(AbstractType):
    #...other mutations...
    createOrUpdateItem = ItemMutation.Field()

class ItemModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'id',
            'name',
            'description',
            'website',
        )

class ItemSerializer(serializers.Serializer):
    item = ItemModelSerializer()

    def create(self, validated_data):
        item_data = validated_data.pop('item')
        return {'item': Item.objects.create(**item_data)}

    # likely want to implement update too

On the upside containing all the item fields in an item object does seem a bit cleaner to me and may leave better room for future schema additions. SerializerMutation calls save() so that can cover updates too.

@patrick91 or @spockNinja in case either of you have time to chime in

Thanks. I just tried it out, but quickly ran into another brick wall. The schema definition for ItemMutationInput now has only the field clientMutationId. I agree it's a little cleaner to have all the item fields in an item object when retrieving but how would one specify the item fields like name and description, if trying to create an item?

The following mutation request for example wouldn't work.

mutation newItem($data: ItemMutationInput!) {
  createOrUpdateItem(input: $data) {
    item{
      name,
      description
    }
  }
}

{
  "data": {
    "item": {
      "name": "Blah",
      "description": "Blah blah blah",
      "website": "https://blah.blah.blah"
    }  
  }
}

GraphQL expects my ItemMutationInput to have only a single field clientMutationId. Could you provide a sample mutation request?

That request works for me as long as ItemMutationInput is ItemSerializerInput so it matches the name of the Serializer rather than the SerializerMutation. clientMutationId makes it sound like you have ClientIDMutation in play somewhere

Ahh looks like that is likely coming from 48bcccd. I'm not using that yet. Seems like things may have changed in newer versions

I'm still fairly new to graphene, I should add. Where does ItemSerializerInput come from? It's certainly not in my schema definition. Using it in my mutation request causes Graphiql to throw the following
Unknown type "ItemSerializerInput". Perhaps you meant "ItemMutationInput"?

Was that functionality also possibly removed in the newer versions? Or is there a way to add the serializers themselves to the schema definition?
Thanks again for taking the time.

I'm new too and running into the same issues as you. You can ignore ItemSerializerInput as that seems to have also changed in recent commits.

I played around with the most recent code and it seems like SerializerMutation may simply be broken. It doesn't look like the handling of input fields is complete:

https://github.com/graphql-python/graphene-django/blob/9cac83b1688f3f608fa22fe2e95e2dd8145ccce1/graphene_django/rest_framework/serializer_converter.py#L43-L50

If you don't need to use any of the latest unreleased features you could try git+https://github.com/graphql-python/graphene-django@cec1a8448048ce55a2844df63b295b8d9d5000d7 -- that's after SerializerMutation was added but before the changes for graphene 2.0

Thanks very much for all your help. You saved me a lot of time and effort. I would have kept running in circles. GraphQL was just an experiment for me to see if I could upgrade my existing DRF projects. But it doesn't appear to be as production ready as I'd hoped, so I'll suspend my curiosity. I'll stick with just DRF for now.

@khalibloo Using DRF serializers as mutations in Graphene used to work for Graphene v1.0. As @syrusakbary is not the main contributor to the conversion of DRF serializers, I would say the main contributor (@patrick91) will make the conversion fully compatible with v2.0 which was only released lately.

Oh thanks for clearing that up. I already tried using v1.0. I get an error saying No module named 'graphene_django.rest_framework'. I checked the site-packages folder and yeah there's no rest_framework folder. Since SerializerMutation resides in the rest_framework folder, it appears it's not been implemented in v1.0. Even v1.3 doesn't have the rest_framework folder.

I remember while following the docs on SerializerMutation, I had to manually download this repo as well as its graphql dependencies to get SerializerMutation.

There must've been something wrong with publishing. I didn't experience it because I had a similar scenario as yours where I had a local copy of graphene-django in my app rather than the version on PyPy.

@abdulhaq-e there was no release of 1.x with SerializerMutation. It was added in July and the most recent 1.x release was 1.3 in April.

For anyone who is interested I opened #261 to try to get SerializerMutation working again for 2.x

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StefanoSega picture StefanoSega  路  4Comments

Dawidpol picture Dawidpol  路  4Comments

dan-klasson picture dan-klasson  路  4Comments

khankuan picture khankuan  路  4Comments

nickhudkins picture nickhudkins  路  3Comments