Mutations - batch creation of objects
Asked Answered
C

4

7

I want to use graphene to create many people in one go. The document only mention the way to create one person like this:

class CreatePerson(graphene.Mutation):
class Input:
    name = graphene.String()
    age = graphene.Int()

ok = graphene.Boolean()
person = graphene.Field(lambda: Person)

@staticmethod
def mutate(root, args, context, info):
    person = Person(name=args.get('name'), age=args.get('age'), mobile=args.get('mobile'))
    ok = True
    return CreatePerson(person=person, ok=ok)

are there any ways to get it done?

Captious answered 19/5, 2017 at 12:44 Comment(1)
FYI you can post your solution as an answer and accept it so people coming after you can easily see the final solution.Chose
C
5

I can figure out a solution base on the answer of Jan Hančič

There is a type called graphene.InputObjectType to use in this case

The solution can be

class PersonInput(InputObjectType):
    name = graphene.String()
    age = graphene.Int()

class CreatePeople(graphene.Mutation):
    class Input:
       people = graphene.List(PersonInput)

    people = graphene.List(lambda: Person)

    @staticmethod
    def mutate(root, args, context, info):
        people = [Person.objects.create(name=person.name, age=person.age) for person in args.get('people')]
        return CreatePeople(people=people)
Captious answered 13/1, 2018 at 4:43 Comment(2)
I created a mutation this way but I can't find a way how to write a graphql query for this. How do you do that?Porscheporsena
@KarinaKlinkevičiūtė you can find the answer here - docs.graphene-python.org/projects/django/en/latest/queriesCaptious
Z
9

Instead of using a mutation that creates a list of objects, you can also call a mutation that creates one objects multiple times in one GraphQL request. This is accomplished using GraphQL Aliases:

mutation {
  c001: createPerson(
    name: "Donald Duck"
    age: 42
  ) {
    id
  }

  c002: createPerson(
    name: "Daisy Duck"
    age: 43
  ) {
    id
  }

  c003: createPerson(
    name: "Mickey Mouse"
    age: 44
  ) {
    id
  }
}
Zilvia answered 20/5, 2017 at 12:38 Comment(2)
the link to the FAQ is dead.Immunotherapy
which one is better, the batch of list or batch of alias?Separatist
C
5

I can figure out a solution base on the answer of Jan Hančič

There is a type called graphene.InputObjectType to use in this case

The solution can be

class PersonInput(InputObjectType):
    name = graphene.String()
    age = graphene.Int()

class CreatePeople(graphene.Mutation):
    class Input:
       people = graphene.List(PersonInput)

    people = graphene.List(lambda: Person)

    @staticmethod
    def mutate(root, args, context, info):
        people = [Person.objects.create(name=person.name, age=person.age) for person in args.get('people')]
        return CreatePeople(people=people)
Captious answered 13/1, 2018 at 4:43 Comment(2)
I created a mutation this way but I can't find a way how to write a graphql query for this. How do you do that?Porscheporsena
@KarinaKlinkevičiūtė you can find the answer here - docs.graphene-python.org/projects/django/en/latest/queriesCaptious
P
4

Make your mutation input a list and return a list of created people. Something like this:

class CreatePerson(graphene.Mutation):
    class Input:
        name = graphene.List(graphene.String)

    ok = graphene.Boolean()
    people = graphene.List(Person)

    @staticmethod
    def mutate(root, args, context, info):
        people = [Person(name=name) for name in args.get('name)]
        ok = True
        return CreatePerson(people=people, ok=ok)
Predesignate answered 19/5, 2017 at 13:17 Comment(3)
great! but how can I deal with more than one field. E.g. name, address and age for the person?Captious
You can have complex input types, see the docs: docs.graphene-python.org/en/latest/types/mutationsChose
Hi Jan, I use the InputObject and it worked but the data was not saved to the db. Would you mind helping on this. Thank you very much!Captious
C
4

Receive a list of input, create all instances and return them all

The model node/type should be like-

class UserType(DjangoObjectType):

    class Meta:
        model = User
        interfaces = (CustomGrapheneNode, )
        filter_fields = {}
        only_fields = (
            'name',
            'email'
        )

Define Input fields

class UserInput(graphene.InputObjectType):
    name = graphene.String(required=True)
    password = graphene.String(required=True)

Mutation class

class CreateUser(graphene.Mutation):
    users = graphene.List(UserType)

    class Input:
        data = graphene.List(UserInput)

    Output = graphene.List(UserType)

    def mutate(self, info, data):
        users = []
        for item in data:
            user = User.objects.create(name=data['name'], 
                                       password=data['password'])
            users.append(user)
        return users

make this mutation callable by main schema

class Mutation():
    create_user = CreateUser.Field()

the Mutation Query view will be as -

mutation{
    createUser(data:[{name:"john", password:"1234"},
                     {name:"john", password:"1234"}]) {
        user{
            name
        }
    }    
}
Cung answered 10/1, 2018 at 9:22 Comment(2)
good solution. Just to add you could use the bulk_create() method there in the mutate() function as described in the docs: docs.djangoproject.com/en/3.2/ref/models/querysetsMight
in case someone else tries this answer and you run into error, in the mutate function just change for item in data: user = User.objects.create(name=data['name'], password=data['password']) to instead: for item in data: user = User.objects.create(name=item['name'], password=item['password'])Might

© 2022 - 2024 — McMap. All rights reserved.