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
scalars
list-and-nonnull
interfaces
abstracttypes
unions
objecttypes
interfaces
unions
schema
mutations
abstracttypes

View File

@ -1,60 +1,170 @@
Interfaces
==========
An Interface contains the essential fields that will be implemented by
multiple ObjectTypes.
An *Interface* is an abstract type that defines a certain set of fields that a
type must include to implement the interface.
The basics:
- 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.
For example, you can define an Interface ``Character`` that represents any
character in the Star Wars trilogy:
.. code:: python
import graphene
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 Meta:
interfaces = (Character, )
born_in = graphene.String()
starships = graphene.List(Starship)
home_planet = graphene.String()
# Droid is a Character implementation
class Droid(graphene.ObjectType):
class Meta:
interfaces = (Character, )
function = graphene.String()
primary_function = graphene.String()
``name`` is a field on the ``Character`` interface that will also exist on both
the ``Human`` and ``Droid`` ObjectTypes (as those implement the ``Character``
interface). Each ObjectType may define additional fields.
Both of these types have all of the fields from the ``Character`` interface,
but also bring in extra fields, ``home_planet``, ``starships`` and
``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::
interface Character {
name: String
}
type Droid implements Character {
name: String
function: String
id: ID!
name: String!
friends: [Character]
}
type Human implements Character {
name: String
bornIn: String
id: ID!
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