mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
Fixed compatibility with Django 1.6, 1.7, 1.8 and 1.9
This commit is contained in:
parent
35d78320e8
commit
8eaa2cfc49
11
.travis.yml
11
.travis.yml
|
@ -73,13 +73,20 @@ after_success:
|
|||
fi
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=build DJANGO_VERSION=1.8
|
||||
- TEST_TYPE=build DJANGO_VERSION=1.9
|
||||
- TEST_TYPE=build
|
||||
global:
|
||||
secure: SQC0eCWCWw8bZxbLE8vQn+UjJOp3Z1m779s9SMK3lCLwJxro/VCLBZ7hj4xsrq1MtcFO2U2Kqf068symw4Hr/0amYI3HFTCFiwXAC3PAKXeURca03eNO2heku+FtnQcOjBanExTsIBQRLDXMOaUkf3MIztpLJ4LHqMfUupKmw9YSB0v40jDbSN8khBnndFykmOnVVHznFp8USoN5F0CiPpnfEvHnJkaX76lNf7Kc9XNShBTTtJsnsHMhuYQeInt0vg9HSjoIYC38Tv2hmMj1myNdzyrHF+LgRjI6ceGi50ApAnGepXC/DNRhXROfECKez+LON/ZSqBGdJhUILqC8A4WmWmIjNcwitVFp3JGBqO7LULS0BI96EtSLe8rD1rkkdTbjivajkbykM1Q0Tnmg1adzGwLxRUbTq9tJQlTTkHBCuXIkpKb1mAtb/TY7A6BqfnPi2xTc/++qEawUG7ePhscdTj0IBrUfZsUNUYZqD8E8XbSWKIuS3SHE+cZ+s/kdAsm4q+FFAlpZKOYGxIkwvgyfu4/Plfol4b7X6iAP9J3r1Kv0DgBVFst5CXEwzZs19/g0CgokQbCXf1N+xeNnUELl6/fImaR3RKP22EaABoil4z8vzl4EqxqVoH1nfhE+WlpryXsuSaF/1R+WklR7aQ1FwoCk8V8HxM2zrj4tI8k=
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- python: '2.7'
|
||||
env: DJANGO_VERSION=1.6
|
||||
- python: '2.7'
|
||||
env: DJANGO_VERSION=1.7
|
||||
- python: '2.7'
|
||||
env: DJANGO_VERSION=1.8
|
||||
- python: '2.7'
|
||||
env: DJANGO_VERSION=1.9
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build_website
|
||||
- python: '2.7'
|
||||
|
|
15
graphene/contrib/django/compat.py
Normal file
15
graphene/contrib/django/compat.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from django.db import models
|
||||
|
||||
try:
|
||||
UUIDField = models.UUIDField
|
||||
except AttributeError:
|
||||
# Improved compatibility for Django 1.6
|
||||
class UUIDField(object):
|
||||
pass
|
||||
|
||||
try:
|
||||
from django.db.models.related import RelatedObject
|
||||
except:
|
||||
# Improved compatibility for Django 1.6
|
||||
class RelatedObject(object):
|
||||
pass
|
|
@ -1,17 +1,11 @@
|
|||
from django.db import models
|
||||
|
||||
from .utils import import_single_dispatch
|
||||
from ...core.types.scalars import ID, Boolean, Float, Int, String
|
||||
from .compat import RelatedObject, UUIDField
|
||||
from .utils import get_related_model, import_single_dispatch
|
||||
|
||||
singledispatch = import_single_dispatch()
|
||||
|
||||
try:
|
||||
UUIDField = models.UUIDField
|
||||
except AttributeError:
|
||||
# Improved compatibility for Django 1.6
|
||||
class UUIDField(object):
|
||||
pass
|
||||
|
||||
|
||||
@singledispatch
|
||||
def convert_django_field(field):
|
||||
|
@ -65,7 +59,15 @@ def convert_field_to_float(field):
|
|||
@convert_django_field.register(models.ManyToOneRel)
|
||||
def convert_field_to_list_or_connection(field):
|
||||
from .fields import DjangoModelField, ConnectionOrListField
|
||||
model_field = DjangoModelField(field.related_model)
|
||||
model_field = DjangoModelField(get_related_model(field))
|
||||
return ConnectionOrListField(model_field)
|
||||
|
||||
|
||||
# For Django 1.6
|
||||
@convert_django_field.register(RelatedObject)
|
||||
def convert_relatedfield_to_djangomodel(field):
|
||||
from .fields import DjangoModelField, ConnectionOrListField
|
||||
model_field = DjangoModelField(field.model)
|
||||
return ConnectionOrListField(model_field)
|
||||
|
||||
|
||||
|
@ -73,4 +75,4 @@ def convert_field_to_list_or_connection(field):
|
|||
@convert_django_field.register(models.ForeignKey)
|
||||
def convert_field_to_djangomodel(field):
|
||||
from .fields import DjangoModelField
|
||||
return DjangoModelField(field.related_model, description=field.help_text)
|
||||
return DjangoModelField(get_related_model(field), description=field.help_text)
|
||||
|
|
|
@ -2,8 +2,8 @@ from contextlib import contextmanager
|
|||
|
||||
from django.db import connections
|
||||
|
||||
from ....core.types import Field
|
||||
from ....core.schema import GraphQLSchema
|
||||
from ....core.types import Field
|
||||
from ....plugins import Plugin
|
||||
from .sql.tracking import unwrap_cursor, wrap_cursor
|
||||
from .sql.types import DjangoDebugSQL
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .....core import Float, ObjectType, String, Boolean
|
||||
from .....core import Boolean, Float, ObjectType, String
|
||||
|
||||
|
||||
class DjangoDebugSQL(ObjectType):
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import warnings
|
||||
|
||||
from .utils import get_type_for_model, DJANGO_FILTER_INSTALLED
|
||||
from .filter.fields import DjangoFilterConnectionField
|
||||
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
|
||||
|
||||
|
||||
class DjangoConnectionField(ConnectionField):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from graphene.relay import ConnectionField
|
||||
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
|
||||
from graphene.contrib.django.utils import get_filtering_args_from_filterset
|
||||
from graphene.relay import ConnectionField
|
||||
|
||||
|
||||
class DjangoFilterConnectionField(ConnectionField):
|
||||
|
|
|
@ -2,11 +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 FilterSetMetaclass, FilterSet
|
||||
from graphql_relay.node.node import from_global_id
|
||||
|
||||
from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
|
||||
from django_filters import Filter, MultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet, FilterSetMetaclass
|
||||
from graphene.contrib.django.forms import (GlobalIDFormField,
|
||||
GlobalIDMultipleChoiceField)
|
||||
from graphql_relay.node.node import from_global_id
|
||||
|
||||
|
||||
class GlobalIDFilter(Filter):
|
||||
|
@ -45,6 +46,7 @@ GRAPHENE_FILTER_SET_OVERRIDES = {
|
|||
|
||||
|
||||
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
|
||||
|
@ -84,7 +86,6 @@ class GrapheneFilterSet(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneF
|
|||
DjangoFilterConnectionField will wrap FilterSets with this class as
|
||||
necessary
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def setup_filterset(filterset_class):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from graphene.contrib.django.filter.filterset import setup_filterset, custom_filterset_factory
|
||||
from graphene.contrib.django.filter.filterset import (custom_filterset_factory,
|
||||
setup_filterset)
|
||||
from graphene.contrib.django.resolvers import BaseQuerySetConnectionResolver
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
from django import forms
|
||||
from django.forms.fields import BaseTemporalField
|
||||
from graphene import String, Int, Boolean, Float, ID
|
||||
from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
|
||||
|
||||
from graphene import ID, Boolean, Float, Int, String
|
||||
from graphene.contrib.django.forms import (GlobalIDFormField,
|
||||
GlobalIDMultipleChoiceField)
|
||||
from graphene.contrib.django.utils import import_single_dispatch
|
||||
from graphene.core.types.definitions import List
|
||||
|
||||
singledispatch = import_single_dispatch()
|
||||
|
||||
try:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import binascii
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms import Field, IntegerField, CharField, MultipleChoiceField
|
||||
from django.forms import CharField, Field, IntegerField, MultipleChoiceField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from graphql_relay import from_global_id
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from .utils import DJANGO_FILTER_INSTALLED
|
||||
from ...core.classtypes.objecttype import ObjectTypeOptions
|
||||
from ...relay.types import Node
|
||||
from ...relay.utils import is_node
|
||||
from .utils import DJANGO_FILTER_INSTALLED
|
||||
|
||||
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import django_filters
|
||||
|
||||
from graphene.contrib.django.tests.models import Reporter
|
||||
from graphene.contrib.django.tests.models import Article, Pet
|
||||
from graphene.contrib.django.tests.models import Article, Pet, Reporter
|
||||
|
||||
|
||||
class ArticleFilter(django_filters.FilterSet):
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import pytest
|
||||
|
||||
from graphene import ObjectType, Schema
|
||||
from graphene.contrib.django import DjangoNode
|
||||
from graphene.contrib.django.forms import (GlobalIDFormField,
|
||||
GlobalIDMultipleChoiceField)
|
||||
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.contrib.django import DjangoNode
|
||||
from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
|
||||
from graphene.contrib.django.tests.models import Article, Pet, Reporter
|
||||
|
||||
pytestmark = []
|
||||
if DJANGO_FILTER_INSTALLED:
|
||||
import django_filters
|
||||
|
@ -22,21 +21,25 @@ pytestmark.append(pytest.mark.django_db)
|
|||
|
||||
|
||||
class ArticleNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
|
||||
|
||||
class ReporterNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
|
||||
|
||||
class PetNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Pet
|
||||
|
||||
schema = Schema()
|
||||
|
||||
|
||||
def assert_arguments(field, *arguments):
|
||||
ignore = ('after', 'before', 'first', 'last', 'orderBy')
|
||||
actual = [
|
||||
|
@ -48,7 +51,7 @@ def assert_arguments(field, *arguments):
|
|||
'Expected arguments ({}) did not match actual ({})'.format(
|
||||
arguments,
|
||||
actual
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def assert_orderable(field):
|
||||
|
@ -118,6 +121,7 @@ def test_filter_shortcut_filterset_extra_meta():
|
|||
|
||||
def test_filter_filterset_information_on_meta():
|
||||
class ReporterFilterNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
filter_fields = ['first_name', 'articles']
|
||||
|
@ -130,12 +134,14 @@ def test_filter_filterset_information_on_meta():
|
|||
|
||||
def test_filter_filterset_information_on_meta_related():
|
||||
class ReporterFilterNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
filter_fields = ['first_name', 'articles']
|
||||
filter_order_by = True
|
||||
|
||||
class ArticleFilterNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
filter_fields = ['headline', 'reporter']
|
||||
|
@ -164,6 +170,7 @@ def test_global_id_field_implicit():
|
|||
|
||||
def test_global_id_field_explicit():
|
||||
class ArticleIdFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
fields = ['id']
|
||||
|
@ -193,6 +200,7 @@ def test_global_id_multiple_field_implicit():
|
|||
|
||||
def test_global_id_multiple_field_explicit():
|
||||
class ReporterPetsFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
fields = ['pets']
|
||||
|
@ -214,6 +222,7 @@ def test_global_id_multiple_field_implicit_reverse():
|
|||
|
||||
def test_global_id_multiple_field_explicit_reverse():
|
||||
class ReporterPetsFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
fields = ['articles']
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
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:
|
||||
|
@ -9,9 +12,6 @@ if DJANGO_FILTER_INSTALLED:
|
|||
else:
|
||||
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
|
||||
|
||||
from graphene.contrib.django.tests.models import Reporter, Article
|
||||
from graphene.contrib.django.tests.test_resolvers import ReporterNode, ArticleNode
|
||||
|
||||
|
||||
def test_filter_get_filterset_class_explicit():
|
||||
reporter = Reporter(id=1, first_name='Cookie Monster')
|
||||
|
|
|
@ -9,8 +9,8 @@ from graphene.contrib.django.fields import (ConnectionOrListField,
|
|||
from .models import Article, Reporter
|
||||
|
||||
|
||||
def assert_conversion(django_field, graphene_field, *args):
|
||||
field = django_field(*args, help_text='Custom Help Text')
|
||||
def assert_conversion(django_field, graphene_field, *args, **kwargs):
|
||||
field = django_field(help_text='Custom Help Text', *args, **kwargs)
|
||||
graphene_type = convert_django_field(field)
|
||||
assert isinstance(graphene_type, graphene_field)
|
||||
field = graphene_type.as_field()
|
||||
|
@ -49,7 +49,7 @@ def test_should_url_convert_string():
|
|||
|
||||
|
||||
def test_should_auto_convert_id():
|
||||
assert_conversion(models.AutoField, graphene.ID)
|
||||
assert_conversion(models.AutoField, graphene.ID, primary_key=True)
|
||||
|
||||
|
||||
def test_should_positive_integer_convert_int():
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from django import forms
|
||||
from graphene.core.types import List, ID
|
||||
from py.test import raises
|
||||
|
||||
import graphene
|
||||
from graphene.contrib.django.form_converter import convert_form_field
|
||||
|
||||
from graphene.core.types import ID, List
|
||||
|
||||
from .models import Reporter
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from graphene.contrib.django import DjangoNode, DjangoObjectType
|
|||
|
||||
from .models import Article, Reporter
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
|
|
|
@ -3,15 +3,17 @@ 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 Reporter, Article
|
||||
from graphene.contrib.django.tests.models import Article, Reporter
|
||||
|
||||
|
||||
class ReporterNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Reporter
|
||||
|
||||
|
||||
class ArticleNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
|
||||
|
@ -34,7 +36,7 @@ def test_simple_get_manager_all():
|
|||
reporter = Reporter(id=1, first_name='Cookie Monster')
|
||||
resolver = SimpleQuerySetConnectionResolver(ReporterNode)
|
||||
resolver(inst=reporter, args={}, info=None)
|
||||
assert type(resolver.get_manager()) == Manager, 'Resolver did not return a Manager'
|
||||
assert isinstance(resolver.get_manager(), Manager), 'Resolver did not return a Manager'
|
||||
|
||||
|
||||
def test_simple_filter():
|
||||
|
|
|
@ -5,7 +5,6 @@ from django.db import models
|
|||
|
||||
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
|
||||
from ...relay.types import Connection, Node, NodeMeta
|
||||
from .utils import DJANGO_FILTER_INSTALLED
|
||||
from .converter import convert_django_field
|
||||
from .options import DjangoOptions
|
||||
from .utils import get_reverse_fields, maybe_queryset
|
||||
|
|
|
@ -3,9 +3,10 @@ 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 graphene import Argument, String
|
||||
from .compat import RelatedObject
|
||||
|
||||
try:
|
||||
import django_filters # noqa
|
||||
|
@ -29,7 +30,12 @@ def get_reverse_fields(model):
|
|||
# Django =>1.9 uses 'rel', django <1.9 uses 'related'
|
||||
related = getattr(attr, 'rel', None) or \
|
||||
getattr(attr, 'related', None)
|
||||
if isinstance(related, models.ManyToOneRel):
|
||||
if isinstance(related, RelatedObject):
|
||||
# Hack for making it compatible with Django 1.6
|
||||
new_related = RelatedObject(related.parent_model, related.model, related.field)
|
||||
new_related.name = name
|
||||
yield new_related
|
||||
elif isinstance(related, models.ManyToOneRel):
|
||||
yield related
|
||||
|
||||
|
||||
|
@ -70,6 +76,13 @@ def get_filtering_args_from_filterset(filterset_class, type):
|
|||
return args
|
||||
|
||||
|
||||
def get_related_model(field):
|
||||
if hasattr(field, 'rel'):
|
||||
# Django 1.6, 1.7
|
||||
return field.rel.to
|
||||
return field.related_model
|
||||
|
||||
|
||||
def import_single_dispatch():
|
||||
try:
|
||||
from functools import singledispatch
|
||||
|
|
|
@ -9,7 +9,8 @@ from ..classtypes.inputobjecttype import InputObjectType
|
|||
from ..classtypes.mutation import Mutation
|
||||
from ..exceptions import SkipField
|
||||
from .argument import Argument, ArgumentsGroup, snake_case_args
|
||||
from .base import GroupNamedType, LazyType, MountType, NamedType, ArgumentType, OrderedType
|
||||
from .base import (ArgumentType, GroupNamedType, LazyType, MountType,
|
||||
NamedType, OrderedType)
|
||||
from .definitions import NonNull
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user