Merge branch 'refs/heads/master' into features/django-fields

Conflicts:
	graphene/contrib/django/converter.py
	graphene/core/classtypes/enum.py
This commit is contained in:
Syrus Akbary 2016-04-02 20:02:12 -07:00
commit 1b39992dbe
10 changed files with 46 additions and 12 deletions

View File

@ -14,10 +14,10 @@ Graphene comes with a management command for Django to dump your schema data to
## Usage ## Usage
Include `graphene.django.contrib` to `INSTALLED_APPS` in you project settings: Include `graphene.contrib.django` to `INSTALLED_APPS` in you project settings:
```python ```python
INSTALLED_APPS += ('graphene.django.contrib') INSTALLED_APPS += ('graphene.contrib.django')
``` ```
Assuming your Graphene schema is at `tutorial.quickstart.schema`, run the command: Assuming your Graphene schema is at `tutorial.quickstart.schema`, run the command:

View File

@ -4,6 +4,7 @@ from ...core.classtypes.enum import Enum
from ...core.types.custom_scalars import DateTime, JSONString from ...core.types.custom_scalars import DateTime, JSONString
from ...core.types.definitions import List from ...core.types.definitions import List
from ...core.types.scalars import ID, Boolean, Float, Int, String from ...core.types.scalars import ID, Boolean, Float, Int, String
from ...utils import to_const
from .compat import (ArrayField, HStoreField, JSONField, RangeField, from .compat import (ArrayField, HStoreField, JSONField, RangeField,
RelatedObject, UUIDField) RelatedObject, UUIDField)
from .utils import get_related_model, import_single_dispatch from .utils import get_related_model, import_single_dispatch
@ -11,12 +12,17 @@ from .utils import get_related_model, import_single_dispatch
singledispatch = import_single_dispatch() singledispatch = import_single_dispatch()
def convert_choices(choices):
for value, name in choices:
yield to_const(name), value
def convert_django_field_with_choices(field): def convert_django_field_with_choices(field):
choices = getattr(field, 'choices', None) choices = getattr(field, 'choices', None)
if choices: if choices:
meta = field.model._meta meta = field.model._meta
name = '{}_{}_{}'.format(meta.app_label, meta.object_name, field.name) name = '{}_{}_{}'.format(meta.app_label, meta.object_name, field.name)
return Enum(name.upper(), choices, description=field.help_text) return Enum(name.upper(), list(convert_choices(choices)), description=field.help_text)
return convert_django_field(field) return convert_django_field(field)

View File

@ -30,6 +30,8 @@ class Article(models.Model):
('es', 'Spanish'), ('es', 'Spanish'),
('en', 'English') ('en', 'English')
], default='es') ], default='es')
importance = models.IntegerField('Importance', null=True, blank=True,
choices=[(1, u'Very important'), (2, u'Not as important')])
def __str__(self): # __unicode__ on Python 2 def __str__(self): # __unicode__ on Python 2
return self.headline return self.headline

View File

@ -113,8 +113,8 @@ def test_field_with_choices_convert_enum():
assert issubclass(graphene_type, graphene.Enum) assert issubclass(graphene_type, graphene.Enum)
assert graphene_type._meta.type_name == 'TEST_TRANSLATEDMODEL_LANGUAGE' assert graphene_type._meta.type_name == 'TEST_TRANSLATEDMODEL_LANGUAGE'
assert graphene_type._meta.description == 'Language' assert graphene_type._meta.description == 'Language'
assert graphene_type.__enum__.__members__['es'].value == 'Spanish' assert graphene_type.__enum__.__members__['SPANISH'].value == 'es'
assert graphene_type.__enum__.__members__['en'].value == 'English' assert graphene_type.__enum__.__members__['ENGLISH'].value == 'en'
def test_should_float_convert_float(): def test_should_float_convert_float():

View File

@ -13,7 +13,6 @@ except ImportError:
pass pass
def convert_sqlalchemy_relationship(relationship): def convert_sqlalchemy_relationship(relationship):
direction = relationship.direction direction = relationship.direction
model = relationship.mapper.entity model = relationship.mapper.entity

View File

@ -1,8 +1,9 @@
import six import six
from graphql.core.type import GraphQLEnumType, GraphQLEnumValue from graphql.core.type import GraphQLEnumType, GraphQLEnumValue
from .base import ClassTypeMeta, ClassType
from ..types.base import MountedType
from ...utils.enum import Enum as PyEnum from ...utils.enum import Enum as PyEnum
from .base import ClassType, ClassTypeMeta
class EnumMeta(ClassTypeMeta): class EnumMeta(ClassTypeMeta):
@ -17,7 +18,12 @@ class EnumMeta(ClassTypeMeta):
attrs[k] = v.value attrs[k] = v.value
return super(EnumMeta, cls).construct(bases, attrs) return super(EnumMeta, cls).construct(bases, attrs)
def __call__(cls, name, names=None, description=None): def __call__(cls, *args, **kwargs):
if cls is Enum:
return cls.create_enum(*args, **kwargs)
return super(EnumMeta, cls).__call__(*args, **kwargs)
def create_enum(cls, name, names=None, description=None):
attrs = { attrs = {
'__enum__': PyEnum(name, names) '__enum__': PyEnum(name, names)
} }
@ -26,7 +32,7 @@ class EnumMeta(ClassTypeMeta):
return type(name, (Enum,), attrs) return type(name, (Enum,), attrs)
class Enum(six.with_metaclass(EnumMeta, ClassType)): class Enum(six.with_metaclass(EnumMeta, ClassType, MountedType)):
class Meta: class Meta:
abstract = True abstract = True

View File

@ -3,6 +3,7 @@ from graphql.core.type import GraphQLEnumType
from graphene.core.schema import Schema from graphene.core.schema import Schema
from ..enum import Enum from ..enum import Enum
from ..objecttype import ObjectType
def test_enum(): def test_enum():
@ -35,3 +36,14 @@ def test_enum_values():
assert RGB.RED == 0 assert RGB.RED == 0
assert RGB.GREEN == 1 assert RGB.GREEN == 1
assert RGB.BLUE == 2 assert RGB.BLUE == 2
def test_enum_instance():
RGB = Enum('RGB', dict(RED=0, GREEN=1, BLUE=2))
RGB_field = RGB(description='RGB enum description')
class ObjectWithColor(ObjectType):
color = RGB_field
object_field = ObjectWithColor._meta.fields_map['color']
assert object_field.description == 'RGB enum description'

View File

@ -1,4 +1,4 @@
from .str_converters import to_camel_case, to_snake_case from .str_converters import to_camel_case, to_snake_case, to_const
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 .maybe_func import maybe_func from .maybe_func import maybe_func
@ -7,6 +7,6 @@ from .resolve_only_args import resolve_only_args
from .lazylist import LazyList from .lazylist import LazyList
__all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict', __all__ = ['to_camel_case', 'to_snake_case', 'to_const', 'ProxySnakeDict',
'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum', 'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum',
'resolve_only_args', 'LazyList'] 'resolve_only_args', 'LazyList']

View File

@ -15,3 +15,7 @@ def to_camel_case(snake_str):
def to_snake_case(name): def to_snake_case(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def to_const(string):
return re.sub('[\W|^(?=\d)]+', '_', string).upper()

View File

@ -1,4 +1,5 @@
from ..str_converters import to_camel_case, to_snake_case # coding: utf-8
from ..str_converters import to_camel_case, to_snake_case, to_const
def test_snake_case(): def test_snake_case():
@ -15,3 +16,7 @@ def test_camel_case():
assert to_camel_case('snakes_on_a_plane') == 'snakesOnAPlane' assert to_camel_case('snakes_on_a_plane') == 'snakesOnAPlane'
assert to_camel_case('snakes_on_a__plane') == 'snakesOnA_Plane' assert to_camel_case('snakes_on_a__plane') == 'snakesOnA_Plane'
assert to_camel_case('i_phone_hysteria') == 'iPhoneHysteria' assert to_camel_case('i_phone_hysteria') == 'iPhoneHysteria'
def test_to_const():
assert to_const('snakes $1. on a "#plane') == 'SNAKES_ON_A_PLANE'