Graphene: Using the same type for querying and mutation

Created on 8 Mar 2016  路  14Comments  路  Source: graphql-python/graphene

Today I learned that to allow a custom type as mutation argument, it needs to inherit from InputObjectType and it's fields need to be of InputField type.

I think this requirement is forcing duplicating of types. What I mean is, say I have a Person type as:

class Person(ObjectType):
    id = Int()
    first_name = String()
    last_name = String()
    address = Field(SomeOtherType)

I expose it as a query-able object as well as return it as a result.

Now, If I want to allow it as a mutation argument to update a Person's details, I will have to redefine it as something else:

class Person_1(InputObjectType):
    id = Int()
    first_name = String()
    last_name = String()
    address = InputField(SomeOtherType)

If this is the only way to do this, I think it is forcing me to define near duplicate types. Is there a way to work around this?

Can something be done about this in the code so that we can use the same type definition for querying as well as a mutation argument?

Most helpful comment

Can you re-open this!! I agree that an AbstractType or similar needs to be able to be used to define both an Object and an InputObject. Noting that the AbstractType is now deprectaed at the least this should be possible from an Interface

All 14 comments

I think so.

My object is defined with SQLAlchemy model:

class BlogArticle(BlogNode):

    class Meta:
        model = models.BlogArticle
        only_fields = ('uid', 'title', 'abstract', 'body', 'body_markup',
                       'status', 'vote_up', 'vote_down', 'view_count', 'comment_count',
                       'is_public', 'created', 'updated')

My mutation defined:

class BlogArticleNew(relay.ClientIDMutation):

    class Input:
        category_uid = graphene.String(description=_("Category UID"))
        catalog_uid = graphene.String(description=_("Catalog UID"))
        title = graphene.String(description=_("Title"), required=True)
        abstract = graphene.String(description=_("Abstract"))
        body = graphene.String(description=_("Body"))
        body_markup = graphene.Int(description=_("Body Markup"))
        is_public = graphene.Boolean(description=_("Is Public"))
        tags = graphene.List(graphene.String())

I thought that:

  1. Every field in class Input would be needed with custom description/doc. Such as title - The title about article. Needed!
  2. class Input could have a custom validation.
  3. Usually, query object is defined with ORM(Django Model / SQLAlchemy)

Could both the InputObjectType and the ObjectType inherit from the same Interface?

By definition, only GraphQL ObjectTypes can inherit from an Interface, so is not possible.
But I think it could be reasonable to add other abstraction so they can both (InputObjectType and ObjectType) inherit from it.

In 1.0 I added the AbstractType type that could be inherited from InputObjectType, Interface, ObjectType and Mutation (Mutation.Input too). This way we can reuse and share fields more easily.

Here's the documentation for it!
http://docs.graphene-python.org/en/latest/types/abstracttypes/

Closing the issue.

Hello @syrusakbary, this really works for single level fields.
Please, how do I nest one inside another?

Let's say I have:

class PersonFields(graphene.AbstractType):
    name = graphene.String()
    age = graphene.Int()

class PersonContactFields(graphene.AbstractType):
    phone = graphene.Int()

How do I fit they together?

class PersonFields(graphene.AbstractType):
    name = graphene.String()
    age = graphene.Int()
    contact = graphene.??????(PersonContactFields)

Did you checked the document ?
(http://docs.graphene-python.org/en/latest/types/mutations/)

import graphene

class LatLngInput(graphene.InputObjectType):
    lat = graphene.Float()
    lng = graphene.Float()

#A location has a latlng associated to it
class LocationInput(graphene.InputObjectType):
    name = graphene.String()
    latlng = graphene.InputField(LatLngInput)

Thanks @valdergallo, but that way you loose the ability to share types, which is the intent of this thread.
It would be needed to maintain it as an AbstractType, to be able to inherit it both from InputType and ObjectType.

Unfortunately I think this is not yet possible.
@syrusakbary Can you confirm please?

InputObjectType and ObjectType are two completely different types in GraphQL, and can't be shared there. Therefore, a type for each needs to be created in order to use it as input, or output type.

Yes, I know that and agree, but that's why you did create AbstractType, isn't it?
The only problem now is that one can't inherit from it without materializing it... That way we could embed types and still share them.

This conversation shouldn't be closed. @syrusakbary the graphene documentation says that:

Each attribute of the AbstractType represents a field (a graphene.Field or graphene.InputField depending on where it is mounted)

This is the opposite of the behavior defined. You should be able to make an AbstractType whose fields are either Field or InputField depending on the mount point?

Can you re-open this!! I agree that an AbstractType or similar needs to be able to be used to define both an Object and an InputObject. Noting that the AbstractType is now deprectaed at the least this should be possible from an Interface

Is there a way to use a interface-like type with InputObjects currently or are there any plans for it in the near future?

I've also arrived here after searching for a way to do this. As things are, I've just had to duplicate an entire nested set of object definitions purely so I can use it for both queries and mutations.

I think this is described here, basically just create a common class and inherit from it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghoshabhi picture ghoshabhi  路  3Comments

mingzhou picture mingzhou  路  3Comments

danpalmer picture danpalmer  路  4Comments

dfee picture dfee  路  4Comments

junchiz picture junchiz  路  3Comments