mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-26 03:23:55 +03:00
Merge pull request #771 from jkimbo/update-interface-documentation
Update interface documentation
This commit is contained in:
commit
9efdf4c46e
|
@ -8,9 +8,9 @@ Types Reference
|
|||
enums
|
||||
scalars
|
||||
list-and-nonnull
|
||||
interfaces
|
||||
abstracttypes
|
||||
unions
|
||||
objecttypes
|
||||
interfaces
|
||||
unions
|
||||
schema
|
||||
mutations
|
||||
abstracttypes
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user