mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-10 19:57:15 +03:00
Merge branch 'master' of https://github.com/graphql-python/graphene-django into fix/enhanced-proxy-model-support
This commit is contained in:
commit
a7ee042e9d
|
@ -96,6 +96,29 @@ schema is simple.
|
|||
|
||||
result = schema.execute(query, context_value=request)
|
||||
|
||||
|
||||
Global Filtering
|
||||
----------------
|
||||
|
||||
If you are using ``DjangoObjectType`` you can define a custom `get_queryset`.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from graphene import relay
|
||||
from graphene_django.types import DjangoObjectType
|
||||
from .models import Post
|
||||
|
||||
class PostNode(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Post
|
||||
|
||||
@classmethod
|
||||
def get_queryset(cls, queryset, info):
|
||||
if info.context.user.is_anonymous:
|
||||
return queryset.filter(published=True)
|
||||
return queryset
|
||||
|
||||
|
||||
Filtering ID-based Node Access
|
||||
------------------------------
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ data to ``schema.json`` that is compatible with babel-relay-plugin.
|
|||
Usage
|
||||
-----
|
||||
|
||||
Include ``graphene_django`` to ``INSTALLED_APPS`` in you project
|
||||
Include ``graphene_django`` to ``INSTALLED_APPS`` in your project
|
||||
settings:
|
||||
|
||||
.. code:: python
|
||||
|
@ -29,6 +29,8 @@ It dumps your full introspection schema to ``schema.json`` inside your
|
|||
project root directory. Point ``babel-relay-plugin`` to this file and
|
||||
you're ready to use Relay with Graphene GraphQL implementation.
|
||||
|
||||
The schema file is sorted to create a reproducible canonical representation.
|
||||
|
||||
Advanced Usage
|
||||
--------------
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
|
|||
The filtering functionality is provided by
|
||||
`django-filter <https://django-filter.readthedocs.org>`__. See the
|
||||
`usage
|
||||
documentation <https://django-filter.readthedocs.org/en/latest/usage.html#the-filter>`__
|
||||
documentation <https://django-filter.readthedocs.org/en/latest/guide/usage.html#the-filter>`__
|
||||
for details on the format for ``filter_fields``. While optional, this
|
||||
tutorial makes use of this functionality so you will need to install
|
||||
``django-filter`` for this tutorial to work:
|
||||
|
|
|
@ -67,6 +67,10 @@ class DjangoConnectionField(ConnectionField):
|
|||
else:
|
||||
return self.model._default_manager
|
||||
|
||||
@classmethod
|
||||
def resolve_queryset(cls, connection, queryset, info, args):
|
||||
return connection._meta.node.get_queryset(queryset, info)
|
||||
|
||||
@classmethod
|
||||
def merge_querysets(cls, default_queryset, queryset):
|
||||
if default_queryset.query.distinct and not queryset.query.distinct:
|
||||
|
@ -135,7 +139,8 @@ class DjangoConnectionField(ConnectionField):
|
|||
args["last"] = min(last, max_limit)
|
||||
|
||||
iterable = resolver(root, info, **args)
|
||||
on_resolve = partial(cls.resolve_connection, connection, default_manager, args)
|
||||
queryset = cls.resolve_queryset(connection, default_manager, info, args)
|
||||
on_resolve = partial(cls.resolve_connection, connection, queryset, args)
|
||||
|
||||
if Promise.is_thenable(iterable):
|
||||
return Promise.resolve(iterable).then(on_resolve)
|
||||
|
|
|
@ -14,7 +14,7 @@ from graphene.types.utils import yank_fields_from_attrs
|
|||
from graphene_django.registry import get_global_registry
|
||||
|
||||
from .converter import convert_form_field
|
||||
from .types import ErrorType
|
||||
from ..types import ErrorType
|
||||
|
||||
|
||||
def fields_for_form(form, only_fields, exclude_fields):
|
||||
|
|
|
@ -39,7 +39,7 @@ class Command(CommandArguments):
|
|||
|
||||
def save_file(self, out, schema_dict, indent):
|
||||
with open(out, "w") as outfile:
|
||||
json.dump(schema_dict, outfile, indent=indent)
|
||||
json.dump(schema_dict, outfile, indent=indent, sort_keys=True)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
options_schema = options.get("schema")
|
||||
|
@ -65,7 +65,7 @@ class Command(CommandArguments):
|
|||
indent = options.get("indent")
|
||||
schema_dict = {"data": schema.introspect()}
|
||||
if out == '-':
|
||||
self.stdout.write(json.dumps(schema_dict, indent=indent))
|
||||
self.stdout.write(json.dumps(schema_dict, indent=indent, sort_keys=True))
|
||||
else:
|
||||
self.save_file(out, schema_dict, indent)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from graphene.relay.mutation import ClientIDMutation
|
|||
from graphene.types.objecttype import yank_fields_from_attrs
|
||||
|
||||
from .serializer_converter import convert_serializer_field
|
||||
from .types import ErrorType
|
||||
from ..types import ErrorType
|
||||
|
||||
|
||||
class SerializerMutationOptions(MutationOptions):
|
||||
|
|
|
@ -2,11 +2,6 @@ import graphene
|
|||
from graphene.types.unmountedtype import UnmountedType
|
||||
|
||||
|
||||
class ErrorType(graphene.ObjectType):
|
||||
field = graphene.String(required=True)
|
||||
messages = graphene.List(graphene.NonNull(graphene.String), required=True)
|
||||
|
||||
|
||||
class DictType(UnmountedType):
|
||||
key = graphene.String()
|
||||
value = graphene.String()
|
||||
|
|
|
@ -28,7 +28,7 @@ except ImportError:
|
|||
DEFAULTS = {
|
||||
"SCHEMA": None,
|
||||
"SCHEMA_OUTPUT": "schema.json",
|
||||
"SCHEMA_INDENT": None,
|
||||
"SCHEMA_INDENT": 2,
|
||||
"MIDDLEWARE": (),
|
||||
# Set to True if the connection fields must have
|
||||
# either the first or last argument
|
||||
|
|
0
graphene_django/tests/issues/__init__.py
Normal file
0
graphene_django/tests/issues/__init__.py
Normal file
44
graphene_django/tests/issues/test_520.py
Normal file
44
graphene_django/tests/issues/test_520.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# https://github.com/graphql-python/graphene-django/issues/520
|
||||
|
||||
import datetime
|
||||
|
||||
from django import forms
|
||||
|
||||
import graphene
|
||||
|
||||
from graphene import Field, ResolveInfo
|
||||
from graphene.types.inputobjecttype import InputObjectType
|
||||
from py.test import raises
|
||||
from py.test import mark
|
||||
from rest_framework import serializers
|
||||
|
||||
from ...types import DjangoObjectType
|
||||
from ...rest_framework.models import MyFakeModel
|
||||
from ...rest_framework.mutation import SerializerMutation
|
||||
from ...forms.mutation import DjangoFormMutation
|
||||
|
||||
|
||||
class MyModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = MyFakeModel
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class MyForm(forms.Form):
|
||||
text = forms.CharField()
|
||||
|
||||
|
||||
def test_can_use_form_and_serializer_mutations():
|
||||
class MyMutation(SerializerMutation):
|
||||
class Meta:
|
||||
serializer_class = MyModelSerializer
|
||||
|
||||
class MyFormMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = MyForm
|
||||
|
||||
class Mutation(graphene.ObjectType):
|
||||
my_mutation = MyMutation.Field()
|
||||
my_form_mutation = MyFormMutation.Field()
|
||||
|
||||
graphene.Schema(mutation=Mutation)
|
|
@ -1,5 +1,5 @@
|
|||
from django.core import management
|
||||
from mock import patch
|
||||
from mock import patch, mock_open
|
||||
from six import StringIO
|
||||
|
||||
|
||||
|
@ -8,3 +8,16 @@ def test_generate_file_on_call_graphql_schema(savefile_mock, settings):
|
|||
out = StringIO()
|
||||
management.call_command("graphql_schema", schema="", stdout=out)
|
||||
assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue()
|
||||
|
||||
|
||||
@patch('json.dump')
|
||||
def test_files_are_canonical(dump_mock):
|
||||
open_mock = mock_open()
|
||||
with patch('graphene_django.management.commands.graphql_schema.open', open_mock):
|
||||
management.call_command('graphql_schema', schema='')
|
||||
|
||||
open_mock.assert_called_once()
|
||||
|
||||
dump_mock.assert_called_once()
|
||||
assert dump_mock.call_args[1]["sort_keys"], "json.mock() should be used to sort the output"
|
||||
assert dump_mock.call_args[1]["indent"] > 0, "output should be pretty-printed by default"
|
||||
|
|
|
@ -83,7 +83,7 @@ def test_should_image_convert_string():
|
|||
assert_conversion(models.ImageField, graphene.String)
|
||||
|
||||
|
||||
def test_should_url_convert_string():
|
||||
def test_should_file_path_field_convert_string():
|
||||
assert_conversion(models.FilePathField, graphene.String)
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ def test_should_auto_convert_id():
|
|||
assert_conversion(models.AutoField, graphene.ID, primary_key=True)
|
||||
|
||||
|
||||
def test_should_auto_convert_id():
|
||||
def test_should_uuid_convert_id():
|
||||
assert_conversion(models.UUIDField, graphene.UUID)
|
||||
|
||||
|
||||
|
|
|
@ -965,3 +965,47 @@ def test_proxy_model_support():
|
|||
result = schema.execute(query)
|
||||
assert not result.errors
|
||||
assert result.data == expected
|
||||
|
||||
|
||||
def test_should_resolve_get_queryset_connectionfields():
|
||||
reporter_1 = Reporter.objects.create(
|
||||
first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
|
||||
)
|
||||
reporter_2 = CNNReporter.objects.create(
|
||||
first_name="Some",
|
||||
last_name="Guy",
|
||||
email="someguy@cnn.com",
|
||||
a_choice=1,
|
||||
reporter_type=2, # set this guy to be CNN
|
||||
)
|
||||
|
||||
class ReporterType(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Reporter
|
||||
interfaces = (Node,)
|
||||
|
||||
@classmethod
|
||||
def get_queryset(cls, queryset, info):
|
||||
return queryset.filter(reporter_type=2)
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
all_reporters = DjangoConnectionField(ReporterType)
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
||||
query = """
|
||||
query ReporterPromiseConnectionQuery {
|
||||
allReporters(first: 1) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
expected = {"allReporters": {"edges": [{"node": {"id": "UmVwb3J0ZXJUeXBlOjI="}}]}}
|
||||
|
||||
result = schema.execute(query)
|
||||
assert not result.errors
|
||||
assert result.data == expected
|
||||
|
|
|
@ -3,6 +3,7 @@ from collections import OrderedDict
|
|||
|
||||
from django.db.models import Model
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
import graphene
|
||||
from graphene import Field
|
||||
from graphene.relay import Connection, Node
|
||||
from graphene.types.objecttype import ObjectType, ObjectTypeOptions
|
||||
|
@ -137,9 +138,19 @@ class DjangoObjectType(ObjectType):
|
|||
|
||||
return model == cls._meta.model
|
||||
|
||||
@classmethod
|
||||
def get_queryset(cls, queryset, info):
|
||||
return queryset
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, info, id):
|
||||
queryset = cls.get_queryset(cls._meta.model.objects, info)
|
||||
try:
|
||||
return cls._meta.model.objects.get(pk=id)
|
||||
return queryset.get(pk=id)
|
||||
except cls._meta.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class ErrorType(ObjectType):
|
||||
field = graphene.String(required=True)
|
||||
messages = graphene.List(graphene.NonNull(graphene.String), required=True)
|
||||
|
|
Loading…
Reference in New Issue
Block a user