mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Merge branch 'refs/heads/master' into docs-playground
This commit is contained in:
commit
4e258b5def
31
examples/complex_example.py
Normal file
31
examples/complex_example.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import graphene
|
||||||
|
|
||||||
|
|
||||||
|
class GeoInput(graphene.InputObjectType):
|
||||||
|
lat = graphene.Float(required=True)
|
||||||
|
lng = graphene.Float(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Address(graphene.ObjectType):
|
||||||
|
latlng = graphene.String()
|
||||||
|
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
address = graphene.Field(Address, geo=graphene.Argument(GeoInput))
|
||||||
|
|
||||||
|
def resolve_address(self, args, info):
|
||||||
|
geo = args.get('geo')
|
||||||
|
return Address(latlng="({},{})".format(geo.get('lat'), geo.get('lng')))
|
||||||
|
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=Query)
|
||||||
|
query = '''
|
||||||
|
query something{
|
||||||
|
address(geo: {lat:32.2, lng:12}) {
|
||||||
|
latlng
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
result = schema.execute(query)
|
||||||
|
print(result.data['address']['latlng'])
|
|
@ -21,7 +21,7 @@ query = '''
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
result = schema.execute(query)
|
result = schema.execute(query)
|
||||||
print(result.data['patron'])
|
print(result.data['patron'])
|
|
@ -1,4 +1,5 @@
|
||||||
from graphene.contrib.django.types import (
|
from graphene.contrib.django.types import (
|
||||||
|
DjangoConnection,
|
||||||
DjangoObjectType,
|
DjangoObjectType,
|
||||||
DjangoInterface,
|
DjangoInterface,
|
||||||
DjangoNode
|
DjangoNode
|
||||||
|
@ -9,4 +10,4 @@ from graphene.contrib.django.fields import (
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = ['DjangoObjectType', 'DjangoInterface', 'DjangoNode',
|
__all__ = ['DjangoObjectType', 'DjangoInterface', 'DjangoNode',
|
||||||
'DjangoConnectionField', 'DjangoModelField']
|
'DjangoConnection', 'DjangoConnectionField', 'DjangoModelField']
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
|
import warnings
|
||||||
|
|
||||||
from ...core.exceptions import SkipField
|
from ...core.exceptions import SkipField
|
||||||
from ...core.fields import Field
|
from ...core.fields import Field
|
||||||
from ...core.types.base import FieldType
|
from ...core.types.base import FieldType
|
||||||
from ...core.types.definitions import List
|
from ...core.types.definitions import List
|
||||||
from ...relay import ConnectionField
|
from ...relay import ConnectionField
|
||||||
from ...relay.utils import is_node
|
from ...relay.utils import is_node
|
||||||
from .utils import get_type_for_model, lazy_map
|
from .utils import get_type_for_model
|
||||||
|
|
||||||
|
|
||||||
class DjangoConnectionField(ConnectionField):
|
class DjangoConnectionField(ConnectionField):
|
||||||
|
|
||||||
def wrap_resolved(self, value, instance, args, info):
|
def __init__(self, *args, **kwargs):
|
||||||
return lazy_map(value, self.type)
|
cls = self.__class__
|
||||||
|
warnings.warn("Using {} will be not longer supported."
|
||||||
|
" Use relay.ConnectionField instead".format(cls.__name__),
|
||||||
class LazyListField(Field):
|
FutureWarning)
|
||||||
|
return super(DjangoConnectionField, self).__init__(*args, **kwargs)
|
||||||
def get_type(self, schema):
|
|
||||||
return List(self.type)
|
|
||||||
|
|
||||||
def resolver(self, instance, args, info):
|
|
||||||
resolved = super(LazyListField, self).resolver(instance, args, info)
|
|
||||||
return lazy_map(resolved, self.type)
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionOrListField(Field):
|
class ConnectionOrListField(Field):
|
||||||
|
@ -33,8 +29,8 @@ class ConnectionOrListField(Field):
|
||||||
if is_node(field_object_type):
|
if is_node(field_object_type):
|
||||||
field = DjangoConnectionField(field_object_type)
|
field = DjangoConnectionField(field_object_type)
|
||||||
else:
|
else:
|
||||||
field = LazyListField(field_object_type)
|
field = Field(List(field_object_type))
|
||||||
field.contribute_to_class(self.object_type, self.name)
|
field.contribute_to_class(self.object_type, self.attname)
|
||||||
return schema.T(field)
|
return schema.T(field)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ def test_node_replacedfield():
|
||||||
|
|
||||||
|
|
||||||
def test_interface_resolve_type():
|
def test_interface_resolve_type():
|
||||||
resolve_type = Character.resolve_type(schema, Human())
|
resolve_type = Character._resolve_type(schema, Human())
|
||||||
assert isinstance(resolve_type, GraphQLObjectType)
|
assert isinstance(resolve_type, GraphQLObjectType)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ import six
|
||||||
|
|
||||||
from ...core.types import BaseObjectType, ObjectTypeMeta
|
from ...core.types import BaseObjectType, ObjectTypeMeta
|
||||||
from ...relay.fields import GlobalIDField
|
from ...relay.fields import GlobalIDField
|
||||||
from ...relay.types import BaseNode
|
from ...relay.types import BaseNode, Connection
|
||||||
from .converter import convert_django_field
|
from .converter import convert_django_field
|
||||||
from .options import DjangoOptions
|
from .options import DjangoOptions
|
||||||
from .utils import get_reverse_fields
|
from .utils import get_reverse_fields, maybe_queryset
|
||||||
|
|
||||||
|
|
||||||
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
|
@ -30,7 +30,7 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
|
||||||
is_excluded = field.name in cls._meta.exclude_fields or is_already_created
|
is_excluded = field.name in cls._meta.exclude_fields or is_already_created
|
||||||
if is_not_in_only or is_excluded:
|
if is_not_in_only or is_excluded:
|
||||||
# We skip this field if we specify only_fields and is not
|
# We skip this field if we specify only_fields and is not
|
||||||
# in there. Or when we excldue this field in exclude_fields
|
# in there. Or when we exclude this field in exclude_fields
|
||||||
continue
|
continue
|
||||||
converted_field = convert_django_field(field)
|
converted_field = convert_django_field(field)
|
||||||
cls.add_to_class(field.name, converted_field)
|
cls.add_to_class(field.name, converted_field)
|
||||||
|
@ -71,6 +71,14 @@ class DjangoInterface(six.with_metaclass(
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoConnection(Connection):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_list(cls, iterable, *args, **kwargs):
|
||||||
|
iterable = maybe_queryset(iterable)
|
||||||
|
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DjangoNode(BaseNode, DjangoInterface):
|
class DjangoNode(BaseNode, DjangoInterface):
|
||||||
id = GlobalIDField()
|
id = GlobalIDField()
|
||||||
|
|
||||||
|
@ -81,3 +89,5 @@ class DjangoNode(BaseNode, DjangoInterface):
|
||||||
return cls(instance)
|
return cls(instance)
|
||||||
except cls._meta.model.DoesNotExist:
|
except cls._meta.model.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
connection_type = DjangoConnection
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
|
|
||||||
from ...utils import LazyMap
|
|
||||||
|
|
||||||
|
|
||||||
def get_type_for_model(schema, model):
|
def get_type_for_model(schema, model):
|
||||||
|
@ -22,9 +19,7 @@ def get_reverse_fields(model):
|
||||||
yield related
|
yield related
|
||||||
|
|
||||||
|
|
||||||
def lazy_map(value, func):
|
def maybe_queryset(value):
|
||||||
if isinstance(value, Manager):
|
if isinstance(value, Manager):
|
||||||
value = value.get_queryset()
|
value = value.get_queryset()
|
||||||
if isinstance(value, QuerySet):
|
|
||||||
return LazyMap(value, func)
|
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -13,7 +13,10 @@ class Character(Interface):
|
||||||
|
|
||||||
|
|
||||||
class Pet(ObjectType):
|
class Pet(ObjectType):
|
||||||
type = String(resolver=lambda *_: 'Dog')
|
type = String()
|
||||||
|
|
||||||
|
def resolve_type(self, args, info):
|
||||||
|
return 'Dog'
|
||||||
|
|
||||||
|
|
||||||
class Human(Character):
|
class Human(Character):
|
||||||
|
|
|
@ -182,7 +182,7 @@ class BaseObjectType(BaseType):
|
||||||
signals.post_init.send(self.__class__, instance=self)
|
signals.post_init.send(self.__class__, instance=self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_type(cls, schema, instance, *args):
|
def _resolve_type(cls, schema, instance, *args):
|
||||||
return schema.T(instance.__class__)
|
return schema.T(instance.__class__)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -191,7 +191,7 @@ class BaseObjectType(BaseType):
|
||||||
return GraphQLInterfaceType(
|
return GraphQLInterfaceType(
|
||||||
cls._meta.type_name,
|
cls._meta.type_name,
|
||||||
description=cls._meta.description,
|
description=cls._meta.description,
|
||||||
resolve_type=partial(cls.resolve_type, schema),
|
resolve_type=partial(cls._resolve_type, schema),
|
||||||
fields=partial(cls.get_fields, schema)
|
fields=partial(cls.get_fields, schema)
|
||||||
)
|
)
|
||||||
elif cls._meta.is_union:
|
elif cls._meta.is_union:
|
||||||
|
@ -219,6 +219,10 @@ class BaseObjectType(BaseType):
|
||||||
|
|
||||||
return OrderedDict(fields)
|
return OrderedDict(fields)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wrap(cls, instance, args, info):
|
||||||
|
return cls(_root=instance)
|
||||||
|
|
||||||
|
|
||||||
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -75,7 +75,7 @@ def test_union_cannot_initialize():
|
||||||
|
|
||||||
|
|
||||||
def test_interface_resolve_type():
|
def test_interface_resolve_type():
|
||||||
resolve_type = Character.resolve_type(schema, Human(object()))
|
resolve_type = Character._resolve_type(schema, Human(object()))
|
||||||
assert isinstance(resolve_type, GraphQLObjectType)
|
assert isinstance(resolve_type, GraphQLObjectType)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
from collections import Iterable
|
|
||||||
|
|
||||||
from graphql_relay.connection.arrayconnection import connection_from_list
|
|
||||||
from graphql_relay.node.node import from_global_id
|
from graphql_relay.node.node import from_global_id
|
||||||
|
|
||||||
from ..core.fields import Field
|
from ..core.fields import Field
|
||||||
|
@ -30,24 +27,12 @@ class ConnectionField(Field):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def resolver(self, instance, args, info):
|
def resolver(self, instance, args, info):
|
||||||
from graphene.relay.types import PageInfo
|
|
||||||
schema = info.schema.graphene_schema
|
schema = info.schema.graphene_schema
|
||||||
|
connection_type = self.get_type(schema)
|
||||||
resolved = super(ConnectionField, self).resolver(instance, args, info)
|
resolved = super(ConnectionField, self).resolver(instance, args, info)
|
||||||
if resolved:
|
if isinstance(resolved, connection_type):
|
||||||
resolved = self.wrap_resolved(resolved, instance, args, info)
|
return resolved
|
||||||
assert isinstance(
|
return connection_type.from_list(resolved, args, info)
|
||||||
resolved, Iterable), 'Resolved value from the connection field have to be iterable'
|
|
||||||
type = schema.T(self.type)
|
|
||||||
node = schema.objecttype(type)
|
|
||||||
connection_type = self.get_connection_type(node)
|
|
||||||
edge_type = self.get_edge_type(node)
|
|
||||||
|
|
||||||
connection = connection_from_list(
|
|
||||||
resolved, args, connection_type=connection_type,
|
|
||||||
edge_type=edge_type, pageinfo_type=PageInfo)
|
|
||||||
connection.set_connection_data(resolved)
|
|
||||||
return connection
|
|
||||||
|
|
||||||
def get_connection_type(self, node):
|
def get_connection_type(self, node):
|
||||||
connection_type = self.connection_type or node.get_connection_type()
|
connection_type = self.connection_type or node.get_connection_type()
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
|
from collections import Iterable
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
from graphql_relay.connection.arrayconnection import connection_from_list
|
||||||
from graphql_relay.node.node import to_global_id
|
from graphql_relay.node.node import to_global_id
|
||||||
|
|
||||||
from ..core.types import (Boolean, Field, InputObjectType, Interface, List,
|
from ..core.types import (Boolean, Field, InputObjectType, Interface, List,
|
||||||
|
@ -63,6 +66,16 @@ class Connection(ObjectType):
|
||||||
(cls,),
|
(cls,),
|
||||||
{'edge_type': edge_type, 'edges': edges})
|
{'edge_type': edge_type, 'edges': edges})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_list(cls, iterable, args, info):
|
||||||
|
assert isinstance(
|
||||||
|
iterable, Iterable), 'Resolved value from the connection field have to be iterable'
|
||||||
|
connection = connection_from_list(
|
||||||
|
iterable, args, connection_type=cls,
|
||||||
|
edge_type=cls.edge_type, pageinfo_type=PageInfo)
|
||||||
|
connection.set_connection_data(iterable)
|
||||||
|
return connection
|
||||||
|
|
||||||
def set_connection_data(self, data):
|
def set_connection_data(self, data):
|
||||||
self._connection_data = data
|
self._connection_data = data
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
from blinker import Signal
|
try:
|
||||||
|
from blinker import Signal
|
||||||
|
except ImportError:
|
||||||
|
class Signal(object):
|
||||||
|
def send(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
init_schema = Signal()
|
init_schema = Signal()
|
||||||
class_prepared = Signal()
|
class_prepared = Signal()
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
from .str_converters import to_camel_case, to_snake_case
|
from .str_converters import to_camel_case, to_snake_case
|
||||||
from .proxy_snake_dict import ProxySnakeDict
|
from .proxy_snake_dict import ProxySnakeDict
|
||||||
from .caching import cached_property, memoize
|
from .caching import cached_property, memoize
|
||||||
from .lazymap import LazyMap
|
|
||||||
from .misc import enum_to_graphql_enum
|
from .misc import enum_to_graphql_enum
|
||||||
from .resolve_only_args import resolve_only_args
|
from .resolve_only_args import resolve_only_args
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict',
|
__all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict',
|
||||||
'cached_property', 'memoize', 'LazyMap', 'enum_to_graphql_enum',
|
'cached_property', 'memoize', 'enum_to_graphql_enum',
|
||||||
'resolve_only_args']
|
'resolve_only_args']
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
class LazyMap(object):
|
|
||||||
|
|
||||||
def __init__(self, origin, _map, state=None):
|
|
||||||
self._origin = origin
|
|
||||||
self._origin_iter = origin.__iter__()
|
|
||||||
self._state = state or []
|
|
||||||
self._finished = False
|
|
||||||
self._map = _map
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self if not self._finished else iter(self._state)
|
|
||||||
|
|
||||||
def iter(self):
|
|
||||||
return self.__iter__()
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return self._origin.__len__()
|
|
||||||
|
|
||||||
def __next__(self):
|
|
||||||
try:
|
|
||||||
n = next(self._origin_iter)
|
|
||||||
n = self._map(n)
|
|
||||||
except StopIteration as e:
|
|
||||||
self._finished = True
|
|
||||||
raise e
|
|
||||||
else:
|
|
||||||
self._state.append(n)
|
|
||||||
return n
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
return self.__next__()
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
item = self._origin[key]
|
|
||||||
if isinstance(key, slice):
|
|
||||||
return LazyMap(item, self._map)
|
|
||||||
return self._map(item)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return getattr(self._origin, name)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<LazyMap %s>" % repr(self._origin)
|
|
|
@ -1,23 +0,0 @@
|
||||||
from py.test import raises
|
|
||||||
|
|
||||||
from ..lazymap import LazyMap
|
|
||||||
|
|
||||||
|
|
||||||
def test_lazymap():
|
|
||||||
data = list(range(10))
|
|
||||||
lm = LazyMap(data, lambda x: 2 * x)
|
|
||||||
assert len(lm) == 10
|
|
||||||
assert lm[1] == 2
|
|
||||||
assert isinstance(lm[1:4], LazyMap)
|
|
||||||
assert lm.append == data.append
|
|
||||||
assert repr(lm) == '<LazyMap [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>'
|
|
||||||
|
|
||||||
|
|
||||||
def test_lazymap_iter():
|
|
||||||
data = list(range(2))
|
|
||||||
lm = LazyMap(data, lambda x: 2 * x)
|
|
||||||
iter_lm = iter(lm)
|
|
||||||
assert iter_lm.next() == 0
|
|
||||||
assert iter_lm.next() == 2
|
|
||||||
with raises(StopIteration):
|
|
||||||
iter_lm.next()
|
|
|
@ -4,3 +4,6 @@ max-line-length = 120
|
||||||
|
|
||||||
[coverage:run]
|
[coverage:run]
|
||||||
omit = core/ntypes/tests/*
|
omit = core/ntypes/tests/*
|
||||||
|
|
||||||
|
[isort]
|
||||||
|
known_first_party=graphene
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -24,7 +24,7 @@ class PyTest(TestCommand):
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='graphene',
|
name='graphene',
|
||||||
version='0.4.1.1',
|
version='0.4.2',
|
||||||
|
|
||||||
description='Graphene: Python DSL for GraphQL',
|
description='Graphene: Python DSL for GraphQL',
|
||||||
long_description=open('README.rst').read(),
|
long_description=open('README.rst').read(),
|
||||||
|
@ -55,7 +55,6 @@ setup(
|
||||||
|
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'six>=1.10.0',
|
'six>=1.10.0',
|
||||||
'blinker',
|
|
||||||
'graphql-core==0.4.9',
|
'graphql-core==0.4.9',
|
||||||
'graphql-relay==0.3.3'
|
'graphql-relay==0.3.3'
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue
Block a user