Merge branch 'master' into recursive-nodes

This commit is contained in:
Nick Hudkins 2017-01-13 10:16:13 -05:00
commit 0200b3244c
19 changed files with 92 additions and 157 deletions

View File

@ -6,7 +6,7 @@ python:
- 3.5 - 3.5
- pypy - pypy
before_install: before_install:
- | - |
if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
export PYENV_ROOT="$HOME/.pyenv" export PYENV_ROOT="$HOME/.pyenv"
if [ -f "$PYENV_ROOT/bin/pyenv" ]; then if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
@ -45,7 +45,7 @@ after_success:
fi fi
env: env:
matrix: matrix:
- TEST_TYPE=build - TEST_TYPE=build DJANGO_VERSION=1.10
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
@ -59,3 +59,10 @@ matrix:
env: TEST_TYPE=build DJANGO_VERSION=1.9 env: TEST_TYPE=build DJANGO_VERSION=1.9
- python: '2.7' - python: '2.7'
env: TEST_TYPE=lint env: TEST_TYPE=lint
deploy:
provider: pypi
user: syrusakbary
on:
tags: true
password:
secure: kymIFCEPUbkgRqe2NAXkWfxMmGRfWvWBOP6LIXdVdkOOkm91fU7bndPGrAjos+/7gN0Org609ZmHSlVXNMJUWcsL2or/x5LcADJ4cZDe+79qynuoRb9xs1Ri4O4SBAuVMZxuVJvs8oUzT2R11ql5vASSMtXgbX+ZDGpmPRVZStkCuXgOc4LBhbPKyl3OFy7UQFPgAEmy3Yjh4ZSKzlXheK+S6mmr60+DCIjpaA0BWPxYK9FUE0qm7JJbHLUbwsUP/QMp5MmGjwFisXCNsIe686B7QKRaiOw62eJc2R7He8AuEC8T9OM4kRwDlecSn8mMpkoSB7QWtlJ+6XdLrJFPNvtrOfgfzS9/96Qrw9WlOslk68hMlhJeRb0s2YUD8tiV3UUkvbL1mfFoS4SI9U+rojS55KhUEJWHg1w7DjoOPoZmaIL2ChRupmvrFYNAGae1cxwG3Urh+t3wYlN3gpKsRDe5GOT7Wm2tr0ad3McCpDGUwSChX59BAJXe/MoLxkKScTrMyR8yMxHOF0b4zpVn5l7xB/o2Ik4zavx5q/0rGBMK2D+5d+gpQogKShoquTPsZUwO7sB5hYeH2hqGqpeGzZtb76E2zZYd18pJ0FsBudm5+KWjYdZ+vbtGrLxdTXJ1EEtzVXm0lscykTpqUucbXSa51dhStJvW2xEEz6p3rHo=

View File

@ -12,7 +12,7 @@ Let's use a simple example model.
from django.db import models from django.db import models
class Post(models.Model): class Post(models.Model):
name = models.CharField(max_length=100) title = models.CharField(max_length=100)
content = models.TextField() content = models.TextField()
published = models.BooleanField(default=False) published = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User') owner = models.ForeignKey('auth.User')

View File

@ -91,50 +91,13 @@ Which you could query as follows:
} }
} }
Orderable fields
----------------
Ordering can also be specified using ``filter_order_by``. Like
``filter_fields``, this value is also passed directly to
``django-filter`` as the ``order_by`` field. For full details see the
`order\_by
documentation <https://django-filter.readthedocs.org/en/latest/usage.html#ordering-using-order-by>`__.
For example:
.. code:: python
class AnimalNode(DjangoObjectType):
class Meta:
model = Animal
filter_fields = ['name', 'genus', 'is_domesticated']
# Either a tuple/list of fields upon which ordering is allowed, or
# True to allow filtering on all fields specified in filter_fields
filter_order_by = True
interfaces = (relay.Node, )
You can then control the ordering via the ``orderBy`` argument:
.. code::
query {
allAnimals(orderBy: "name") {
edges {
node {
id,
name
}
}
}
}
Custom Filtersets Custom Filtersets
----------------- -----------------
By default Graphene provides easy access to the most commonly used By default Graphene provides easy access to the most commonly used
features of ``django-filter``. This is done by transparently creating a features of ``django-filter``. This is done by transparently creating a
``django_filters.FilterSet`` class for you and passing in the values for ``django_filters.FilterSet`` class for you and passing in the values for
``filter_fields`` and ``filter_order_by``. ``filter_fields``.
However, you may find this to be insufficient. In these cases you can However, you may find this to be insufficient. In these cases you can
create your own ``Filterset`` as follows: create your own ``Filterset`` as follows:

View File

@ -31,6 +31,7 @@ We will setup the project, create the following:
# Set up a new project with a single application # Set up a new project with a single application
django-admin.py startproject cookbook . # Note the trailing '.' character django-admin.py startproject cookbook . # Note the trailing '.' character
cd cookbook
django-admin.py startapp ingredients django-admin.py startapp ingredients
Now sync your database for the first time: Now sync your database for the first time:
@ -98,7 +99,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
class Meta: class Meta:
model = Category model = Category
filter_fields = ['name', 'ingredients'] filter_fields = ['name', 'ingredients']
filter_order_by = ['name']
interfaces = (relay.Node, ) interfaces = (relay.Node, )
@ -112,7 +112,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
'category': ['exact'], 'category': ['exact'],
'category__name': ['exact'], 'category__name': ['exact'],
} }
filter_order_by = ['name', 'category__name']
interfaces = (relay.Node, ) interfaces = (relay.Node, )

View File

@ -60,5 +60,5 @@ Now you should be ready to start the server:
Now head on over to Now head on over to
[http://127.0.0.1:8000/graphql](http://127.0.0.1:8000/graphql) [http://127.0.0.1:8000/graphql](http://127.0.0.1:8000/graphql)
and run some queries! and run some queries!
(See the [Graphene-Django Tutorial](http://docs.graphene-python.org/projects/django/en/latest/tutorial.html#testing-our-graphql-schema) (See the [Graphene-Django Tutorial](http://docs.graphene-python.org/projects/django/en/latest/tutorial#testing-our-graphql-schema)
for some example queries) for some example queries)

View File

@ -12,7 +12,6 @@ class CategoryNode(DjangoObjectType):
model = Category model = Category
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['name', 'ingredients'] filter_fields = ['name', 'ingredients']
filter_order_by = ['name']
class IngredientNode(DjangoObjectType): class IngredientNode(DjangoObjectType):
@ -27,7 +26,6 @@ class IngredientNode(DjangoObjectType):
'category': ['exact'], 'category': ['exact'],
'category__name': ['exact'], 'category__name': ['exact'],
} }
filter_order_by = ['name', 'category__name']
class Query(AbstractType): class Query(AbstractType):

View File

@ -9,7 +9,7 @@ class RecipeNode(DjangoObjectType):
model = Recipe model = Recipe
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['title','amounts'] filter_fields = ['title','amounts']
filter_order_by = ['title']
class RecipeIngredientNode(DjangoObjectType): class RecipeIngredientNode(DjangoObjectType):
@ -22,7 +22,7 @@ class RecipeIngredientNode(DjangoObjectType):
'recipe': ['exact'], 'recipe': ['exact'],
'recipe__title': ['icontains'], 'recipe__title': ['icontains'],
} }
filter_order_by = ['ingredient__name', 'recipe__title',]
class Query(AbstractType): class Query(AbstractType):
recipe = Node.Field(RecipeNode) recipe = Node.Field(RecipeNode)

View File

@ -4,6 +4,7 @@ from django.db import models
class MissingType(object): class MissingType(object):
pass pass
try: try:
DurationField = models.DurationField DurationField = models.DurationField
UUIDField = models.UUIDField UUIDField = models.UUIDField
@ -21,6 +22,13 @@ except:
try: try:
# Postgres fields are only available in Django 1.8+ # Postgres fields are only available in Django 1.8+
from django.contrib.postgres.fields import ArrayField, HStoreField, JSONField, RangeField from django.contrib.postgres.fields import ArrayField, HStoreField, RangeField
except ImportError: except ImportError:
ArrayField, HStoreField, JSONField, RangeField = (MissingType, ) * 4 ArrayField, HStoreField, JSONField, RangeField = (MissingType, ) * 4
try:
# Postgres fields are only available in Django 1.9+
from django.contrib.postgres.fields import JSONField
except ImportError:
JSONField = MissingType

View File

@ -4,9 +4,9 @@ from django.utils.encoding import force_text
from graphene import (ID, Boolean, Dynamic, Enum, Field, Float, Int, List, from graphene import (ID, Boolean, Dynamic, Enum, Field, Float, Int, List,
NonNull, String) NonNull, String)
from graphene.relay import is_node from graphene.relay import is_node
from graphene.types.datetime import DateTime from graphene.types.datetime import DateTime, Time
from graphene.types.json import JSONString from graphene.types.json import JSONString
from graphene.utils.str_converters import to_const from graphene.utils.str_converters import to_camel_case, to_const
from graphql import assert_valid_name from graphql import assert_valid_name
from .compat import (ArrayField, HStoreField, JSONField, RangeField, from .compat import (ArrayField, HStoreField, JSONField, RangeField,
@ -41,7 +41,7 @@ def convert_django_field_with_choices(field, registry=None):
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.object_name, field.name.capitalize()) name = to_camel_case('{}_{}'.format(meta.object_name, field.name))
choices = list(get_choices(choices)) choices = list(get_choices(choices))
named_choices = [(c[0], c[1]) for c in choices] named_choices = [(c[0], c[1]) for c in choices]
named_choices_descriptions = {c[0]: c[2] for c in choices} named_choices_descriptions = {c[0]: c[2] for c in choices}
@ -112,6 +112,11 @@ def convert_date_to_string(field, registry=None):
return DateTime(description=field.help_text, required=not field.null) return DateTime(description=field.help_text, required=not field.null)
@convert_django_field.register(models.TimeField)
def convert_time_to_string(field, registry=None):
return Time(description=field.help_text, required=not field.null)
@convert_django_field.register(models.OneToOneRel) @convert_django_field.register(models.OneToOneRel)
def convert_onetoone_field_to_djangomodel(field, registry=None): def convert_onetoone_field_to_djangomodel(field, registry=None):
model = get_related_model(field) model = get_related_model(field)

View File

@ -8,7 +8,7 @@ if not DJANGO_FILTER_INSTALLED:
) )
else: else:
from .fields import DjangoFilterConnectionField from .fields import DjangoFilterConnectionField
from .filterset import GrapheneFilterSet, GlobalIDFilter, GlobalIDMultipleChoiceFilter from .filterset import GlobalIDFilter, GlobalIDMultipleChoiceFilter
__all__ = ['DjangoFilterConnectionField', 'GrapheneFilterSet', __all__ = ['DjangoFilterConnectionField',
'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter'] 'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter']

View File

@ -13,7 +13,6 @@ class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, fields=None, order_by=None, def __init__(self, type, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None, extra_filter_meta=None, filterset_class=None,
*args, **kwargs): *args, **kwargs):
self._order_by = order_by
self._fields = fields self._fields = fields
self._type = type self._type = type
self._filterset_class = filterset_class self._filterset_class = filterset_class
@ -27,15 +26,10 @@ class DjangoFilterConnectionField(DjangoConnectionField):
return self._type() return self._type()
return self._type return self._type
@property
def order_by(self):
return self._order_by or self.node_type._meta.filter_order_by
@property @property
def meta(self): def meta(self):
meta = dict(model=self.node_type._meta.model, meta = dict(model=self.node_type._meta.model,
fields=self.fields, fields=self.fields)
order_by=self.order_by)
if self._extra_filter_meta: if self._extra_filter_meta:
meta.update(self._extra_filter_meta) meta.update(self._extra_filter_meta)
return meta return meta
@ -64,12 +58,8 @@ class DjangoFilterConnectionField(DjangoConnectionField):
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
root, args, context, info): root, args, context, info):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
order = args.get('order_by', None)
qs = default_manager.get_queryset() qs = default_manager.get_queryset()
if order: qs = filterset_class(data=filter_kwargs, queryset=qs).qs
qs = qs.order_by(order)
qs = filterset_class(data=filter_kwargs, queryset=qs)
return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)
def get_resolver(self, parent_resolver): def get_resolver(self, parent_resolver):

View File

@ -1,11 +1,9 @@
import itertools import itertools
import six
from django.conf import settings
from django.db import models from django.db import models
from django.utils.text import capfirst from django.utils.text import capfirst
from django_filters import Filter, MultipleChoiceFilter from django_filters import Filter, MultipleChoiceFilter
from django_filters.filterset import FilterSet, FilterSetMetaclass from django_filters.filterset import BaseFilterSet, FilterSet
from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS
from graphql_relay.node.node import from_global_id from graphql_relay.node.node import from_global_id
@ -29,9 +27,6 @@ class GlobalIDMultipleChoiceFilter(MultipleChoiceFilter):
return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids) return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids)
ORDER_BY_FIELD = getattr(settings, 'GRAPHENE_ORDER_BY_FIELD', 'order_by')
GRAPHENE_FILTER_SET_OVERRIDES = { GRAPHENE_FILTER_SET_OVERRIDES = {
models.AutoField: { models.AutoField: {
'filter_class': GlobalIDFilter, 'filter_class': GlobalIDFilter,
@ -48,25 +43,7 @@ GRAPHENE_FILTER_SET_OVERRIDES = {
} }
# Only useful for Django-filter 0.14-, not necessary in latest version 0.15+ class GrapheneFilterSetMixin(BaseFilterSet):
class GrapheneFilterSetMetaclass(FilterSetMetaclass):
def __new__(cls, name, bases, attrs):
new_class = super(GrapheneFilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
# Customise the filter_overrides for Graphene
if hasattr(new_class, '_meta') and hasattr(new_class._meta, 'filter_overrides'):
filter_overrides = new_class._meta.filter_overrides
else:
filter_overrides = new_class.filter_overrides
for k, v in GRAPHENE_FILTER_SET_OVERRIDES.items():
filter_overrides.setdefault(k, v)
return new_class
class GrapheneFilterSetMixin(object):
order_by_field = ORDER_BY_FIELD
FILTER_DEFAULTS = dict(itertools.chain( FILTER_DEFAULTS = dict(itertools.chain(
FILTER_FOR_DBFIELD_DEFAULTS.items(), FILTER_FOR_DBFIELD_DEFAULTS.items(),
GRAPHENE_FILTER_SET_OVERRIDES.items() GRAPHENE_FILTER_SET_OVERRIDES.items()
@ -93,26 +70,17 @@ class GrapheneFilterSetMixin(object):
return GlobalIDFilter(**default) return GlobalIDFilter(**default)
class GrapheneFilterSet(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, FilterSet)):
""" Base class for FilterSets used by Graphene
You shouldn't usually need to use this class. The
DjangoFilterConnectionField will wrap FilterSets with this class as
necessary
"""
def setup_filterset(filterset_class): def setup_filterset(filterset_class):
""" Wrap a provided filterset in Graphene-specific functionality """ Wrap a provided filterset in Graphene-specific functionality
""" """
return type( return type(
'Graphene{}'.format(filterset_class.__name__), 'Graphene{}'.format(filterset_class.__name__),
(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, filterset_class),), (filterset_class, GrapheneFilterSetMixin),
{}, {},
) )
def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet, def custom_filterset_factory(model, filterset_base_class=FilterSet,
**meta): **meta):
""" Create a filterset for the given model using the provided meta data """ Create a filterset for the given model using the provided meta data
""" """
@ -122,7 +90,7 @@ def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet,
meta_class = type(str('Meta'), (object,), meta) meta_class = type(str('Meta'), (object,), meta)
filterset = type( filterset = type(
str('%sFilterSet' % model._meta.object_name), str('%sFilterSet' % model._meta.object_name),
(filterset_base_class,), (filterset_base_class, GrapheneFilterSetMixin),
{ {
'Meta': meta_class 'Meta': meta_class
} }

View File

@ -1,4 +1,5 @@
import django_filters import django_filters
from django_filters import OrderingFilter
from graphene_django.tests.models import Article, Pet, Reporter from graphene_django.tests.models import Article, Pet, Reporter
@ -12,7 +13,8 @@ class ArticleFilter(django_filters.FilterSet):
'pub_date': ['gt', 'lt', 'exact'], 'pub_date': ['gt', 'lt', 'exact'],
'reporter': ['exact'], 'reporter': ['exact'],
} }
order_by = False
order_by = OrderingFilter(fields=('pub_date',))
class ReporterFilter(django_filters.FilterSet): class ReporterFilter(django_filters.FilterSet):
@ -20,7 +22,8 @@ class ReporterFilter(django_filters.FilterSet):
class Meta: class Meta:
model = Reporter model = Reporter
fields = ['first_name', 'last_name', 'email', 'pets'] fields = ['first_name', 'last_name', 'email', 'pets']
order_by = True
order_by = OrderingFilter(fields=('pub_date',))
class PetFilter(django_filters.FilterSet): class PetFilter(django_filters.FilterSet):
@ -28,4 +31,3 @@ class PetFilter(django_filters.FilterSet):
class Meta: class Meta:
model = Pet model = Pet
fields = ['name'] fields = ['name']
order_by = False

View File

@ -11,6 +11,7 @@ from graphene_django.tests.models import Article, Pet, Reporter
from graphene_django.utils import DJANGO_FILTER_INSTALLED from graphene_django.utils import DJANGO_FILTER_INSTALLED
pytestmark = [] pytestmark = []
if DJANGO_FILTER_INSTALLED: if DJANGO_FILTER_INSTALLED:
import django_filters import django_filters
from graphene_django.filter import (GlobalIDFilter, DjangoFilterConnectionField, from graphene_django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
@ -22,27 +23,29 @@ else:
pytestmark.append(pytest.mark.django_db) pytestmark.append(pytest.mark.django_db)
class ArticleNode(DjangoObjectType): if DJANGO_FILTER_INSTALLED:
class ArticleNode(DjangoObjectType):
class Meta: class Meta:
model = Article model = Article
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ('headline', )
class ReporterNode(DjangoObjectType): class ReporterNode(DjangoObjectType):
class Meta: class Meta:
model = Reporter model = Reporter
interfaces = (Node, ) interfaces = (Node, )
class PetNode(DjangoObjectType): class PetNode(DjangoObjectType):
class Meta: class Meta:
model = Pet model = Pet
interfaces = (Node, ) interfaces = (Node, )
# schema = Schema() # schema = Schema()
def get_args(field): def get_args(field):
@ -110,8 +113,8 @@ def test_filter_explicit_filterset_orderable():
def test_filter_shortcut_filterset_orderable_true(): def test_filter_shortcut_filterset_orderable_true():
field = DjangoFilterConnectionField(ReporterNode, order_by=True) field = DjangoFilterConnectionField(ReporterNode)
assert_orderable(field) assert_not_orderable(field)
# def test_filter_shortcut_filterset_orderable_headline(): # def test_filter_shortcut_filterset_orderable_headline():
@ -126,9 +129,9 @@ def test_filter_explicit_filterset_not_orderable():
def test_filter_shortcut_filterset_extra_meta(): def test_filter_shortcut_filterset_extra_meta():
field = DjangoFilterConnectionField(ArticleNode, extra_filter_meta={ field = DjangoFilterConnectionField(ArticleNode, extra_filter_meta={
'order_by': True 'exclude': ('headline', )
}) })
assert_orderable(field) assert 'headline' not in field.filterset_class.get_fields()
def test_filter_filterset_information_on_meta(): def test_filter_filterset_information_on_meta():
@ -138,11 +141,10 @@ def test_filter_filterset_information_on_meta():
model = Reporter model = Reporter
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['first_name', 'articles'] filter_fields = ['first_name', 'articles']
filter_order_by = True
field = DjangoFilterConnectionField(ReporterFilterNode) field = DjangoFilterConnectionField(ReporterFilterNode)
assert_arguments(field, 'first_name', 'articles') assert_arguments(field, 'first_name', 'articles')
assert_orderable(field) assert_not_orderable(field)
def test_filter_filterset_information_on_meta_related(): def test_filter_filterset_information_on_meta_related():
@ -152,7 +154,6 @@ def test_filter_filterset_information_on_meta_related():
model = Reporter model = Reporter
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['first_name', 'articles'] filter_fields = ['first_name', 'articles']
filter_order_by = True
class ArticleFilterNode(DjangoObjectType): class ArticleFilterNode(DjangoObjectType):
@ -160,7 +161,6 @@ def test_filter_filterset_information_on_meta_related():
model = Article model = Article
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['headline', 'reporter'] filter_fields = ['headline', 'reporter']
filter_order_by = True
class Query(ObjectType): class Query(ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterFilterNode) all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
@ -171,7 +171,7 @@ def test_filter_filterset_information_on_meta_related():
schema = Schema(query=Query) schema = Schema(query=Query)
articles_field = ReporterFilterNode._meta.fields['articles'].get_type() articles_field = ReporterFilterNode._meta.fields['articles'].get_type()
assert_arguments(articles_field, 'headline', 'reporter') assert_arguments(articles_field, 'headline', 'reporter')
assert_orderable(articles_field) assert_not_orderable(articles_field)
def test_filter_filterset_related_results(): def test_filter_filterset_related_results():
@ -181,7 +181,6 @@ def test_filter_filterset_related_results():
model = Reporter model = Reporter
interfaces = (Node, ) interfaces = (Node, )
filter_fields = ['first_name', 'articles'] filter_fields = ['first_name', 'articles']
filter_order_by = True
class ArticleFilterNode(DjangoObjectType): class ArticleFilterNode(DjangoObjectType):
@ -189,7 +188,6 @@ def test_filter_filterset_related_results():
interfaces = (Node, ) interfaces = (Node, )
model = Article model = Article
filter_fields = ['headline', 'reporter'] filter_fields = ['headline', 'reporter']
filter_order_by = True
class Query(ObjectType): class Query(ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterFilterNode) all_reporters = DjangoFilterConnectionField(ReporterFilterNode)

View File

@ -1,7 +1,5 @@
import six import six
from graphene import String
from .filterset import custom_filterset_factory, setup_filterset from .filterset import custom_filterset_factory, setup_filterset
@ -18,10 +16,6 @@ def get_filtering_args_from_filterset(filterset_class, type):
field_type.description = filter_field.label field_type.description = filter_field.label
args[name] = field_type args[name] = field_type
# Also add the 'order_by' field
if filterset_class._meta.order_by:
args[filterset_class.order_by_field] = String()
return args return args

View File

@ -5,7 +5,7 @@ from py.test import raises
import graphene import graphene
from graphene.relay import ConnectionField, Node from graphene.relay import ConnectionField, Node
from graphene.types.datetime import DateTime from graphene.types.datetime import DateTime, Time
from graphene.types.json import JSONString from graphene.types.json import JSONString
from ..compat import (ArrayField, HStoreField, JSONField, MissingType, from ..compat import (ArrayField, HStoreField, JSONField, MissingType,
@ -16,7 +16,7 @@ from ..types import DjangoObjectType
from .models import Article, Film, FilmDetails, Reporter from .models import Article, Film, FilmDetails, Reporter
# from graphene.core.types.custom_scalars import DateTime, JSONString # from graphene.core.types.custom_scalars import DateTime, Time, JSONString
def assert_conversion(django_field, graphene_field, *args, **kwargs): def assert_conversion(django_field, graphene_field, *args, **kwargs):
@ -44,6 +44,10 @@ def test_should_date_convert_string():
assert_conversion(models.DateField, DateTime) assert_conversion(models.DateField, DateTime)
def test_should_time_convert_string():
assert_conversion(models.TimeField, Time)
def test_should_char_convert_string(): def test_should_char_convert_string():
assert_conversion(models.CharField, graphene.String) assert_conversion(models.CharField, graphene.String)

View File

@ -73,7 +73,7 @@ type Article implements Node {
type ArticleConnection { type ArticleConnection {
pageInfo: PageInfo! pageInfo: PageInfo!
edges: [ArticleEdge] edges: [ArticleEdge]!
} }
type ArticleEdge { type ArticleEdge {
@ -110,11 +110,11 @@ type Reporter {
lastName: String! lastName: String!
email: String! email: String!
pets: [Reporter] pets: [Reporter]
aChoice: ReporterA_choice! aChoice: ReporterAChoice!
articles(before: String, after: String, first: Int, last: Int): ArticleConnection articles(before: String, after: String, first: Int, last: Int): ArticleConnection
} }
enum ReporterA_choice { enum ReporterAChoice {
A_1 A_1
A_2 A_2
} }

View File

@ -65,7 +65,6 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
# we allow more attributes in Meta # we allow more attributes in Meta
defaults.update( defaults.update(
filter_fields=(), filter_fields=(),
filter_order_by=(),
) )
options = Options( options = Options(

View File

@ -2,7 +2,7 @@ from setuptools import find_packages, setup
setup( setup(
name='graphene-django', name='graphene-django',
version='1.0', version='1.2.1',
description='Graphene Django integration', description='Graphene Django integration',
long_description=open('README.rst').read(), long_description=open('README.rst').read(),
@ -33,7 +33,7 @@ setup(
install_requires=[ install_requires=[
'six>=1.10.0', 'six>=1.10.0',
'graphene>=1.0', 'graphene>=1.1.3',
'Django>=1.6.0', 'Django>=1.6.0',
'iso8601', 'iso8601',
'singledispatch>=3.4.0.3', 'singledispatch>=3.4.0.3',
@ -42,7 +42,7 @@ setup(
'pytest-runner', 'pytest-runner',
], ],
tests_require=[ tests_require=[
'django-filter>=0.10.0', 'django-filter>=1.0.0',
'pytest', 'pytest',
'pytest-django==2.9.1', 'pytest-django==2.9.1',
'mock', 'mock',