mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-11 08:42:32 +03:00
Merge remote-tracking branch 'upstream/master' into graphene-3
This commit is contained in:
commit
650fb7d4c7
|
@ -126,7 +126,7 @@ create your own ``FilterSet``. You can pass it directly as follows:
|
||||||
all_animals = DjangoFilterConnectionField(AnimalNode,
|
all_animals = DjangoFilterConnectionField(AnimalNode,
|
||||||
filterset_class=AnimalFilter)
|
filterset_class=AnimalFilter)
|
||||||
|
|
||||||
You can also specify the ``FilterSet`` class using the ``filerset_class``
|
You can also specify the ``FilterSet`` class using the ``filterset_class``
|
||||||
parameter when defining your ``DjangoObjectType``, however, this can't be used
|
parameter when defining your ``DjangoObjectType``, however, this can't be used
|
||||||
in unison with the ``filter_fields`` parameter:
|
in unison with the ``filter_fields`` parameter:
|
||||||
|
|
||||||
|
@ -217,4 +217,4 @@ with this set up, you can now order the users under group:
|
||||||
xxx
|
xxx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from .types import DjangoObjectType
|
from .types import DjangoObjectType
|
||||||
from .fields import DjangoConnectionField
|
from .fields import DjangoConnectionField
|
||||||
|
|
||||||
__version__ = "2.5.0"
|
__version__ = "2.6.0"
|
||||||
|
|
||||||
__all__ = ["__version__", "DjangoObjectType", "DjangoConnectionField"]
|
__all__ = ["__version__", "DjangoObjectType", "DjangoConnectionField"]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from collections import OrderedDict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise
|
from django.utils.functional import Promise
|
||||||
|
@ -39,6 +40,8 @@ def convert_choice_name(name):
|
||||||
|
|
||||||
def get_choices(choices):
|
def get_choices(choices):
|
||||||
converted_names = []
|
converted_names = []
|
||||||
|
if isinstance(choices, OrderedDict):
|
||||||
|
choices = choices.items()
|
||||||
for value, help_text in choices:
|
for value, help_text in choices:
|
||||||
if isinstance(help_text, (tuple, list)):
|
if isinstance(help_text, (tuple, list)):
|
||||||
for choice in get_choices(help_text):
|
for choice in get_choices(help_text):
|
||||||
|
@ -52,6 +55,19 @@ def get_choices(choices):
|
||||||
yield name, value, description
|
yield name, value, description
|
||||||
|
|
||||||
|
|
||||||
|
def convert_choices_to_named_enum_with_descriptions(name, choices):
|
||||||
|
choices = list(get_choices(choices))
|
||||||
|
named_choices = [(c[0], c[1]) for c in choices]
|
||||||
|
named_choices_descriptions = {c[0]: c[2] for c in choices}
|
||||||
|
|
||||||
|
class EnumWithDescriptionsType(object):
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return named_choices_descriptions[self.name]
|
||||||
|
|
||||||
|
return Enum(name, list(named_choices), type=EnumWithDescriptionsType)
|
||||||
|
|
||||||
|
|
||||||
def convert_django_field_with_choices(
|
def convert_django_field_with_choices(
|
||||||
field, registry=None, convert_choices_to_enum=True
|
field, registry=None, convert_choices_to_enum=True
|
||||||
):
|
):
|
||||||
|
@ -63,16 +79,7 @@ def convert_django_field_with_choices(
|
||||||
if choices and convert_choices_to_enum:
|
if choices and convert_choices_to_enum:
|
||||||
meta = field.model._meta
|
meta = field.model._meta
|
||||||
name = to_camel_case("{}_{}".format(meta.object_name, field.name))
|
name = to_camel_case("{}_{}".format(meta.object_name, field.name))
|
||||||
choices = list(get_choices(choices))
|
enum = convert_choices_to_named_enum_with_descriptions(name, choices)
|
||||||
named_choices = [(c[0], c[1]) for c in choices]
|
|
||||||
named_choices_descriptions = {c[0]: c[2] for c in choices}
|
|
||||||
|
|
||||||
class EnumWithDescriptionsType(object):
|
|
||||||
@property
|
|
||||||
def description(self):
|
|
||||||
return named_choices_descriptions[self.name]
|
|
||||||
|
|
||||||
enum = Enum(name, list(named_choices), type=EnumWithDescriptionsType)
|
|
||||||
required = not (field.blank or field.null)
|
required = not (field.blank or field.null)
|
||||||
converted = enum(description=field.help_text, required=required)
|
converted = enum(description=field.help_text, required=required)
|
||||||
else:
|
else:
|
||||||
|
@ -235,12 +242,12 @@ def convert_postgres_array_to_list(field, registry=None):
|
||||||
|
|
||||||
@convert_django_field.register(HStoreField)
|
@convert_django_field.register(HStoreField)
|
||||||
@convert_django_field.register(JSONField)
|
@convert_django_field.register(JSONField)
|
||||||
def convert_posgres_field_to_string(field, registry=None):
|
def convert_postgres_field_to_string(field, registry=None):
|
||||||
return JSONString(description=field.help_text, required=not field.null)
|
return JSONString(description=field.help_text, required=not field.null)
|
||||||
|
|
||||||
|
|
||||||
@convert_django_field.register(RangeField)
|
@convert_django_field.register(RangeField)
|
||||||
def convert_posgres_range_to_string(field, registry=None):
|
def convert_postgres_range_to_string(field, registry=None):
|
||||||
inner_type = convert_django_field(field.base_field)
|
inner_type = convert_django_field(field.base_field)
|
||||||
if not isinstance(inner_type, (List, NonNull)):
|
if not isinstance(inner_type, (List, NonNull)):
|
||||||
inner_type = type(inner_type)
|
inner_type = type(inner_type)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from graphene import NonNull
|
|
||||||
from graphene.relay.connection import page_info_adapter, connection_adapter
|
from graphene.relay.connection import page_info_adapter, connection_adapter
|
||||||
|
|
||||||
|
from graphql_relay.connection.arrayconnection import connection_from_list_slice
|
||||||
from promise import Promise
|
from promise import Promise
|
||||||
|
|
||||||
from graphene.types import Field, List
|
from graphene import NonNull
|
||||||
from graphene.relay import ConnectionField
|
from graphene.relay import ConnectionField
|
||||||
from graphql_relay.connection.arrayconnection import connection_from_list_slice
|
from graphene.types import Field, List
|
||||||
|
|
||||||
from .settings import graphene_settings
|
from .settings import graphene_settings
|
||||||
from .utils import maybe_queryset
|
from .utils import maybe_queryset
|
||||||
|
@ -16,19 +16,43 @@ from .utils import maybe_queryset
|
||||||
|
|
||||||
class DjangoListField(Field):
|
class DjangoListField(Field):
|
||||||
def __init__(self, _type, *args, **kwargs):
|
def __init__(self, _type, *args, **kwargs):
|
||||||
|
from .types import DjangoObjectType
|
||||||
|
|
||||||
|
if isinstance(_type, NonNull):
|
||||||
|
_type = _type.of_type
|
||||||
|
|
||||||
|
assert issubclass(
|
||||||
|
_type, DjangoObjectType
|
||||||
|
), "DjangoListField only accepts DjangoObjectType types"
|
||||||
|
|
||||||
# Django would never return a Set of None vvvvvvv
|
# Django would never return a Set of None vvvvvvv
|
||||||
super(DjangoListField, self).__init__(List(NonNull(_type)), *args, **kwargs)
|
super(DjangoListField, self).__init__(List(NonNull(_type)), *args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self):
|
def model(self):
|
||||||
return self.type.of_type._meta.node._meta.model
|
_type = self.type.of_type
|
||||||
|
if isinstance(_type, NonNull):
|
||||||
|
_type = _type.of_type
|
||||||
|
return _type._meta.model
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_resolver(resolver, root, info, **args):
|
def list_resolver(django_object_type, resolver, root, info, **args):
|
||||||
return maybe_queryset(resolver(root, info, **args))
|
queryset = maybe_queryset(resolver(root, info, **args))
|
||||||
|
if queryset is None:
|
||||||
|
# Default to Django Model queryset
|
||||||
|
# N.B. This happens if DjangoListField is used in the top level Query object
|
||||||
|
model = django_object_type._meta.model
|
||||||
|
queryset = maybe_queryset(
|
||||||
|
django_object_type.get_queryset(model.objects, info)
|
||||||
|
)
|
||||||
|
return queryset
|
||||||
|
|
||||||
def get_resolver(self, parent_resolver):
|
def get_resolver(self, parent_resolver):
|
||||||
return partial(self.list_resolver, parent_resolver)
|
_type = self.type
|
||||||
|
if isinstance(_type, NonNull):
|
||||||
|
_type = _type.of_type
|
||||||
|
django_object_type = _type.of_type.of_type
|
||||||
|
return partial(self.list_resolver, django_object_type, parent_resolver)
|
||||||
|
|
||||||
|
|
||||||
class DjangoConnectionField(ConnectionField):
|
class DjangoConnectionField(ConnectionField):
|
||||||
|
|
|
@ -56,8 +56,6 @@ if DJANGO_FILTER_INSTALLED:
|
||||||
model = Pet
|
model = Pet
|
||||||
interfaces = (Node,)
|
interfaces = (Node,)
|
||||||
|
|
||||||
# schema = Schema()
|
|
||||||
|
|
||||||
|
|
||||||
def get_args(field):
|
def get_args(field):
|
||||||
return field.args
|
return field.args
|
||||||
|
@ -837,6 +835,75 @@ def test_integer_field_filter_type():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_other_filter_types():
|
||||||
|
class PetType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = Pet
|
||||||
|
interfaces = (Node,)
|
||||||
|
filter_fields = {"age": ["exact", "isnull", "lt"]}
|
||||||
|
fields = ("age",)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
pets = DjangoFilterConnectionField(PetType)
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
assert str(schema) == dedent(
|
||||||
|
"""\
|
||||||
|
\"""An object with an ID\"""
|
||||||
|
interface Node {
|
||||||
|
\"""The ID of the object\"""
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
\"""
|
||||||
|
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
|
||||||
|
\"""
|
||||||
|
type PageInfo {
|
||||||
|
\"""When paginating forwards, are there more items?\"""
|
||||||
|
hasNextPage: Boolean!
|
||||||
|
|
||||||
|
\"""When paginating backwards, are there more items?\"""
|
||||||
|
hasPreviousPage: Boolean!
|
||||||
|
|
||||||
|
\"""When paginating backwards, the cursor to continue.\"""
|
||||||
|
startCursor: String
|
||||||
|
|
||||||
|
\"""When paginating forwards, the cursor to continue.\"""
|
||||||
|
endCursor: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type PetType implements Node {
|
||||||
|
age: Int!
|
||||||
|
|
||||||
|
\"""The ID of the object\"""
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type PetTypeConnection {
|
||||||
|
\"""Pagination data for this connection.\"""
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
|
||||||
|
\"""Contains the nodes in this connection.\"""
|
||||||
|
edges: [PetTypeEdge]!
|
||||||
|
}
|
||||||
|
|
||||||
|
\"""A Relay edge containing a `PetType` and its cursor.\"""
|
||||||
|
type PetTypeEdge {
|
||||||
|
\"""The item at the end of the edge\"""
|
||||||
|
node: PetType
|
||||||
|
|
||||||
|
\"""A cursor for use in pagination\"""
|
||||||
|
cursor: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
pets(before: String = null, after: String = null, first: Int = null, last: Int = null, age: Int = null, age_Isnull: Boolean = null, age_Lt: Int = null): PetTypeConnection
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_filter_filterset_based_on_mixin():
|
def test_filter_filterset_based_on_mixin():
|
||||||
class ArticleFilterMixin(FilterSet):
|
class ArticleFilterMixin(FilterSet):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -18,9 +18,16 @@ def get_filtering_args_from_filterset(filterset_class, type):
|
||||||
if name in filterset_class.declared_filters:
|
if name in filterset_class.declared_filters:
|
||||||
form_field = filter_field.field
|
form_field = filter_field.field
|
||||||
else:
|
else:
|
||||||
field_name = name.split("__", 1)[0]
|
try:
|
||||||
|
field_name, filter_type = name.rsplit("__", 1)
|
||||||
|
except ValueError:
|
||||||
|
field_name = name
|
||||||
|
filter_type = None
|
||||||
|
|
||||||
if hasattr(model, field_name):
|
# If the filter type is `isnull` then use the filter provided by
|
||||||
|
# DjangoFilter (a BooleanFilter).
|
||||||
|
# Otherwise try and get a filter based on the actual model field
|
||||||
|
if filter_type != "isnull" and hasattr(model, field_name):
|
||||||
model_field = model._meta.get_field(field_name)
|
model_field = model._meta.get_field(field_name)
|
||||||
|
|
||||||
if hasattr(model_field, "formfield"):
|
if hasattr(model_field, "formfield"):
|
||||||
|
|
|
@ -5,6 +5,7 @@ from functools import singledispatch
|
||||||
import graphene
|
import graphene
|
||||||
|
|
||||||
from ..registry import get_global_registry
|
from ..registry import get_global_registry
|
||||||
|
from ..converter import convert_choices_to_named_enum_with_descriptions
|
||||||
from .types import DictType
|
from .types import DictType
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,7 +129,6 @@ def convert_serializer_field_to_time(field):
|
||||||
@get_graphene_type_from_serializer_field.register(serializers.ListField)
|
@get_graphene_type_from_serializer_field.register(serializers.ListField)
|
||||||
def convert_serializer_field_to_list(field, is_input=True):
|
def convert_serializer_field_to_list(field, is_input=True):
|
||||||
child_type = get_graphene_type_from_serializer_field(field.child)
|
child_type = get_graphene_type_from_serializer_field(field.child)
|
||||||
|
|
||||||
return (graphene.List, child_type)
|
return (graphene.List, child_type)
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,5 +143,13 @@ def convert_serializer_field_to_jsonstring(field):
|
||||||
|
|
||||||
|
|
||||||
@get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField)
|
@get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField)
|
||||||
def convert_serializer_field_to_list_of_string(field):
|
def convert_serializer_field_to_list_of_enum(field):
|
||||||
return (graphene.List, graphene.String)
|
child_type = convert_serializer_field_to_enum(field)
|
||||||
|
return (graphene.List, child_type)
|
||||||
|
|
||||||
|
|
||||||
|
@get_graphene_type_from_serializer_field.register(serializers.ChoiceField)
|
||||||
|
def convert_serializer_field_to_enum(field):
|
||||||
|
# enums require a name
|
||||||
|
name = field.field_name or field.source or "Choices"
|
||||||
|
return convert_choices_to_named_enum_with_descriptions(name, field.choices)
|
||||||
|
|
|
@ -60,8 +60,17 @@ def test_should_url_convert_string():
|
||||||
assert_conversion(serializers.URLField, graphene.String)
|
assert_conversion(serializers.URLField, graphene.String)
|
||||||
|
|
||||||
|
|
||||||
def test_should_choice_convert_string():
|
def test_should_choice_convert_enum():
|
||||||
assert_conversion(serializers.ChoiceField, graphene.String, choices=[])
|
field = assert_conversion(
|
||||||
|
serializers.ChoiceField,
|
||||||
|
graphene.Enum,
|
||||||
|
choices=[("h", "Hello"), ("w", "World")],
|
||||||
|
source="word",
|
||||||
|
)
|
||||||
|
assert field._meta.enum.__members__["H"].value == "h"
|
||||||
|
assert field._meta.enum.__members__["H"].description == "Hello"
|
||||||
|
assert field._meta.enum.__members__["W"].value == "w"
|
||||||
|
assert field._meta.enum.__members__["W"].description == "World"
|
||||||
|
|
||||||
|
|
||||||
def test_should_base_field_convert_string():
|
def test_should_base_field_convert_string():
|
||||||
|
@ -174,7 +183,7 @@ def test_should_file_convert_string():
|
||||||
|
|
||||||
|
|
||||||
def test_should_filepath_convert_string():
|
def test_should_filepath_convert_string():
|
||||||
assert_conversion(serializers.FilePathField, graphene.String, path="/")
|
assert_conversion(serializers.FilePathField, graphene.Enum, path="/")
|
||||||
|
|
||||||
|
|
||||||
def test_should_ip_convert_string():
|
def test_should_ip_convert_string():
|
||||||
|
@ -189,9 +198,9 @@ def test_should_json_convert_jsonstring():
|
||||||
assert_conversion(serializers.JSONField, graphene.types.json.JSONString)
|
assert_conversion(serializers.JSONField, graphene.types.json.JSONString)
|
||||||
|
|
||||||
|
|
||||||
def test_should_multiplechoicefield_convert_to_list_of_string():
|
def test_should_multiplechoicefield_convert_to_list_of_enum():
|
||||||
field = assert_conversion(
|
field = assert_conversion(
|
||||||
serializers.MultipleChoiceField, graphene.List, choices=[1, 2, 3]
|
serializers.MultipleChoiceField, graphene.List, choices=[1, 2, 3]
|
||||||
)
|
)
|
||||||
|
|
||||||
assert field.of_type == graphene.String
|
assert issubclass(field.of_type, graphene.Enum)
|
||||||
|
|
199
graphene_django/tests/test_fields.py
Normal file
199
graphene_django/tests/test_fields.py
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from graphene import List, NonNull, ObjectType, Schema, String
|
||||||
|
|
||||||
|
from ..fields import DjangoListField
|
||||||
|
from ..types import DjangoObjectType
|
||||||
|
from .models import Article as ArticleModel
|
||||||
|
from .models import Reporter as ReporterModel
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestDjangoListField:
|
||||||
|
def test_only_django_object_types(self):
|
||||||
|
class TestType(ObjectType):
|
||||||
|
foo = String()
|
||||||
|
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
list_field = DjangoListField(TestType)
|
||||||
|
|
||||||
|
def test_non_null_type(self):
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name",)
|
||||||
|
|
||||||
|
list_field = DjangoListField(NonNull(Reporter))
|
||||||
|
|
||||||
|
assert isinstance(list_field.type, List)
|
||||||
|
assert isinstance(list_field.type.of_type, NonNull)
|
||||||
|
assert list_field.type.of_type.of_type is Reporter
|
||||||
|
|
||||||
|
def test_get_django_model(self):
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name",)
|
||||||
|
|
||||||
|
list_field = DjangoListField(Reporter)
|
||||||
|
assert list_field.model is ReporterModel
|
||||||
|
|
||||||
|
def test_list_field_default_queryset(self):
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name",)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
reporters = DjangoListField(Reporter)
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
query {
|
||||||
|
reporters {
|
||||||
|
firstName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReporterModel.objects.create(first_name="Tara", last_name="West")
|
||||||
|
ReporterModel.objects.create(first_name="Debra", last_name="Payne")
|
||||||
|
|
||||||
|
result = schema.execute(query)
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
"reporters": [{"firstName": "Tara"}, {"firstName": "Debra"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_override_resolver(self):
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name",)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
reporters = DjangoListField(Reporter)
|
||||||
|
|
||||||
|
def resolve_reporters(_, info):
|
||||||
|
return ReporterModel.objects.filter(first_name="Tara")
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
query {
|
||||||
|
reporters {
|
||||||
|
firstName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReporterModel.objects.create(first_name="Tara", last_name="West")
|
||||||
|
ReporterModel.objects.create(first_name="Debra", last_name="Payne")
|
||||||
|
|
||||||
|
result = schema.execute(query)
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"reporters": [{"firstName": "Tara"}]}
|
||||||
|
|
||||||
|
def test_nested_list_field(self):
|
||||||
|
class Article(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ArticleModel
|
||||||
|
fields = ("headline",)
|
||||||
|
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name", "articles")
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
reporters = DjangoListField(Reporter)
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
query {
|
||||||
|
reporters {
|
||||||
|
firstName
|
||||||
|
articles {
|
||||||
|
headline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
r1 = ReporterModel.objects.create(first_name="Tara", last_name="West")
|
||||||
|
ReporterModel.objects.create(first_name="Debra", last_name="Payne")
|
||||||
|
|
||||||
|
ArticleModel.objects.create(
|
||||||
|
headline="Amazing news",
|
||||||
|
reporter=r1,
|
||||||
|
pub_date=datetime.date.today(),
|
||||||
|
pub_date_time=datetime.datetime.now(),
|
||||||
|
editor=r1,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute(query)
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
"reporters": [
|
||||||
|
{"firstName": "Tara", "articles": [{"headline": "Amazing news"}]},
|
||||||
|
{"firstName": "Debra", "articles": []},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_override_resolver_nested_list_field(self):
|
||||||
|
class Article(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ArticleModel
|
||||||
|
fields = ("headline",)
|
||||||
|
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
fields = ("first_name", "articles")
|
||||||
|
|
||||||
|
def resolve_reporters(reporter, info):
|
||||||
|
return reporter.articles.all()
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
reporters = DjangoListField(Reporter)
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
query {
|
||||||
|
reporters {
|
||||||
|
firstName
|
||||||
|
articles {
|
||||||
|
headline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
r1 = ReporterModel.objects.create(first_name="Tara", last_name="West")
|
||||||
|
ReporterModel.objects.create(first_name="Debra", last_name="Payne")
|
||||||
|
|
||||||
|
ArticleModel.objects.create(
|
||||||
|
headline="Amazing news",
|
||||||
|
reporter=r1,
|
||||||
|
pub_date=datetime.date.today(),
|
||||||
|
pub_date_time=datetime.datetime.now(),
|
||||||
|
editor=r1,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute(query)
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
"reporters": [
|
||||||
|
{"firstName": "Tara", "articles": [{"headline": "Amazing news"}]},
|
||||||
|
{"firstName": "Debra", "articles": []},
|
||||||
|
]
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ def instantiate_middleware(middlewares):
|
||||||
|
|
||||||
|
|
||||||
class GraphQLView(View):
|
class GraphQLView(View):
|
||||||
graphiql_version = "0.13.0"
|
graphiql_version = "0.14.0"
|
||||||
graphiql_template = "graphene/graphiql.html"
|
graphiql_template = "graphene/graphiql.html"
|
||||||
react_version = "16.8.6"
|
react_version = "16.8.6"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user