mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 01:47:57 +03:00 
			
		
		
		
	Extend DjangoListField to use model queryset if none defined (#732)
* Fix model property * Only allow DjangoObjectTypes to DjangoListField * Resolve model queryset by default * Add some more tests to check behaviour
This commit is contained in:
		
							parent
							
								
									4bbc0824a6
								
							
						
					
					
						commit
						fea9b5b194
					
				| 
						 | 
					@ -1,13 +1,12 @@
 | 
				
			||||||
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 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, PageInfo
 | 
					from graphene.relay import ConnectionField, PageInfo
 | 
				
			||||||
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
 | 
				
			||||||
| 
						 | 
					@ -15,19 +14,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):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										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": []},
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user