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)
|
||||
else:
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -10,23 +10,26 @@ from graphql.core.type import (
|
|||
GraphQLArgument,
|
||||
GraphQLFloat,
|
||||
)
|
||||
from graphene.utils import cached_property, memoize
|
||||
from graphene.utils import memoize, to_camel_case
|
||||
from graphene.core.types import BaseObjectType
|
||||
|
||||
|
||||
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.resolve_fn = resolve
|
||||
self.null = null
|
||||
self.args = args or {}
|
||||
self.extra_args = extra_args
|
||||
self._type = None
|
||||
self.name = name
|
||||
self.description = description or self.__doc__
|
||||
self.object_type = None
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
if not self.name:
|
||||
self.name = to_camel_case(name)
|
||||
self.field_name = name
|
||||
self.object_type = cls
|
||||
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" % (
|
||||
self.object_type,
|
||||
self.field_name,
|
||||
','.join(meta_attrs.keys())
|
||||
','.join(extra_args.keys())
|
||||
))
|
||||
|
||||
internal_type = self.internal_type(schema)
|
||||
|
@ -107,7 +110,7 @@ class Field(object):
|
|||
)
|
||||
|
||||
def __str__(self):
|
||||
""" Return "object_type.field_name". """
|
||||
""" Return "object_type.name". """
|
||||
return '%s.%s' % (self.object_type, self.field_name)
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -71,3 +71,7 @@ class Options(object):
|
|||
@cached_property
|
||||
def fields_map(self):
|
||||
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_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:
|
||||
original_base = base
|
||||
|
@ -65,12 +65,12 @@ class ObjectTypeMeta(type):
|
|||
# on the base classes (we cannot handle shadowed fields at the
|
||||
# moment).
|
||||
for field in parent_fields:
|
||||
if field.field_name in field_names:
|
||||
if field.name in field_names:
|
||||
raise Exception(
|
||||
'Local field %r in class %r clashes '
|
||||
'with field of similar name from '
|
||||
'base class %r' % (
|
||||
field.field_name, name, base.__name__)
|
||||
field.name, name, base.__name__)
|
||||
)
|
||||
new_class._meta.parents.append(base)
|
||||
if base._meta.interface:
|
||||
|
@ -99,7 +99,7 @@ class BaseObjectType(object):
|
|||
def __new__(cls, instance=None, *args, **kwargs):
|
||||
if cls._meta.interface:
|
||||
raise Exception("An interface cannot be initialized")
|
||||
if instance == None:
|
||||
if instance is None:
|
||||
return None
|
||||
return super(BaseObjectType, cls).__new__(cls, instance, *args, **kwargs)
|
||||
|
||||
|
@ -137,7 +137,7 @@ class BaseObjectType(object):
|
|||
@memoize
|
||||
@register_internal_type
|
||||
def internal_type(cls, schema):
|
||||
fields_map = cls._meta.fields_map
|
||||
fields_map = cls._meta.internal_fields_map
|
||||
fields = lambda: {
|
||||
name: field.internal_field(schema)
|
||||
for name, field in fields_map.items()
|
||||
|
|
|
@ -32,3 +32,12 @@ def memoize(fun):
|
|||
return ret
|
||||
cache = {}
|
||||
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 ReporterQuery {
|
||||
reporter {
|
||||
first_name,
|
||||
last_name,
|
||||
firstName,
|
||||
lastName,
|
||||
email
|
||||
}
|
||||
}
|
||||
'''
|
||||
expected = {
|
||||
'reporter': {
|
||||
'first_name': 'ABA',
|
||||
'last_name': 'X',
|
||||
'firstName': 'ABA',
|
||||
'lastName': 'X',
|
||||
'email': ''
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ def test_should_node():
|
|||
query ReporterQuery {
|
||||
reporter {
|
||||
id,
|
||||
first_name,
|
||||
firstName,
|
||||
articles {
|
||||
edges {
|
||||
node {
|
||||
|
@ -141,13 +141,13 @@ def test_should_node():
|
|||
}
|
||||
}
|
||||
}
|
||||
last_name,
|
||||
lastName,
|
||||
email
|
||||
}
|
||||
my_article: node(id:"QXJ0aWNsZU5vZGVUeXBlOjE=") {
|
||||
myArticle: node(id:"QXJ0aWNsZU5vZGVUeXBlOjE=") {
|
||||
id
|
||||
... on ReporterNodeType {
|
||||
first_name
|
||||
firstName
|
||||
}
|
||||
... on ArticleNodeType {
|
||||
headline
|
||||
|
@ -158,8 +158,8 @@ def test_should_node():
|
|||
expected = {
|
||||
'reporter': {
|
||||
'id': 'UmVwb3J0ZXJOb2RlVHlwZTox',
|
||||
'first_name': 'ABA',
|
||||
'last_name': 'X',
|
||||
'firstName': 'ABA',
|
||||
'lastName': 'X',
|
||||
'email': '',
|
||||
'articles': {
|
||||
'edges': [{
|
||||
|
@ -169,7 +169,7 @@ def test_should_node():
|
|||
}]
|
||||
},
|
||||
},
|
||||
'my_article': {
|
||||
'myArticle': {
|
||||
'id': 'QXJ0aWNsZU5vZGVUeXBlOjE=',
|
||||
'headline': 'Article node'
|
||||
}
|
||||
|
|
|
@ -40,16 +40,16 @@ schema = Schema()
|
|||
|
||||
|
||||
def test_django_interface():
|
||||
assert DjangoNode._meta.interface == True
|
||||
assert DjangoNode._meta.interface is True
|
||||
|
||||
|
||||
def test_pseudo_interface():
|
||||
object_type = Character.internal_type(schema)
|
||||
assert Character._meta.interface == True
|
||||
assert Character._meta.interface is True
|
||||
assert isinstance(object_type, GraphQLInterfaceType)
|
||||
assert Character._meta.model == Reporter
|
||||
assert object_type.get_fields().keys() == [
|
||||
'articles', 'first_name', 'last_name', 'id', 'email']
|
||||
'lastName', 'email', 'id', 'firstName', 'articles']
|
||||
|
||||
|
||||
def test_interface_resolve_type():
|
||||
|
@ -59,12 +59,13 @@ def test_interface_resolve_type():
|
|||
|
||||
def test_object_type():
|
||||
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 object_type.get_fields() == {
|
||||
'headline': Human._meta.fields_map['headline'].internal_field(schema),
|
||||
'id': Human._meta.fields_map['id'].internal_field(schema),
|
||||
'reporter': Human._meta.fields_map['reporter'].internal_field(schema),
|
||||
'pub_date': Human._meta.fields_map['pub_date'].internal_field(schema),
|
||||
'headline': internal_fields_map['headline'].internal_field(schema),
|
||||
'id': internal_fields_map['id'].internal_field(schema),
|
||||
'reporter': internal_fields_map['reporter'].internal_field(schema),
|
||||
'pubDate': internal_fields_map['pubDate'].internal_field(schema),
|
||||
}
|
||||
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]
|
||||
|
|
Loading…
Reference in New Issue
Block a user