mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Added automatic snake casing to camel casing conversion in field names
This commit is contained in:
parent
176696c1ac
commit
701c49db26
|
@ -41,7 +41,7 @@ class ConnectionOrListField(LazyField):
|
||||||
field = DjangoConnectionField(model_field)
|
field = DjangoConnectionField(model_field)
|
||||||
else:
|
else:
|
||||||
field = ListField(model_field)
|
field = ListField(model_field)
|
||||||
field.contribute_to_class(self.object_type, self.field_name)
|
field.contribute_to_class(self.object_type, self.name)
|
||||||
return field
|
return field
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,23 +10,26 @@ from graphql.core.type import (
|
||||||
GraphQLArgument,
|
GraphQLArgument,
|
||||||
GraphQLFloat,
|
GraphQLFloat,
|
||||||
)
|
)
|
||||||
from graphene.utils import cached_property, memoize
|
from graphene.utils import memoize, to_camel_case
|
||||||
from graphene.core.types import BaseObjectType
|
from graphene.core.types import BaseObjectType
|
||||||
|
|
||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
|
|
||||||
def __init__(self, field_type, resolve=None, null=True, args=None, description='', **extra_args):
|
def __init__(self, field_type, name=None, resolve=None, null=True, args=None, description='', **extra_args):
|
||||||
self.field_type = field_type
|
self.field_type = field_type
|
||||||
self.resolve_fn = resolve
|
self.resolve_fn = resolve
|
||||||
self.null = null
|
self.null = null
|
||||||
self.args = args or {}
|
self.args = args or {}
|
||||||
self.extra_args = extra_args
|
self.extra_args = extra_args
|
||||||
self._type = None
|
self._type = None
|
||||||
|
self.name = name
|
||||||
self.description = description or self.__doc__
|
self.description = description or self.__doc__
|
||||||
self.object_type = None
|
self.object_type = None
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
|
if not self.name:
|
||||||
|
self.name = to_camel_case(name)
|
||||||
self.field_name = name
|
self.field_name = name
|
||||||
self.object_type = cls
|
self.object_type = cls
|
||||||
if isinstance(self.field_type, Field) and not self.field_type.object_type:
|
if isinstance(self.field_type, Field) and not self.field_type.object_type:
|
||||||
|
@ -94,7 +97,7 @@ class Field(object):
|
||||||
raise TypeError("Field %s.%s initiated with invalid args: %s" % (
|
raise TypeError("Field %s.%s initiated with invalid args: %s" % (
|
||||||
self.object_type,
|
self.object_type,
|
||||||
self.field_name,
|
self.field_name,
|
||||||
','.join(meta_attrs.keys())
|
','.join(extra_args.keys())
|
||||||
))
|
))
|
||||||
|
|
||||||
internal_type = self.internal_type(schema)
|
internal_type = self.internal_type(schema)
|
||||||
|
@ -107,7 +110,7 @@ class Field(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" Return "object_type.field_name". """
|
""" Return "object_type.name". """
|
||||||
return '%s.%s' % (self.object_type, self.field_name)
|
return '%s.%s' % (self.object_type, self.field_name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -71,3 +71,7 @@ class Options(object):
|
||||||
@cached_property
|
@cached_property
|
||||||
def fields_map(self):
|
def fields_map(self):
|
||||||
return {f.field_name: f for f in self.fields}
|
return {f.field_name: f for f in self.fields}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def internal_fields_map(self):
|
||||||
|
return {f.name: f for f in self.fields}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ObjectTypeMeta(type):
|
||||||
new_class.add_extra_fields()
|
new_class.add_extra_fields()
|
||||||
|
|
||||||
new_fields = new_class._meta.local_fields
|
new_fields = new_class._meta.local_fields
|
||||||
field_names = {f.field_name for f in new_fields}
|
field_names = {f.name for f in new_fields}
|
||||||
|
|
||||||
for base in parents:
|
for base in parents:
|
||||||
original_base = base
|
original_base = base
|
||||||
|
@ -65,12 +65,12 @@ class ObjectTypeMeta(type):
|
||||||
# on the base classes (we cannot handle shadowed fields at the
|
# on the base classes (we cannot handle shadowed fields at the
|
||||||
# moment).
|
# moment).
|
||||||
for field in parent_fields:
|
for field in parent_fields:
|
||||||
if field.field_name in field_names:
|
if field.name in field_names:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Local field %r in class %r clashes '
|
'Local field %r in class %r clashes '
|
||||||
'with field of similar name from '
|
'with field of similar name from '
|
||||||
'base class %r' % (
|
'base class %r' % (
|
||||||
field.field_name, name, base.__name__)
|
field.name, name, base.__name__)
|
||||||
)
|
)
|
||||||
new_class._meta.parents.append(base)
|
new_class._meta.parents.append(base)
|
||||||
if base._meta.interface:
|
if base._meta.interface:
|
||||||
|
@ -99,7 +99,7 @@ class BaseObjectType(object):
|
||||||
def __new__(cls, instance=None, *args, **kwargs):
|
def __new__(cls, instance=None, *args, **kwargs):
|
||||||
if cls._meta.interface:
|
if cls._meta.interface:
|
||||||
raise Exception("An interface cannot be initialized")
|
raise Exception("An interface cannot be initialized")
|
||||||
if instance == None:
|
if instance is None:
|
||||||
return None
|
return None
|
||||||
return super(BaseObjectType, cls).__new__(cls, instance, *args, **kwargs)
|
return super(BaseObjectType, cls).__new__(cls, instance, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ class BaseObjectType(object):
|
||||||
@memoize
|
@memoize
|
||||||
@register_internal_type
|
@register_internal_type
|
||||||
def internal_type(cls, schema):
|
def internal_type(cls, schema):
|
||||||
fields_map = cls._meta.fields_map
|
fields_map = cls._meta.internal_fields_map
|
||||||
fields = lambda: {
|
fields = lambda: {
|
||||||
name: field.internal_field(schema)
|
name: field.internal_field(schema)
|
||||||
for name, field in fields_map.items()
|
for name, field in fields_map.items()
|
||||||
|
|
|
@ -32,3 +32,12 @@ def memoize(fun):
|
||||||
return ret
|
return ret
|
||||||
cache = {}
|
cache = {}
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# From this response in Stackoverflow
|
||||||
|
# http://stackoverflow.com/a/19053800/1072990
|
||||||
|
def to_camel_case(snake_str):
|
||||||
|
components = snake_str.split('_')
|
||||||
|
# We capitalize the first letter of each component except the first one
|
||||||
|
# with the 'title' method and join them together.
|
||||||
|
return components[0] + "".join(x.title() for x in components[1:])
|
||||||
|
|
|
@ -71,16 +71,16 @@ def test_should_map_fields():
|
||||||
query = '''
|
query = '''
|
||||||
query ReporterQuery {
|
query ReporterQuery {
|
||||||
reporter {
|
reporter {
|
||||||
first_name,
|
firstName,
|
||||||
last_name,
|
lastName,
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
expected = {
|
expected = {
|
||||||
'reporter': {
|
'reporter': {
|
||||||
'first_name': 'ABA',
|
'firstName': 'ABA',
|
||||||
'last_name': 'X',
|
'lastName': 'X',
|
||||||
'email': ''
|
'email': ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ def test_should_node():
|
||||||
query ReporterQuery {
|
query ReporterQuery {
|
||||||
reporter {
|
reporter {
|
||||||
id,
|
id,
|
||||||
first_name,
|
firstName,
|
||||||
articles {
|
articles {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -141,13 +141,13 @@ def test_should_node():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_name,
|
lastName,
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
my_article: node(id:"QXJ0aWNsZU5vZGVUeXBlOjE=") {
|
myArticle: node(id:"QXJ0aWNsZU5vZGVUeXBlOjE=") {
|
||||||
id
|
id
|
||||||
... on ReporterNodeType {
|
... on ReporterNodeType {
|
||||||
first_name
|
firstName
|
||||||
}
|
}
|
||||||
... on ArticleNodeType {
|
... on ArticleNodeType {
|
||||||
headline
|
headline
|
||||||
|
@ -158,8 +158,8 @@ def test_should_node():
|
||||||
expected = {
|
expected = {
|
||||||
'reporter': {
|
'reporter': {
|
||||||
'id': 'UmVwb3J0ZXJOb2RlVHlwZTox',
|
'id': 'UmVwb3J0ZXJOb2RlVHlwZTox',
|
||||||
'first_name': 'ABA',
|
'firstName': 'ABA',
|
||||||
'last_name': 'X',
|
'lastName': 'X',
|
||||||
'email': '',
|
'email': '',
|
||||||
'articles': {
|
'articles': {
|
||||||
'edges': [{
|
'edges': [{
|
||||||
|
@ -169,7 +169,7 @@ def test_should_node():
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'my_article': {
|
'myArticle': {
|
||||||
'id': 'QXJ0aWNsZU5vZGVUeXBlOjE=',
|
'id': 'QXJ0aWNsZU5vZGVUeXBlOjE=',
|
||||||
'headline': 'Article node'
|
'headline': 'Article node'
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,16 +40,16 @@ schema = Schema()
|
||||||
|
|
||||||
|
|
||||||
def test_django_interface():
|
def test_django_interface():
|
||||||
assert DjangoNode._meta.interface == True
|
assert DjangoNode._meta.interface is True
|
||||||
|
|
||||||
|
|
||||||
def test_pseudo_interface():
|
def test_pseudo_interface():
|
||||||
object_type = Character.internal_type(schema)
|
object_type = Character.internal_type(schema)
|
||||||
assert Character._meta.interface == True
|
assert Character._meta.interface is True
|
||||||
assert isinstance(object_type, GraphQLInterfaceType)
|
assert isinstance(object_type, GraphQLInterfaceType)
|
||||||
assert Character._meta.model == Reporter
|
assert Character._meta.model == Reporter
|
||||||
assert object_type.get_fields().keys() == [
|
assert object_type.get_fields().keys() == [
|
||||||
'articles', 'first_name', 'last_name', 'id', 'email']
|
'lastName', 'email', 'id', 'firstName', 'articles']
|
||||||
|
|
||||||
|
|
||||||
def test_interface_resolve_type():
|
def test_interface_resolve_type():
|
||||||
|
@ -59,12 +59,13 @@ def test_interface_resolve_type():
|
||||||
|
|
||||||
def test_object_type():
|
def test_object_type():
|
||||||
object_type = Human.internal_type(schema)
|
object_type = Human.internal_type(schema)
|
||||||
assert Human._meta.interface == False
|
internal_fields_map = Human._meta.internal_fields_map
|
||||||
|
assert Human._meta.interface is False
|
||||||
assert isinstance(object_type, GraphQLObjectType)
|
assert isinstance(object_type, GraphQLObjectType)
|
||||||
assert object_type.get_fields() == {
|
assert object_type.get_fields() == {
|
||||||
'headline': Human._meta.fields_map['headline'].internal_field(schema),
|
'headline': internal_fields_map['headline'].internal_field(schema),
|
||||||
'id': Human._meta.fields_map['id'].internal_field(schema),
|
'id': internal_fields_map['id'].internal_field(schema),
|
||||||
'reporter': Human._meta.fields_map['reporter'].internal_field(schema),
|
'reporter': internal_fields_map['reporter'].internal_field(schema),
|
||||||
'pub_date': Human._meta.fields_map['pub_date'].internal_field(schema),
|
'pubDate': internal_fields_map['pubDate'].internal_field(schema),
|
||||||
}
|
}
|
||||||
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]
|
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user