mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-29 04:53:55 +03:00
Improved types as containers
This commit is contained in:
parent
2958cc18af
commit
129999d41a
72
README.md
72
README.md
|
@ -1,10 +1,10 @@
|
|||
# ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
|
||||
# ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene) [![PyPI version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
|
||||
|
||||
|
||||
Graphene is a Python library for building GraphQL schemas/types fast and easily.
|
||||
* **Easy to use:** It maps the models/fields to internal GraphQL objects without effort.
|
||||
* **Relay:** Graphene has builtin support for Relay
|
||||
* **Django:** Automatic [Django models](#djangorelay-schema) conversion. *See an [example Django](http://github.com/graphql-python/swapi-graphene) implementation*
|
||||
* **Django:** Automatic *Django model* mapping to Graphene Types. *See an [example Django](http://github.com/graphql-python/swapi-graphene) implementation*
|
||||
|
||||
|
||||
## Installation
|
||||
|
@ -16,26 +16,21 @@ pip install graphene
|
|||
```
|
||||
|
||||
|
||||
## Usage
|
||||
## Examples
|
||||
|
||||
Example code of a GraphQL schema using Graphene:
|
||||
|
||||
### Schema definition
|
||||
Here is one example for get you started:
|
||||
|
||||
```python
|
||||
class Character(graphene.Interface):
|
||||
id = graphene.IDField()
|
||||
name = graphene.StringField()
|
||||
friends = graphene.ListField('self')
|
||||
|
||||
def resolve_friends(self, args, *_):
|
||||
return [Human(f) for f in self.instance.friends]
|
||||
|
||||
class Human(Character):
|
||||
homePlanet = graphene.StringField()
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
human = graphene.Field(Human)
|
||||
hello = graphene.StringField(description='A typical hello world')
|
||||
ping = graphene.StringField(description='Ping someone',
|
||||
to=graphene.Argument(graphene.String))
|
||||
|
||||
def resolve_hello(self, args, info):
|
||||
return 'World'
|
||||
|
||||
def resolve_ping(self, args, info):
|
||||
return 'Pinging {}'.format(args.get('to'))
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
||||
```
|
||||
|
@ -44,48 +39,19 @@ Then Querying `graphene.Schema` is as simple as:
|
|||
|
||||
```python
|
||||
query = '''
|
||||
query HeroNameQuery {
|
||||
hero {
|
||||
name
|
||||
}
|
||||
query SayHello {
|
||||
hello
|
||||
ping(to:'peter')
|
||||
}
|
||||
'''
|
||||
result = schema.execute(query)
|
||||
```
|
||||
|
||||
### Relay Schema
|
||||
If you want to learn even more, you can also check the following examples:
|
||||
|
||||
Graphene also supports Relay, check the [Starwars Relay example](tests/starwars_relay)!
|
||||
* Relay Schema: [Starwars Relay example](tests/starwars_relay)
|
||||
* Django: [Starwars Django example](tests/starwars_django)
|
||||
|
||||
```python
|
||||
class Ship(relay.Node):
|
||||
name = graphene.StringField()
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id):
|
||||
return Ship(your_ship_instance)
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
ships = relay.ConnectionField(Ship)
|
||||
node = relay.NodeField()
|
||||
|
||||
```
|
||||
|
||||
### Django+Relay Schema
|
||||
|
||||
If you want to use graphene with your Django Models check the [Starwars Django example](tests/starwars_django)!
|
||||
|
||||
```python
|
||||
class Ship(DjangoNode):
|
||||
class Meta:
|
||||
model = YourDjangoModelHere
|
||||
# only_fields = ('id', 'name') # Only map this fields from the model
|
||||
# exclude_fields ('field_to_exclude', ) # Exclude mapping this fields from the model
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
node = relay.NodeField()
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -37,11 +37,20 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
|||
cls.add_to_class(field.name, converted_field)
|
||||
|
||||
|
||||
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
|
||||
class InstanceObjectType(BaseObjectType):
|
||||
def __init__(self, instance=None):
|
||||
self.instance = instance
|
||||
super(InstanceObjectType, self).__init__()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.instance, attr)
|
||||
|
||||
|
||||
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, InstanceObjectType)):
|
||||
pass
|
||||
|
||||
|
||||
class DjangoInterface(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
|
||||
class DjangoInterface(six.with_metaclass(DjangoObjectTypeMeta, InstanceObjectType)):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class Field(object):
|
|||
creation_counter = 0
|
||||
required = False
|
||||
|
||||
def __init__(self, field_type, name=None, resolve=None, required=False, args=None, description='', **extra_args):
|
||||
def __init__(self, field_type, name=None, resolve=None, required=False, args=None, description='', default=None, **extra_args):
|
||||
self.field_type = field_type
|
||||
self.resolve_fn = resolve
|
||||
self.required = self.required or required
|
||||
|
@ -38,9 +38,13 @@ class Field(object):
|
|||
self.name = name
|
||||
self.description = description or self.__doc__
|
||||
self.object_type = None
|
||||
self.default = default
|
||||
self.creation_counter = Field.creation_counter
|
||||
Field.creation_counter += 1
|
||||
|
||||
def get_default(self):
|
||||
return self.default
|
||||
|
||||
def contribute_to_class(self, cls, name, add=True):
|
||||
if not self.name:
|
||||
self.name = to_camel_case(name)
|
||||
|
@ -57,7 +61,7 @@ class Field(object):
|
|||
if resolve_fn:
|
||||
return resolve_fn(instance, args, info)
|
||||
else:
|
||||
return getattr(instance, self.field_name, None)
|
||||
return getattr(instance, self.field_name, self.get_default())
|
||||
|
||||
def get_resolve_fn(self, schema):
|
||||
object_type = self.get_object_type(schema)
|
||||
|
|
|
@ -129,29 +129,48 @@ class ObjectTypeMeta(type):
|
|||
|
||||
class BaseObjectType(object):
|
||||
|
||||
def __new__(cls, instance=None, **kwargs):
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._meta.is_interface:
|
||||
raise Exception("An interface cannot be initialized")
|
||||
if instance is None:
|
||||
if not kwargs:
|
||||
return None
|
||||
elif type(instance) is cls:
|
||||
return instance
|
||||
|
||||
if not args and not kwargs:
|
||||
return None
|
||||
return super(BaseObjectType, cls).__new__(cls)
|
||||
|
||||
def __init__(self, instance=None, **kwargs):
|
||||
signals.pre_init.send(self.__class__, instance=instance)
|
||||
assert instance or kwargs
|
||||
if not instance:
|
||||
init_kwargs = dict({k: None for k in self._meta.fields_map.keys()}, **kwargs)
|
||||
instance = self._meta.object(**init_kwargs)
|
||||
self.instance = instance
|
||||
signals.post_init.send(self.__class__, instance=self)
|
||||
def __init__(self, *args, **kwargs):
|
||||
signals.pre_init.send(self.__class__, args=args, kwargs=kwargs)
|
||||
args_len = len(args)
|
||||
fields = self._meta.fields
|
||||
if args_len > len(fields):
|
||||
# Daft, but matches old exception sans the err msg.
|
||||
raise IndexError("Number of args exceeds number of fields")
|
||||
fields_iter = iter(fields)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self.instance:
|
||||
return getattr(self.instance, name)
|
||||
if not kwargs:
|
||||
for val, field in zip(args, fields_iter):
|
||||
setattr(self, field.field_name, val)
|
||||
else:
|
||||
for val, field in zip(args, fields_iter):
|
||||
setattr(self, field.field_name, val)
|
||||
kwargs.pop(field.field_name, None)
|
||||
|
||||
for field in fields_iter:
|
||||
try:
|
||||
val = kwargs.pop(field.field_name)
|
||||
setattr(self, field.field_name, val)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if kwargs:
|
||||
for prop in list(kwargs):
|
||||
try:
|
||||
if isinstance(getattr(self.__class__, prop), property):
|
||||
setattr(self, prop, kwargs.pop(prop))
|
||||
except AttributeError:
|
||||
pass
|
||||
if kwargs:
|
||||
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
|
||||
|
||||
signals.post_init.send(self.__class__, instance=self)
|
||||
|
||||
@classmethod
|
||||
def fields_as_arguments(cls, schema):
|
||||
|
|
|
@ -32,21 +32,3 @@ def test_node_should_have_same_connection_always():
|
|||
|
||||
def test_node_should_have_id_field():
|
||||
assert 'id' in OtherNode._meta.fields_map
|
||||
|
||||
|
||||
# def test_field_no_contributed_raises_error():
|
||||
# with raises(Exception) as excinfo:
|
||||
# class Ship(graphene.ObjectType):
|
||||
# name = graphene.StringField()
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
|
||||
# class Faction(relay.Node):
|
||||
# name = graphene.StringField()
|
||||
# ships = relay.ConnectionField(Ship)
|
||||
# @classmethod
|
||||
# def get_node(cls):
|
||||
# pass
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
# assert 'same type_name' in str(excinfo.value)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from pytest import raises
|
||||
from graphql.core.type import (
|
||||
GraphQLNonNull,
|
||||
GraphQLID
|
||||
|
@ -10,11 +9,6 @@ from graphene import relay
|
|||
schema = graphene.Schema()
|
||||
|
||||
|
||||
class MyType(object):
|
||||
name = 'my'
|
||||
arg = None
|
||||
|
||||
|
||||
class MyConnection(relay.Connection):
|
||||
my_custom_field = graphene.StringField(resolve=lambda instance, *_: 'Custom')
|
||||
|
||||
|
@ -24,7 +18,7 @@ class MyNode(relay.Node):
|
|||
|
||||
@classmethod
|
||||
def get_node(cls, id):
|
||||
return MyNode(MyType())
|
||||
return MyNode(name='mo')
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
|
@ -32,10 +26,9 @@ class Query(graphene.ObjectType):
|
|||
all_my_nodes = relay.ConnectionField(MyNode, connection_type=MyConnection, customArg=graphene.Argument(graphene.String))
|
||||
|
||||
def resolve_all_my_nodes(self, args, info):
|
||||
t = MyType()
|
||||
custom_arg = args.get('customArg')
|
||||
assert custom_arg == "1"
|
||||
return [MyNode(t)]
|
||||
return [MyNode(name='my')]
|
||||
|
||||
schema.query = Query
|
||||
|
||||
|
@ -61,7 +54,7 @@ def test_nodefield_query():
|
|||
'''
|
||||
expected = {
|
||||
'myNode': {
|
||||
'name': 'my'
|
||||
'name': 'mo'
|
||||
},
|
||||
'allMyNodes': {
|
||||
'edges': [{
|
||||
|
|
|
@ -1,77 +1,78 @@
|
|||
from collections import namedtuple
|
||||
humanData = {}
|
||||
droidData = {}
|
||||
|
||||
Human = namedtuple('Human', 'id name friends appearsIn homePlanet')
|
||||
|
||||
luke = Human(
|
||||
id='1000',
|
||||
name='Luke Skywalker',
|
||||
friends=['1002', '1003', '2000', '2001'],
|
||||
appearsIn=[4, 5, 6],
|
||||
homePlanet='Tatooine',
|
||||
)
|
||||
def setup():
|
||||
from .schema import Human, Droid
|
||||
global humanData, droidData
|
||||
luke = Human(
|
||||
id='1000',
|
||||
name='Luke Skywalker',
|
||||
friends=['1002', '1003', '2000', '2001'],
|
||||
appears_in=[4, 5, 6],
|
||||
home_planet='Tatooine',
|
||||
)
|
||||
|
||||
vader = Human(
|
||||
id='1001',
|
||||
name='Darth Vader',
|
||||
friends=['1004'],
|
||||
appearsIn=[4, 5, 6],
|
||||
homePlanet='Tatooine',
|
||||
)
|
||||
vader = Human(
|
||||
id='1001',
|
||||
name='Darth Vader',
|
||||
friends=['1004'],
|
||||
appears_in=[4, 5, 6],
|
||||
home_planet='Tatooine',
|
||||
)
|
||||
|
||||
han = Human(
|
||||
id='1002',
|
||||
name='Han Solo',
|
||||
friends=['1000', '1003', '2001'],
|
||||
appearsIn=[4, 5, 6],
|
||||
homePlanet=None,
|
||||
)
|
||||
han = Human(
|
||||
id='1002',
|
||||
name='Han Solo',
|
||||
friends=['1000', '1003', '2001'],
|
||||
appears_in=[4, 5, 6],
|
||||
home_planet=None,
|
||||
)
|
||||
|
||||
leia = Human(
|
||||
id='1003',
|
||||
name='Leia Organa',
|
||||
friends=['1000', '1002', '2000', '2001'],
|
||||
appearsIn=[4, 5, 6],
|
||||
homePlanet='Alderaan',
|
||||
)
|
||||
leia = Human(
|
||||
id='1003',
|
||||
name='Leia Organa',
|
||||
friends=['1000', '1002', '2000', '2001'],
|
||||
appears_in=[4, 5, 6],
|
||||
home_planet='Alderaan',
|
||||
)
|
||||
|
||||
tarkin = Human(
|
||||
id='1004',
|
||||
name='Wilhuff Tarkin',
|
||||
friends=['1001'],
|
||||
appearsIn=[4],
|
||||
homePlanet=None,
|
||||
)
|
||||
tarkin = Human(
|
||||
id='1004',
|
||||
name='Wilhuff Tarkin',
|
||||
friends=['1001'],
|
||||
appears_in=[4],
|
||||
home_planet=None,
|
||||
)
|
||||
|
||||
humanData = {
|
||||
'1000': luke,
|
||||
'1001': vader,
|
||||
'1002': han,
|
||||
'1003': leia,
|
||||
'1004': tarkin,
|
||||
}
|
||||
humanData = {
|
||||
'1000': luke,
|
||||
'1001': vader,
|
||||
'1002': han,
|
||||
'1003': leia,
|
||||
'1004': tarkin,
|
||||
}
|
||||
|
||||
Droid = namedtuple('Droid', 'id name friends appearsIn primaryFunction')
|
||||
threepio = Droid(
|
||||
id='2000',
|
||||
name='C-3PO',
|
||||
friends=['1000', '1002', '1003', '2001'],
|
||||
appears_in=[4, 5, 6],
|
||||
primary_function='Protocol',
|
||||
)
|
||||
|
||||
threepio = Droid(
|
||||
id='2000',
|
||||
name='C-3PO',
|
||||
friends=['1000', '1002', '1003', '2001'],
|
||||
appearsIn=[4, 5, 6],
|
||||
primaryFunction='Protocol',
|
||||
)
|
||||
artoo = Droid(
|
||||
id='2001',
|
||||
name='R2-D2',
|
||||
friends=['1000', '1002', '1003'],
|
||||
appears_in=[4, 5, 6],
|
||||
primary_function='Astromech',
|
||||
)
|
||||
|
||||
artoo = Droid(
|
||||
id='2001',
|
||||
name='R2-D2',
|
||||
friends=['1000', '1002', '1003'],
|
||||
appearsIn=[4, 5, 6],
|
||||
primaryFunction='Astromech',
|
||||
)
|
||||
|
||||
droidData = {
|
||||
'2000': threepio,
|
||||
'2001': artoo,
|
||||
}
|
||||
droidData = {
|
||||
'2000': threepio,
|
||||
'2001': artoo,
|
||||
}
|
||||
|
||||
|
||||
def getCharacter(id):
|
||||
|
@ -84,8 +85,8 @@ def getFriends(character):
|
|||
|
||||
def getHero(episode):
|
||||
if episode == 5:
|
||||
return luke
|
||||
return artoo
|
||||
return humanData['1000']
|
||||
return droidData['2001']
|
||||
|
||||
|
||||
def getHuman(id):
|
||||
|
|
|
@ -2,7 +2,7 @@ from graphql.core.type import GraphQLEnumValue
|
|||
import graphene
|
||||
from graphene import resolve_only_args
|
||||
|
||||
from .data import getHero, getHuman, getCharacter, getDroid, Human as _Human, Droid as _Droid
|
||||
from .data import getHero, getHuman, getCharacter, getDroid
|
||||
|
||||
Episode = graphene.Enum('Episode', dict(
|
||||
NEWHOPE=GraphQLEnumValue(4),
|
||||
|
@ -11,29 +11,23 @@ Episode = graphene.Enum('Episode', dict(
|
|||
))
|
||||
|
||||
|
||||
def wrap_character(character):
|
||||
if isinstance(character, _Human):
|
||||
return Human(character)
|
||||
elif isinstance(character, _Droid):
|
||||
return Droid(character)
|
||||
|
||||
|
||||
class Character(graphene.Interface):
|
||||
id = graphene.IDField()
|
||||
name = graphene.StringField()
|
||||
friends = graphene.ListField('self')
|
||||
appearsIn = graphene.ListField(Episode)
|
||||
appears_in = graphene.ListField(Episode)
|
||||
|
||||
def resolve_friends(self, args, *_):
|
||||
return [wrap_character(getCharacter(f)) for f in self.instance.friends]
|
||||
# The character friends is a list of strings
|
||||
return [getCharacter(f) for f in self.friends]
|
||||
|
||||
|
||||
class Human(Character):
|
||||
homePlanet = graphene.StringField()
|
||||
home_planet = graphene.StringField()
|
||||
|
||||
|
||||
class Droid(Character):
|
||||
primaryFunction = graphene.StringField()
|
||||
primary_function = graphene.StringField()
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
|
@ -52,15 +46,15 @@ class Query(graphene.ObjectType):
|
|||
|
||||
@resolve_only_args
|
||||
def resolve_hero(self, episode=None):
|
||||
return wrap_character(getHero(episode))
|
||||
return getHero(episode)
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_human(self, id):
|
||||
return wrap_character(getHuman(id))
|
||||
return getHuman(id)
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_droid(self, id):
|
||||
return wrap_character(getDroid(id))
|
||||
return getDroid(id)
|
||||
|
||||
|
||||
Schema = graphene.Schema(query=Query)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from .schema import Schema, Query
|
||||
from graphql.core import graphql
|
||||
from .data import setup
|
||||
|
||||
setup()
|
||||
|
||||
def test_hero_name_query():
|
||||
query = '''
|
||||
|
|
|
@ -27,7 +27,7 @@ class Ship(DjangoNode):
|
|||
|
||||
|
||||
@schema.register
|
||||
class CharacterModel(DjangoObjectType):
|
||||
class Character(DjangoObjectType):
|
||||
class Meta:
|
||||
model = CharacterModel
|
||||
|
||||
|
|
|
@ -1,78 +1,80 @@
|
|||
from collections import namedtuple
|
||||
data = {}
|
||||
|
||||
Ship = namedtuple('Ship', ['id', 'name'])
|
||||
Faction = namedtuple('Faction', ['id', 'name', 'ships'])
|
||||
|
||||
xwing = Ship(
|
||||
id='1',
|
||||
name='X-Wing',
|
||||
)
|
||||
def setup():
|
||||
global data
|
||||
|
||||
ywing = Ship(
|
||||
id='2',
|
||||
name='Y-Wing',
|
||||
)
|
||||
from .schema import Ship, Faction
|
||||
xwing = Ship(
|
||||
id='1',
|
||||
name='X-Wing',
|
||||
)
|
||||
|
||||
awing = Ship(
|
||||
id='3',
|
||||
name='A-Wing',
|
||||
)
|
||||
ywing = Ship(
|
||||
id='2',
|
||||
name='Y-Wing',
|
||||
)
|
||||
|
||||
# Yeah, technically it's Corellian. But it flew in the service of the rebels,
|
||||
# so for the purposes of this demo it's a rebel ship.
|
||||
falcon = Ship(
|
||||
id='4',
|
||||
name='Millenium Falcon',
|
||||
)
|
||||
awing = Ship(
|
||||
id='3',
|
||||
name='A-Wing',
|
||||
)
|
||||
|
||||
homeOne = Ship(
|
||||
id='5',
|
||||
name='Home One',
|
||||
)
|
||||
# Yeah, technically it's Corellian. But it flew in the service of the rebels,
|
||||
# so for the purposes of this demo it's a rebel ship.
|
||||
falcon = Ship(
|
||||
id='4',
|
||||
name='Millenium Falcon',
|
||||
)
|
||||
|
||||
tieFighter = Ship(
|
||||
id='6',
|
||||
name='TIE Fighter',
|
||||
)
|
||||
homeOne = Ship(
|
||||
id='5',
|
||||
name='Home One',
|
||||
)
|
||||
|
||||
tieInterceptor = Ship(
|
||||
id='7',
|
||||
name='TIE Interceptor',
|
||||
)
|
||||
tieFighter = Ship(
|
||||
id='6',
|
||||
name='TIE Fighter',
|
||||
)
|
||||
|
||||
executor = Ship(
|
||||
id='8',
|
||||
name='Executor',
|
||||
)
|
||||
tieInterceptor = Ship(
|
||||
id='7',
|
||||
name='TIE Interceptor',
|
||||
)
|
||||
|
||||
rebels = Faction(
|
||||
id='1',
|
||||
name='Alliance to Restore the Republic',
|
||||
ships=['1', '2', '3', '4', '5']
|
||||
)
|
||||
executor = Ship(
|
||||
id='8',
|
||||
name='Executor',
|
||||
)
|
||||
|
||||
empire = Faction(
|
||||
id='2',
|
||||
name='Galactic Empire',
|
||||
ships=['6', '7', '8']
|
||||
)
|
||||
rebels = Faction(
|
||||
id='1',
|
||||
name='Alliance to Restore the Republic',
|
||||
ships=['1', '2', '3', '4', '5']
|
||||
)
|
||||
|
||||
data = {
|
||||
'Faction': {
|
||||
'1': rebels,
|
||||
'2': empire
|
||||
},
|
||||
'Ship': {
|
||||
'1': xwing,
|
||||
'2': ywing,
|
||||
'3': awing,
|
||||
'4': falcon,
|
||||
'5': homeOne,
|
||||
'6': tieFighter,
|
||||
'7': tieInterceptor,
|
||||
'8': executor
|
||||
empire = Faction(
|
||||
id='2',
|
||||
name='Galactic Empire',
|
||||
ships=['6', '7', '8']
|
||||
)
|
||||
|
||||
data = {
|
||||
'Faction': {
|
||||
'1': rebels,
|
||||
'2': empire
|
||||
},
|
||||
'Ship': {
|
||||
'1': xwing,
|
||||
'2': ywing,
|
||||
'3': awing,
|
||||
'4': falcon,
|
||||
'5': homeOne,
|
||||
'6': tieFighter,
|
||||
'7': tieInterceptor,
|
||||
'8': executor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def createShip(shipName, factionId):
|
||||
|
@ -95,8 +97,8 @@ def getFaction(_id):
|
|||
|
||||
|
||||
def getRebels():
|
||||
return rebels
|
||||
return getFaction('1')
|
||||
|
||||
|
||||
def getEmpire():
|
||||
return empire
|
||||
return getFaction('2')
|
||||
|
|
|
@ -12,29 +12,28 @@ schema = graphene.Schema(name='Starwars Relay Schema')
|
|||
|
||||
|
||||
class Ship(relay.Node):
|
||||
|
||||
'''A ship in the Star Wars saga'''
|
||||
name = graphene.StringField(description='The name of the ship.')
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id):
|
||||
return Ship(getShip(id))
|
||||
return getShip(id)
|
||||
|
||||
|
||||
class Faction(relay.Node):
|
||||
|
||||
'''A faction in the Star Wars saga'''
|
||||
name = graphene.StringField(description='The name of the faction.')
|
||||
ships = relay.ConnectionField(
|
||||
Ship, description='The ships used by the faction.')
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_ships(self, **kwargs):
|
||||
return [Ship(getShip(ship)) for ship in self.instance.ships]
|
||||
def resolve_ships(self, **args):
|
||||
# Transform the instance ship_ids into real instances
|
||||
return [getShip(ship_id) for ship_id in self.ships]
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id):
|
||||
return Faction(getFaction(id))
|
||||
return getFaction(id)
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
|
@ -44,11 +43,11 @@ class Query(graphene.ObjectType):
|
|||
|
||||
@resolve_only_args
|
||||
def resolve_rebels(self):
|
||||
return Faction(getRebels())
|
||||
return getRebels()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_empire(self):
|
||||
return Faction(getEmpire())
|
||||
return getEmpire()
|
||||
|
||||
|
||||
schema.query = Query
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import graphene
|
||||
from graphene import resolve_only_args, relay
|
||||
|
||||
from .data import (
|
||||
getHero, getHuman, getCharacter, getDroid,
|
||||
Human as _Human, Droid as _Droid)
|
||||
|
||||
Episode = graphene.Enum('Episode', dict(
|
||||
NEWHOPE=4,
|
||||
EMPIRE=5,
|
||||
JEDI=6
|
||||
))
|
||||
|
||||
|
||||
def wrap_character(character):
|
||||
if isinstance(character, _Human):
|
||||
return Human(character)
|
||||
elif isinstance(character, _Droid):
|
||||
return Droid(character)
|
||||
|
||||
|
||||
class Character(graphene.Interface):
|
||||
name = graphene.StringField()
|
||||
friends = relay.Connection('Character')
|
||||
appearsIn = graphene.ListField(Episode)
|
||||
|
||||
def resolve_friends(self, args, *_):
|
||||
return [wrap_character(getCharacter(f)) for f in self.instance.friends]
|
||||
|
||||
|
||||
class Human(relay.Node, Character):
|
||||
homePlanet = graphene.StringField()
|
||||
|
||||
|
||||
class Droid(relay.Node, Character):
|
||||
primaryFunction = graphene.StringField()
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hero = graphene.Field(Character,
|
||||
episode=graphene.Argument(Episode))
|
||||
human = graphene.Field(Human,
|
||||
id=graphene.Argument(graphene.String))
|
||||
droid = graphene.Field(Droid,
|
||||
id=graphene.Argument(graphene.String))
|
||||
node = relay.NodeField()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_hero(self, episode):
|
||||
return wrap_character(getHero(episode))
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_human(self, id):
|
||||
return wrap_character(getHuman(id))
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_droid(self, id):
|
||||
return wrap_character(getDroid(id))
|
||||
|
||||
|
||||
Schema = graphene.Schema(query=Query)
|
|
@ -1,7 +1,7 @@
|
|||
from pytest import raises
|
||||
from graphql.core import graphql
|
||||
|
||||
from .schema import schema
|
||||
from .data import setup
|
||||
|
||||
setup()
|
||||
|
||||
|
||||
def test_correct_fetch_first_ship_rebels():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from pytest import raises
|
||||
from graphql.core import graphql
|
||||
|
||||
from .schema import schema
|
||||
from .data import setup
|
||||
|
||||
setup()
|
||||
|
||||
|
||||
def test_correctly_fetches_id_name_rebels():
|
||||
|
|
Loading…
Reference in New Issue
Block a user