graphene-sqlalchemy icon indicating copy to clipboard operation
graphene-sqlalchemy copied to clipboard

Nested inputs in mutations

Open NathanBWaters opened this issue 7 years ago • 2 comments

I'm failing to convert nested inputs into sqlalchemy models in mutations. Here's my example:

Let's say I want to create a quiz. For that I have the following code:

'''
GraphQL Models
'''
class Quiz(SQLAlchemyObjectType):
    '''
    GraphQL representation of a Quiz
    '''
    class Meta:
        model = QuizModel


class Question(SQLAlchemyObjectType):
    '''
    GraphQL representation of a Question
    '''
    class Meta:
        model = QuestionModel


'''
Inputs
'''
class QuestionInput(graphene.InputObjectType):
    text = graphene.String()
    media = graphene.String()


class QuizInput(graphene.InputObjectType):
    name = graphene.String()
    creator_id = graphene.Int()
    description = graphene.String()
    media = graphene.String()
    id = graphene.Int()
    debugging_questions = graphene.InputField(graphene.List(QuestionInput))
    questions = graphene.InputField(graphene.List(QuestionInput))


'''
Mutation
'''
class CreateQuiz(graphene.Mutation):
    class Arguments:
        quiz = QuizInput(required=True)

    quiz = graphene.Field(lambda: Quiz)

    def mutate(self, info, **kwargs):
        quiz_attributes = dict(kwargs['quiz'])
        if quiz_attributes.get('id'):
            quiz = db_session.query(QuizModel).filter((QuizModel.id == quiz_attributes.get('id'))).one()
            quiz_attributes.pop('id')
            for key, value in quiz_attributes.items():
                setattr(quiz, key, value)
        else:
            quiz = QuizModel(**quiz_attributes)

        db_session.add(quiz)
        db_session.commit()

        return CreateQuiz(quiz=quiz)

My GraphQL query is the following:

mutation creatingQuiz($quiz: QuizInput!) {
	createQuiz(quiz: $quiz) {
		quiz {
			id,
			name,
			description,
			media,
			creatorId,
			questions {
				id,
				text,
				media,
			}
		}
	}
}

Note the relation between the global variables and the response: Example A - returns a response, obviously doesn't add any quizzes because it uses debuggingQuestions.

Global variables:
{
	"quiz": {
		"id": 136,
		"name": "fake name",
		"description": "simple desc",
		"creatorId": 1,
		"media": "img.jpg",
		"debuggingQuestions": [{
			"media": "media",
			"text": "text"
		}]
	}
}

Response:
{
	"data": {
		"createQuiz": {
			"quiz": {
				"id": "136",
				"name": "fake name",
				"description": "simple desc",
				"media": "img.jpg",
				"creatorId": 1,
				"questions": []
			}
		}
	}
}

Now if I try and pass question data in the questions field instead of debuggingQuestions:

Global variables:
{
	"quiz": {
		"id": 136,
		"name": "fake name",
		"description": "simple desc",
		"creatorId": 1,
		"media": "img.jpg",
		"questions": [{
			"media": "media",
			"text": "text"
		}]
	}
}

Response:
{
	"errors": [{
		"message": "unhashable type: 'QuestionInput'",
		"locations": [{
			"line": 2,
			"column": 3
		}]
	}],
	"data": {
		"createQuiz": null
	}
}

What step am I missing so that QuestionInput is automatically converted into a Question sqlalchemy model?

NathanBWaters avatar May 22 '18 19:05 NathanBWaters

Also, all feedback welcome on how to improve the code.

NathanBWaters avatar May 22 '18 19:05 NathanBWaters

Hmm I'm confused. It seems you can have nested inputs but not nested mutations. https://github.com/graphql/graphql-js/issues/672

NathanBWaters avatar May 22 '18 19:05 NathanBWaters