Merge pull request #2 from syrusakbary/feature/django

Improved integration with Django
This commit is contained in:
Adam Charnock 2016-01-02 20:36:06 +00:00
commit 2264c3dce2
51 changed files with 136 additions and 349 deletions

View File

@ -80,13 +80,13 @@ matrix:
fast_finish: true
include:
- python: '2.7'
env: DJANGO_VERSION=1.6
env: TEST_TYPE=build DJANGO_VERSION=1.6
- python: '2.7'
env: DJANGO_VERSION=1.7
env: TEST_TYPE=build DJANGO_VERSION=1.7
- python: '2.7'
env: DJANGO_VERSION=1.8
env: TEST_TYPE=build DJANGO_VERSION=1.8
- python: '2.7'
env: DJANGO_VERSION=1.9
env: TEST_TYPE=build DJANGO_VERSION=1.9
- python: '2.7'
env: TEST_TYPE=build_website
- python: '2.7'

View File

@ -1,5 +1,7 @@
#!/bin/bash
# Install the required scripts with
# pip install autoflake autopep8 isort
autoflake ./examples/ ./graphene/ -r --remove-unused-variables --remove-all-unused-imports --in-place
autopep8 ./examples/ ./graphene/ -r --in-place --experimental --aggressive --max-line-length 120
isort -rc ./examples/ ./graphene/

View File

@ -1,6 +1,6 @@
from django.contrib import admin
from cookbook.ingredients.models import Ingredient, Category
from cookbook.ingredients.models import Category, Ingredient
admin.site.register(Ingredient)
admin.site.register(Category)

View File

@ -2,8 +2,8 @@
# Generated by Django 1.9 on 2015-12-04 18:15
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@ -1,13 +1,13 @@
from graphene import relay, ObjectType
from cookbook.ingredients.models import Category, Ingredient
from graphene import ObjectType, relay
from graphene.contrib.django.filter import DjangoFilterConnectionField
from graphene.contrib.django.types import DjangoNode
from cookbook.ingredients.models import Category, Ingredient
# Graphene will automatically map the User model's fields onto the UserType.
# This is configured in the UserType's Meta class (as you can see below)
class CategoryNode(DjangoNode):
class Meta:
model = Category
filter_fields = ['name', 'ingredients']
@ -15,6 +15,7 @@ class CategoryNode(DjangoNode):
class IngredientNode(DjangoNode):
class Meta:
model = Ingredient
# Allow for some more advanced filtering here

View File

@ -1,6 +1,5 @@
import graphene
import cookbook.ingredients.schema
import graphene
class Query(cookbook.ingredients.schema.Query):

View File

@ -1,10 +1,9 @@
from django.conf.urls import url, include
from django.conf.urls import include, url
from django.contrib import admin
from django.views.decorators.csrf import csrf_exempt
from graphene.contrib.django.views import GraphQLView
from cookbook.schema import schema
from graphene.contrib.django.views import GraphQLView
urlpatterns = [
url(r'^admin/', admin.site.urls),

View File

@ -1,28 +1,44 @@
import warnings
from ...core.exceptions import SkipField
from ...core.fields import Field
from ...core.types.base import FieldType
from ...core.types.definitions import List
from ...relay import ConnectionField
from ...relay.utils import is_node
from .filter.fields import DjangoFilterConnectionField
from .utils import get_type_for_model
from .utils import get_type_for_model, maybe_queryset
class DjangoConnectionField(ConnectionField):
def __init__(self, *args, **kwargs):
cls = self.__class__
warnings.warn("Using {} will be not longer supported."
" Use relay.ConnectionField instead".format(cls.__name__),
FutureWarning)
self.on = kwargs.pop('on', False)
return super(DjangoConnectionField, self).__init__(*args, **kwargs)
@property
def model(self):
return self.type._meta.model
def get_manager(self):
if self.on:
return getattr(self.model, self.on)
else:
return self.model._default_manager
def get_queryset(self, resolved_qs, args, info):
return resolved_qs
def from_list(self, connection_type, resolved, args, info):
if not resolved:
resolved = self.get_manager()
resolved_qs = maybe_queryset(resolved)
qs = self.get_queryset(resolved_qs, args, info)
return super(DjangoConnectionField, self).from_list(connection_type, qs, args, info)
class ConnectionOrListField(Field):
def internal_type(self, schema):
from .filter.fields import DjangoFilterConnectionField
model_field = self.type
field_object_type = model_field.get_object_type(schema)
if not field_object_type:
@ -31,7 +47,7 @@ class ConnectionOrListField(Field):
if field_object_type._meta.filter_fields:
field = DjangoFilterConnectionField(field_object_type)
else:
field = ConnectionField(field_object_type)
field = DjangoConnectionField(field_object_type)
else:
field = Field(List(field_object_type))
field.contribute_to_class(self.object_type, self.attname)

View File

@ -8,8 +8,6 @@ if not DJANGO_FILTER_INSTALLED:
from .fields import DjangoFilterConnectionField
from .filterset import GrapheneFilterSet, GlobalIDFilter, GlobalIDMultipleChoiceFilter
from .resolvers import FilterConnectionResolver
__all__ = ['DjangoFilterConnectionField', 'GrapheneFilterSet',
'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter',
'FilterConnectionResolver']
'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter']

View File

@ -1,25 +1,36 @@
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
from graphene.contrib.django.utils import get_filtering_args_from_filterset
from graphene.relay import ConnectionField
from ..fields import DjangoConnectionField
from .utils import get_filtering_args_from_filterset, get_filterset_class
class DjangoFilterConnectionField(ConnectionField):
class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, on=None, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None, resolver=None,
def __init__(self, type, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None,
*args, **kwargs):
if not resolver:
resolver = FilterConnectionResolver(
node=type,
on=on,
filterset_class=filterset_class,
fields=fields,
order_by=order_by,
extra_filter_meta=extra_filter_meta,
)
filtering_args = get_filtering_args_from_filterset(resolver.get_filterset_class(), type)
self.order_by = order_by or type._meta.filter_order_by
self.fields = fields or type._meta.filter_fields
meta = dict(model=type._meta.model,
fields=self.fields,
order_by=self.order_by)
if extra_filter_meta:
meta.update(extra_filter_meta)
self.filterset_class = get_filterset_class(filterset_class, **meta)
self.filtering_args = get_filtering_args_from_filterset(self.filterset_class, type)
kwargs.setdefault('args', {})
kwargs['args'].update(**filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, resolver, *args, **kwargs)
kwargs['args'].update(**self.filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
def get_queryset(self, qs, args, info):
filterset_class = self.filterset_class
filter_kwargs = self.get_filter_kwargs(args)
order = self.get_order(args)
if order:
qs = qs.order_by(order)
return filterset_class(data=filter_kwargs, queryset=qs)
def get_filter_kwargs(self, args):
return {k: v for k, v in args.items() if k in self.filtering_args}
def get_order(self, args):
return args.get('order_by', None)

View File

@ -2,12 +2,12 @@ import six
from django.conf import settings
from django.db import models
from django.utils.text import capfirst
from django_filters import Filter, MultipleChoiceFilter
from django_filters.filterset import FilterSet, FilterSetMetaclass
from graphql_relay.node.node import from_global_id
from graphene.contrib.django.forms import (GlobalIDFormField,
GlobalIDMultipleChoiceField)
from graphql_relay.node.node import from_global_id
class GlobalIDFilter(Filter):

View File

@ -1,64 +0,0 @@
from django.core.exceptions import ImproperlyConfigured
from graphene.contrib.django.filter.filterset import (custom_filterset_factory,
setup_filterset)
from graphene.contrib.django.resolvers import BaseQuerySetConnectionResolver
class FilterConnectionResolver(BaseQuerySetConnectionResolver):
# Querying using django-filter
def __init__(self, node, on=None, filterset_class=None,
fields=None, order_by=None, extra_filter_meta=None):
self.filterset_class = filterset_class
self.fields = fields or node._meta.filter_fields
self.order_by = order_by or node._meta.filter_order_by
self.extra_filter_meta = extra_filter_meta or {}
self._filterset_class = None
super(FilterConnectionResolver, self).__init__(node, on)
def make_query(self):
filterset_class = self.get_filterset_class()
filterset = self.get_filterset(filterset_class)
return filterset.qs
def get_filterset_class(self):
"""Get the class to be used as the FilterSet"""
if self._filterset_class:
return self._filterset_class
if self.filterset_class:
# If were given a FilterSet class, then set it up and
# return it
self._filterset_class = setup_filterset(self.filterset_class)
elif self.model:
# If no filter class was specified then create one given the
# other information provided
meta = dict(
model=self.model,
fields=self.fields,
order_by=self.order_by,
)
meta.update(self.extra_filter_meta)
self._filterset_class = custom_filterset_factory(**meta)
else:
msg = "Neither 'filterset_class' or 'model' available in '%s'. " \
"Either pass in 'filterset_class' or 'model' when " \
"initialising, or extend this class and override " \
"get_filterset() or get_filterset_class()"
raise ImproperlyConfigured(msg % self.__class__.__name__)
return self._filterset_class
def get_filterset(self, filterset_class):
"""Get an instance of the FilterSet"""
kwargs = self.get_filterset_kwargs(filterset_class)
return filterset_class(**kwargs)
def get_filterset_kwargs(self, filterset_class):
"""Get the kwargs to use when initialising the FilterSet class"""
kwargs = {
'data': self.args or None,
'queryset': self.get_manager()
}
return kwargs

View File

@ -1,4 +1,5 @@
import django_filters
from graphene.contrib.django.tests.models import Article, Pet, Reporter

View File

@ -1,7 +1,6 @@
from datetime import datetime
import pytest
from graphql.core.execution.base import ResolveInfo, ExecutionContext
from graphene import ObjectType, Schema
from graphene.contrib.django import DjangoNode
@ -10,7 +9,6 @@ from graphene.contrib.django.forms import (GlobalIDFormField,
from graphene.contrib.django.tests.models import Article, Pet, Reporter
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
from graphene.relay import NodeField
from graphene.utils import ProxySnakeDict
pytestmark = []
if DJANGO_FILTER_INSTALLED:
@ -187,8 +185,8 @@ def test_filter_filterset_related_results():
r1 = Reporter.objects.create(first_name='r1', last_name='r1', email='r1@test.com')
r2 = Reporter.objects.create(first_name='r2', last_name='r2', email='r2@test.com')
a1 = Article.objects.create(headline='a1', pub_date=datetime.now(), reporter=r1)
a2 = Article.objects.create(headline='a2', pub_date=datetime.now(), reporter=r2)
Article.objects.create(headline='a1', pub_date=datetime.now(), reporter=r1)
Article.objects.create(headline='a2', pub_date=datetime.now(), reporter=r2)
query = '''
query {
@ -217,7 +215,7 @@ def test_filter_filterset_related_results():
def test_global_id_field_implicit():
field = DjangoFilterConnectionField(ArticleNode, fields=['id'])
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
id_filter = filterset_class.base_filters['id']
assert isinstance(id_filter, GlobalIDFilter)
assert id_filter.field_class == GlobalIDFormField
@ -231,7 +229,7 @@ def test_global_id_field_explicit():
fields = ['id']
field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleIdFilter)
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
id_filter = filterset_class.base_filters['id']
assert isinstance(id_filter, GlobalIDFilter)
assert id_filter.field_class == GlobalIDFormField
@ -239,7 +237,7 @@ def test_global_id_field_explicit():
def test_global_id_field_relation():
field = DjangoFilterConnectionField(ArticleNode, fields=['reporter'])
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
id_filter = filterset_class.base_filters['reporter']
assert isinstance(id_filter, GlobalIDFilter)
assert id_filter.field_class == GlobalIDFormField
@ -247,7 +245,7 @@ def test_global_id_field_relation():
def test_global_id_multiple_field_implicit():
field = DjangoFilterConnectionField(ReporterNode, fields=['pets'])
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
multiple_filter = filterset_class.base_filters['pets']
assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
assert multiple_filter.field_class == GlobalIDMultipleChoiceField
@ -261,7 +259,7 @@ def test_global_id_multiple_field_explicit():
fields = ['pets']
field = DjangoFilterConnectionField(ReporterNode, filterset_class=ReporterPetsFilter)
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
multiple_filter = filterset_class.base_filters['pets']
assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
assert multiple_filter.field_class == GlobalIDMultipleChoiceField
@ -269,7 +267,7 @@ def test_global_id_multiple_field_explicit():
def test_global_id_multiple_field_implicit_reverse():
field = DjangoFilterConnectionField(ReporterNode, fields=['articles'])
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
multiple_filter = filterset_class.base_filters['articles']
assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
assert multiple_filter.field_class == GlobalIDMultipleChoiceField
@ -283,7 +281,7 @@ def test_global_id_multiple_field_explicit_reverse():
fields = ['articles']
field = DjangoFilterConnectionField(ReporterNode, filterset_class=ReporterPetsFilter)
filterset_class = field.resolver_fn.get_filterset_class()
filterset_class = field.filterset_class
multiple_filter = filterset_class.base_filters['articles']
assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
assert multiple_filter.field_class == GlobalIDMultipleChoiceField

View File

@ -1,82 +0,0 @@
import pytest
from django.core.exceptions import ImproperlyConfigured
from graphene.contrib.django.tests.models import Article, Reporter
from graphene.contrib.django.tests.test_resolvers import (ArticleNode,
ReporterNode)
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
if DJANGO_FILTER_INSTALLED:
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
from graphene.contrib.django.filter.tests.filters import ArticleFilter, ReporterFilter
else:
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
def test_filter_get_filterset_class_explicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolver(inst=reporter, args={}, info=None)
assert issubclass(resolver.get_filterset_class(), ReporterFilter), \
'ReporterFilter not returned'
def test_filter_get_filterset_class_implicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver(inst=reporter, args={}, info=None)
assert resolver.get_filterset_class().__name__ == 'ReporterFilterSet'
def test_filter_get_filterset_class_error():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver.model = None
with pytest.raises(ImproperlyConfigured) as excinfo:
resolver(inst=reporter, args={}, info=None)
assert "Neither 'filterset_class' or 'model' available" in str(excinfo.value)
def test_filter_filter():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
'first_name': 'Elmo'
}, info=None)
assert '"first_name" = Elmo' in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)
def test_filter_filter_contains():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
'headline__icontains': 'Elmo'
}, info=None)
assert '"headline" LIKE %Elmo%' in str(resolved.query)
def test_filter_order():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
'order_by': 'headline'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' in str(resolved.query)
assert '"headline" ASC' in str(resolved.query)
def test_filter_order_not_available():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
'order_by': 'last_name'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)

View File

@ -0,0 +1,31 @@
import six
from ....core.types import Argument, String
from .filterset import custom_filterset_factory, setup_filterset
def get_filtering_args_from_filterset(filterset_class, type):
""" Inspect a FilterSet and produce the arguments to pass to
a Graphene Field. These arguments will be available to
filter against in the GraphQL
"""
from graphene.contrib.django.form_converter import convert_form_field
args = {}
for name, filter_field in six.iteritems(filterset_class.base_filters):
field_type = Argument(convert_form_field(filter_field.field))
args[name] = field_type
# Also add the 'order_by' field
if filterset_class._meta.order_by:
args[filterset_class.order_by_field] = Argument(String())
return args
def get_filterset_class(filterset_class, **meta):
"""Get the class to be used as the FilterSet"""
if filterset_class:
# If were given a FilterSet class, then set it up and
# return it
return setup_filterset(filterset_class)
return custom_filterset_factory(**meta)

View File

@ -3,7 +3,6 @@ import binascii
from django.core.exceptions import ValidationError
from django.forms import CharField, Field, IntegerField, MultipleChoiceField
from django.utils.translation import ugettext_lazy as _
from graphql_relay import from_global_id

View File

@ -1,8 +1,8 @@
from django.core.management.base import BaseCommand, CommandError
import importlib
import json
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
help = 'Dump Graphene schema JSON to file'

View File

@ -1,43 +0,0 @@
class BaseQuerySetConnectionResolver(object):
def __init__(self, node, on=None):
self.node = node
self.model = node._meta.model
# The name of the field on the model which contains the
# manager upon which to perform the query. Optional.
# If omitted the model's default manager will be used.
self.on = on
def __call__(self, inst, args, info):
self.inst = inst
self.args = args
self.info = info
return self.make_query()
def get_manager(self):
if self.on:
return getattr(self.inst, self.on)
else:
return self.model._default_manager
def make_query(self):
raise NotImplemented()
class SimpleQuerySetConnectionResolver(BaseQuerySetConnectionResolver):
# Simple querying without using django-filter (ported from previous gist)
def make_query(self):
filter_kwargs = self.get_filter_kwargs()
query = self.get_manager().filter(**filter_kwargs)
order = self.get_order()
if order:
query = query.order_by(order)
return query
def get_filter_kwargs(self):
ignore = ['first', 'last', 'before', 'after', 'order_by']
return {k: v for k, v in self.args.items() if k not in ignore}
def get_order(self):
return self.args.get('order_by', None)

View File

@ -1,60 +0,0 @@
from django.db.models import Manager
from django.db.models.query import QuerySet
from graphene.contrib.django import DjangoNode
from graphene.contrib.django.resolvers import SimpleQuerySetConnectionResolver
from graphene.contrib.django.tests.models import Article, Reporter
class ReporterNode(DjangoNode):
class Meta:
model = Reporter
class ArticleNode(DjangoNode):
class Meta:
model = Article
def test_simple_resolve():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = SimpleQuerySetConnectionResolver(ReporterNode, on='articles')
resolved = resolver(inst=reporter, args={}, info=None)
assert isinstance(resolved, QuerySet), 'Did not resolve to a queryset'
def test_simple_get_manager_related():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = SimpleQuerySetConnectionResolver(ReporterNode, on='articles')
resolver(inst=reporter, args={}, info=None)
assert resolver.get_manager().instance == reporter, 'Resolver did not return a RelatedManager'
def test_simple_get_manager_all():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = SimpleQuerySetConnectionResolver(ReporterNode)
resolver(inst=reporter, args={}, info=None)
assert isinstance(resolver.get_manager(), Manager), 'Resolver did not return a Manager'
def test_simple_filter():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = SimpleQuerySetConnectionResolver(ReporterNode)
resolved = resolver(inst=reporter, args={
'first_name': 'Elmo'
}, info=None)
assert '"first_name" = Elmo' in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)
def test_simple_order():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = SimpleQuerySetConnectionResolver(ReporterNode)
resolved = resolver(inst=reporter, args={
'order_by': 'last_name'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' in str(resolved.query)
assert '"last_name" ASC' in str(resolved.query)

View File

@ -1,7 +1,7 @@
from py.test import raises
from tests.utils import assert_equal_lists
from graphene.contrib.django import DjangoObjectType
from tests.utils import assert_equal_lists
from .models import Reporter

View File

@ -1,12 +1,12 @@
from graphql.core.type import GraphQLObjectType
from mock import patch
from tests.utils import assert_equal_lists
from graphene import Schema
from graphene.contrib.django.types import DjangoNode, DjangoObjectType
from graphene.core.fields import Field
from graphene.core.types.scalars import Int
from graphene.relay.fields import GlobalIDField
from tests.utils import assert_equal_lists
from .models import Article, Reporter

View File

@ -7,7 +7,7 @@ from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
from ...relay.types import Connection, Node, NodeMeta
from .converter import convert_django_field
from .options import DjangoOptions
from .utils import get_reverse_fields, maybe_queryset
from .utils import get_reverse_fields
class DjangoObjectTypeMeta(ObjectTypeMeta):
@ -82,11 +82,7 @@ class DjangoObjectType(six.with_metaclass(
class DjangoConnection(Connection):
@classmethod
def from_list(cls, iterable, *args, **kwargs):
iterable = maybe_queryset(iterable)
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
pass
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
@ -112,5 +108,3 @@ class DjangoNode(six.with_metaclass(
return cls(instance)
except cls._meta.model.DoesNotExist:
return None
connection_type = DjangoConnection

View File

@ -1,9 +1,7 @@
import six
from django.db import models
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from graphene import Argument, String
from graphene.utils import LazyList
from .compat import RelatedObject
@ -56,26 +54,6 @@ def maybe_queryset(value):
return value
def get_filtering_args_from_filterset(filterset_class, type):
""" Inspect a FilterSet and produce the arguments to pass to
a Graphene Field. These arguments will be available to
filter against in the GraphQL
"""
from graphene.contrib.django.form_converter import convert_form_field
args = {}
for name, filter_field in six.iteritems(filterset_class.base_filters):
field_type = Argument(convert_form_field(filter_field.field))
# Is this correct? I don't quite grok the 'parent' system yet
field_type.mount(type)
args[name] = field_type
# Also add the 'order_by' field
if filterset_class._meta.order_by:
args[filterset_class.order_by_field] = Argument(String())
return args
def get_related_model(field):
if hasattr(field, 'rel'):
# Django 1.6, 1.7

View File

@ -1,10 +1,10 @@
from graphql.core import graphql
from py.test import raises
from tests.utils import assert_equal_lists
from graphene import Interface, List, ObjectType, Schema, String
from graphene.core.fields import Field
from graphene.core.types.base import LazyType
from tests.utils import assert_equal_lists
schema = Schema(name='My own schema')

View File

@ -51,6 +51,16 @@ class Field(NamedType, OrderedType):
def resolver(self):
return self.resolver_fn or self.get_resolver_fn()
@property
def default(self):
if callable(self._default):
return self._default()
return self._default
@default.setter
def default(self, value):
self._default = value
def get_resolver_fn(self):
resolve_fn_name = 'resolve_%s' % self.attname
if hasattr(self.object_type, resolve_fn_name):

View File

@ -23,15 +23,15 @@ class ConnectionField(Field):
self.connection_type = connection_type
self.edge_type = edge_type
def wrap_resolved(self, value, instance, args, info):
return value
def resolver(self, instance, args, info):
schema = info.schema.graphene_schema
connection_type = self.get_type(schema)
resolved = super(ConnectionField, self).resolver(instance, args, info)
if isinstance(resolved, connection_type):
return resolved
return self.from_list(connection_type, resolved, args, info)
def from_list(self, connection_type, resolved, args, info):
return connection_type.from_list(resolved, args, info)
def get_connection_type(self, node):

View File

@ -4,7 +4,6 @@ from collections import Iterable
from functools import wraps
import six
from graphql_relay.connection.arrayconnection import connection_from_list
from graphql_relay.node.node import to_global_id

View File

@ -1,5 +1,5 @@
[flake8]
exclude = setup.py,docs/*
exclude = setup.py,docs/*,examples/cookbook_django/*
max-line-length = 120
[coverage:run]

View File

@ -55,7 +55,7 @@ setup(
install_requires=[
'six>=1.10.0',
'graphql-core==0.4.9',
'graphql-core>=0.4.9',
'graphql-relay==0.3.3',
],
tests_require=[