Improved fields ordering. Thanks @leebyron for pointing this!

Not everything yet fixed. Have to fix too in graphql-core/relay
This commit is contained in:
Syrus Akbary 2015-10-08 23:24:21 -07:00
parent fbd9465e57
commit 0bc0218032
6 changed files with 40 additions and 25 deletions

View File

@ -25,10 +25,10 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
if not cls._meta.model: if not cls._meta.model:
return return
only_fields = cls._meta.only_fields only_fields = cls._meta.only_fields
reverse_fields = tuple(get_reverse_fields(cls._meta.model)) reverse_fields = get_reverse_fields(cls._meta.model)
all_fields = (list(cls._meta.model._meta.fields) + all_fields = sorted(list(cls._meta.model._meta.fields) +
list(reverse_fields) + list(reverse_fields) +
list(cls._meta.model._meta.local_many_to_many)) list(cls._meta.model._meta.local_many_to_many))
for field in all_fields: for field in all_fields:
is_not_in_only = only_fields and field.name not in only_fields is_not_in_only = only_fields and field.name not in only_fields

View File

@ -1,5 +1,6 @@
import inspect import inspect
import six import six
from functools import total_ordering
from graphql.core.type import ( from graphql.core.type import (
GraphQLField, GraphQLField,
GraphQLList, GraphQLList,
@ -16,8 +17,10 @@ from graphene.core.types import BaseObjectType
from graphene.core.scalars import GraphQLSkipField from graphene.core.scalars import GraphQLSkipField
@total_ordering
class Field(object): class Field(object):
SKIP = GraphQLSkipField SKIP = GraphQLSkipField
creation_counter = 0
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='', **extra_args):
self.field_type = field_type self.field_type = field_type
@ -29,6 +32,8 @@ class Field(object):
self.name = name self.name = name
self.description = description or self.__doc__ self.description = description or self.__doc__
self.object_type = None self.object_type = None
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
if not self.name: if not self.name:
@ -122,6 +127,21 @@ class Field(object):
return '<%s: %s>' % (path, name) return '<%s: %s>' % (path, name)
return '<%s>' % path return '<%s>' % path
def __eq__(self, other):
# Needed for @total_ordering
if isinstance(other, Field):
return self.creation_counter == other.creation_counter
return NotImplemented
def __lt__(self, other):
# This is needed because bisect does not take a comparison function.
if isinstance(other, Field):
return self.creation_counter < other.creation_counter
return NotImplemented
def __hash__(self):
return hash(self.creation_counter)
class NativeField(Field): class NativeField(Field):

View File

@ -1,4 +1,5 @@
from graphene.utils import cached_property from graphene.utils import cached_property
from collections import OrderedDict
DEFAULT_NAMES = ('description', 'name', 'interface', DEFAULT_NAMES = ('description', 'name', 'interface',
'type_name', 'interfaces', 'proxy') 'type_name', 'interfaces', 'proxy')
@ -66,12 +67,8 @@ class Options(object):
fields = [] fields = []
for parent in self.parents: for parent in self.parents:
fields.extend(parent._meta.fields) fields.extend(parent._meta.fields)
return self.local_fields + fields return sorted(self.local_fields + fields)
@cached_property @cached_property
def fields_map(self): def fields_map(self):
return {f.field_name: f for f in self.fields} return OrderedDict([(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}

View File

@ -1,5 +1,6 @@
import inspect import inspect
import six import six
from collections import OrderedDict
from graphql.core.type import ( from graphql.core.type import (
GraphQLObjectType, GraphQLObjectType,
@ -49,10 +50,9 @@ 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.name:f for f in new_fields} field_names = {f.name: f for f in new_fields}
for base in parents: for base in parents:
original_base = base
if not hasattr(base, '_meta'): if not hasattr(base, '_meta'):
# Things without _meta aren't functional models, so they're # Things without _meta aren't functional models, so they're
# uninteresting parents. # uninteresting parents.
@ -132,11 +132,9 @@ 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.internal_fields_map fields_list = cls._meta.fields
fields = lambda: { fields = lambda: OrderedDict([(f.name, f.internal_field(schema))
name: field.internal_field(schema) for f in fields_list])
for name, field in fields_map.items()
}
if cls._meta.interface: if cls._meta.interface:
return GraphQLInterfaceType( return GraphQLInterfaceType(
cls._meta.type_name, cls._meta.type_name,

View File

@ -1,4 +1,4 @@
import collections from collections import Iterable, OrderedDict
from graphql_relay.connection.arrayconnection import ( from graphql_relay.connection.arrayconnection import (
connectionFromArray connectionFromArray
@ -30,7 +30,7 @@ class ConnectionField(Field):
if resolved: if resolved:
resolved = self.wrap_resolved(resolved, instance, args, info) resolved = self.wrap_resolved(resolved, instance, args, info)
assert isinstance( assert isinstance(
resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable' resolved, Iterable), 'Resolved value from the connection field have to be iterable'
return connectionFromArray(resolved, args) return connectionFromArray(resolved, args)
@memoize @memoize
@ -71,7 +71,7 @@ class NodeTypeField(LazyField):
if resolved_global_id.type == self.field_object_type._meta.type_name: if resolved_global_id.type == self.field_object_type._meta.type_name:
return node_field.resolver(instance, args, info) return node_field.resolver(instance, args, info)
args = {a.name: a for a in node_field.args} args = OrderedDict([(a.name, a) for a in node_field.args])
field = Field(self.field_object_type, id=args['id'], resolve=resolver) field = Field(self.field_object_type, id=args['id'], resolve=resolver)
field.contribute_to_class(self.object_type, self.field_name) field.contribute_to_class(self.object_type, self.field_name)

View File

@ -63,13 +63,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)
internal_fields_map = Human._meta.internal_fields_map fields_map = Human._meta.fields_map
assert Human._meta.interface is False 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': internal_fields_map['headline'].internal_field(schema), 'headline': fields_map['headline'].internal_field(schema),
'id': internal_fields_map['id'].internal_field(schema), 'id': fields_map['id'].internal_field(schema),
'reporter': internal_fields_map['reporter'].internal_field(schema), 'reporter': fields_map['reporter'].internal_field(schema),
'pubDate': internal_fields_map['pubDate'].internal_field(schema), 'pubDate': fields_map['pub_date'].internal_field(schema),
} }
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)] assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]