Merge pull request #771 from jkimbo/update-interface-documentation

Update interface documentation
This commit is contained in:
Syrus Akbary 2018-06-18 13:33:59 -07:00 committed by GitHub
commit 9efdf4c46e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 142 additions and 32 deletions

View File

@ -8,9 +8,9 @@ Types Reference
enums enums
scalars scalars
list-and-nonnull list-and-nonnull
interfaces
abstracttypes
unions
objecttypes objecttypes
interfaces
unions
schema schema
mutations mutations
abstracttypes

View File

@ -1,60 +1,170 @@
Interfaces Interfaces
========== ==========
An Interface contains the essential fields that will be implemented by An *Interface* is an abstract type that defines a certain set of fields that a
multiple ObjectTypes. type must include to implement the interface.
The basics: For example, you can define an Interface ``Character`` that represents any
character in the Star Wars trilogy:
- Each Interface is a Python class that inherits from ``graphene.Interface``.
- Each attribute of the Interface represents a GraphQL field.
Quick example
-------------
This example model defines a ``Character`` interface with a name. ``Human``
and ``Droid`` are two implementations of that interface.
.. code:: python .. code:: python
import graphene import graphene
class Character(graphene.Interface): class Character(graphene.Interface):
name = graphene.String() id = graphene.ID(required=True)
name = graphene.String(required=True)
friends = graphene.List(lambda: Character)
Any ObjectType that implements ``Character`` will have these exact fields, with
these arguments and return types.
For example, here are some types that might implement ``Character``:
.. code:: python
# Human is a Character implementation
class Human(graphene.ObjectType): class Human(graphene.ObjectType):
class Meta: class Meta:
interfaces = (Character, ) interfaces = (Character, )
born_in = graphene.String() starships = graphene.List(Starship)
home_planet = graphene.String()
# Droid is a Character implementation
class Droid(graphene.ObjectType): class Droid(graphene.ObjectType):
class Meta: class Meta:
interfaces = (Character, ) interfaces = (Character, )
function = graphene.String() primary_function = graphene.String()
``name`` is a field on the ``Character`` interface that will also exist on both Both of these types have all of the fields from the ``Character`` interface,
the ``Human`` and ``Droid`` ObjectTypes (as those implement the ``Character`` but also bring in extra fields, ``home_planet``, ``starships`` and
interface). Each ObjectType may define additional fields. ``primary_function``, that are specific to that particular type of character.
The above types have the following representation in a schema: The full GraphQL schema defition will look like this:
.. code:: .. code::
interface Character { interface Character {
name: String id: ID!
} name: String!
friends: [Character]
type Droid implements Character {
name: String
function: String
} }
type Human implements Character { type Human implements Character {
name: String id: ID!
bornIn: String name: String!
friends: [Character]
starships: [Starship]
homePlanet: String
} }
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
primaryFunction: String
}
Interfaces are useful when you want to return an object or set of objects,
which might be of several different types.
For example, you can define a field ``hero`` that resolves to any
``Character``, depending on the episode, like this:
.. code:: python
class Query(graphene.ObjectType):
hero = graphene.Field(
Character,
required=True,
episode=graphene.Int(required=True)
)
def resolve_hero(_, info, episode):
# Luke is the hero of Episode V
if episode == 5:
return get_human(name='Luke Skywalker')
return get_droid(name='R2-D2')
schema = graphene.Schema(query=Query, types=[Human, Droid])
This allows you to directly query for fields that exist on the Character interface
as well as selecting specific fields on any type that implments the interface
using `inline fragments <https://graphql.org/learn/queries/#inline-fragments>`_.
For example, the following query:
.. code::
query HeroForEpisode($episode: Int!) {
hero(episode: $episode) {
__typename
name
... on Droid {
primaryFunction
}
... on Human {
homePlanet
}
}
}
Will return the following data with variables ``{ "episode": 4 }``:
.. code:: json
{
"data": {
"hero": {
"__typename": "Droid",
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
And different data with the variables ``{ "episode": 5 }``:
.. code:: json
{
"data": {
"hero": {
"__typename": "Human",
"name": "Luke Skywalker",
"homePlanet": "Tatooine"
}
}
}
Resolving data objects to types
-------------------------------
As you build out your schema in Graphene it's common for your resolvers to
return objects that represent the data backing your GraphQL types rather than
instances of the Graphene types (e.g. Django or SQLAlchemy models). This works
well with ``ObjectType`` and ``Scalar`` fields, however when you start using
Interfaces you might come across this error:
.. code::
"Abstract type Character must resolve to an Object type at runtime for field Query.hero ..."
This happens because Graphene doesn't have enough information to convert the
data object into a Graphene type needed to resolve the ``Interface``. To solve
this you can define a ``resolve_type`` class method on the ``Interface`` which
maps a data object to a Graphene type:
.. code:: python
class Character(graphene.Interface):
id = graphene.ID(required=True)
name = graphene.String(required=True)
@classmethod
def resolve_type(cls, instance, info):
if instance.type == 'DROID':
return Droid
return Human