From b06e33ddd7407b4e1cf468117904a34309e51763 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 18 Feb 2017 14:02:31 -0800 Subject: [PATCH] Improved registry --- graphene_django/debug/tests/test_query.py | 5 ++ graphene_django/filter/tests/test_fields.py | 21 ++++++-- graphene_django/registry.py | 11 ++-- graphene_django/tests/test_converter.py | 5 +- graphene_django/tests/test_query.py | 5 ++ graphene_django/tests/test_registry.py | 56 +++++++++++++++++++++ graphene_django/tests/test_schema.py | 6 ++- graphene_django/tests/test_types.py | 3 +- graphene_django/types.py | 10 +++- 9 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 graphene_django/tests/test_registry.py diff --git a/graphene_django/debug/tests/test_query.py b/graphene_django/debug/tests/test_query.py index e0851e7..c265329 100644 --- a/graphene_django/debug/tests/test_query.py +++ b/graphene_django/debug/tests/test_query.py @@ -6,10 +6,15 @@ from graphene_django import DjangoConnectionField, DjangoObjectType from graphene_django.utils import DJANGO_FILTER_INSTALLED from ...tests.models import Reporter +from ...registry import reset_global_registry from ..middleware import DjangoDebugMiddleware from ..types import DjangoDebug +def setup_function(function): + reset_global_registry() + + class context(object): pass diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py index 63c9e37..fdf6219 100644 --- a/graphene_django/filter/tests/test_fields.py +++ b/graphene_django/filter/tests/test_fields.py @@ -9,6 +9,7 @@ from graphene_django.forms import (GlobalIDFormField, GlobalIDMultipleChoiceField) from graphene_django.tests.models import Article, Pet, Reporter from graphene_django.utils import DJANGO_FILTER_INSTALLED +from graphene_django.registry import Registry, reset_global_registry pytestmark = [] @@ -24,6 +25,8 @@ pytestmark.append(pytest.mark.django_db) if DJANGO_FILTER_INSTALLED: + reset_global_registry() + class ArticleNode(DjangoObjectType): class Meta: @@ -47,6 +50,10 @@ if DJANGO_FILTER_INSTALLED: # schema = Schema() +@pytest.fixture +def _registry(): + return Registry() + def get_args(field): return field.args @@ -134,26 +141,28 @@ def test_filter_shortcut_filterset_extra_meta(): assert 'headline' not in field.filterset_class.get_fields() -def test_filter_filterset_information_on_meta(): +def test_filter_filterset_information_on_meta(_registry): class ReporterFilterNode(DjangoObjectType): class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] + registry = _registry field = DjangoFilterConnectionField(ReporterFilterNode) assert_arguments(field, 'first_name', 'articles') assert_not_orderable(field) -def test_filter_filterset_information_on_meta_related(): +def test_filter_filterset_information_on_meta_related(_registry): class ReporterFilterNode(DjangoObjectType): class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] + registry = _registry class ArticleFilterNode(DjangoObjectType): @@ -161,6 +170,7 @@ def test_filter_filterset_information_on_meta_related(): model = Article interfaces = (Node, ) filter_fields = ['headline', 'reporter'] + registry = _registry class Query(ObjectType): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) @@ -174,13 +184,14 @@ def test_filter_filterset_information_on_meta_related(): assert_not_orderable(articles_field) -def test_filter_filterset_related_results(): +def test_filter_filterset_related_results(_registry): class ReporterFilterNode(DjangoObjectType): class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] + registry = _registry class ArticleFilterNode(DjangoObjectType): @@ -188,6 +199,7 @@ def test_filter_filterset_related_results(): interfaces = (Node, ) model = Article filter_fields = ['headline', 'reporter'] + registry = _registry class Query(ObjectType): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) @@ -315,7 +327,7 @@ def test_global_id_multiple_field_explicit_reverse(): assert multiple_filter.field_class == GlobalIDMultipleChoiceField -def test_filter_filterset_related_results(): +def test_filter_filterset_related_results(_registry): class ReporterFilterNode(DjangoObjectType): class Meta: @@ -324,6 +336,7 @@ def test_filter_filterset_related_results(): filter_fields = { 'first_name': ['icontains'] } + registry = _registry class Query(ObjectType): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) diff --git a/graphene_django/registry.py b/graphene_django/registry.py index 21fed12..33b05d5 100644 --- a/graphene_django/registry.py +++ b/graphene_django/registry.py @@ -6,15 +6,16 @@ class Registry(object): def register(self, cls): from .types import DjangoObjectType + model = cls._meta.model + assert self._registry.get(model, cls) == cls, ( + 'Django Model "{}.{}" already associated with {}. ' + 'You can use a different registry for {} or skip the global Registry with "{}.Meta.skip_global_registry = True".' + ).format(model._meta.app_label, model._meta.object_name, repr(self.get_type_for_model(cls._meta.model)), repr(cls), cls) assert issubclass( cls, DjangoObjectType), 'Only DjangoObjectTypes can be registered, received "{}"'.format( cls.__name__) assert cls._meta.registry == self, 'Registry for a Model have to match.' - # assert self.get_type_for_model(cls._meta.model) == cls, ( - # 'Multiple DjangoObjectTypes registered for "{}"'.format(cls._meta.model) - # ) - if not getattr(cls._meta, 'skip_registry', False): - self._registry[cls._meta.model] = cls + self._registry[cls._meta.model] = cls def get_type_for_model(self, model): return self._registry.get(model) diff --git a/graphene_django/tests/test_converter.py b/graphene_django/tests/test_converter.py index 997b03c..a01253b 100644 --- a/graphene_django/tests/test_converter.py +++ b/graphene_django/tests/test_converter.py @@ -11,12 +11,13 @@ from graphene.types.json import JSONString from ..compat import (ArrayField, HStoreField, JSONField, MissingType, RangeField, UUIDField, DurationField) from ..converter import convert_django_field, convert_django_field_with_choices -from ..registry import Registry +from ..registry import Registry, reset_global_registry from ..types import DjangoObjectType from .models import Article, Film, FilmDetails, Reporter -# from graphene.core.types.custom_scalars import DateTime, Time, JSONString +def setup_function(function): + reset_global_registry() def assert_conversion(django_field, graphene_field, *args, **kwargs): diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 750a421..8cdf4f5 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -12,11 +12,16 @@ from ..utils import DJANGO_FILTER_INSTALLED from ..compat import MissingType, RangeField from ..fields import DjangoConnectionField from ..types import DjangoObjectType +from ..registry import reset_global_registry from .models import Article, Reporter pytestmark = pytest.mark.django_db +def setup_function(function): + reset_global_registry() + + def test_should_query_only_fields(): with raises(Exception): class ReporterType(DjangoObjectType): diff --git a/graphene_django/tests/test_registry.py b/graphene_django/tests/test_registry.py new file mode 100644 index 0000000..a7327bf --- /dev/null +++ b/graphene_django/tests/test_registry.py @@ -0,0 +1,56 @@ +from pytest import raises + +from ..registry import Registry, get_global_registry, reset_global_registry +from ..types import DjangoObjectType +from .models import Reporter as ReporterModel + + +def setup_function(function): + reset_global_registry() + + +def test_registry_basic(): + global_registry = get_global_registry() + + class Reporter(DjangoObjectType): + '''Reporter description''' + class Meta: + model = ReporterModel + + assert Reporter._meta.registry == global_registry + assert global_registry.get_type_for_model(ReporterModel) == Reporter + + +def test_registry_multiple_types(): + class Reporter(DjangoObjectType): + '''Reporter description''' + class Meta: + model = ReporterModel + + with raises(Exception) as exc_info: + class Reporter2(DjangoObjectType): + '''Reporter2 description''' + class Meta: + model = ReporterModel + + assert str(exc_info.value) == ( + 'Django Model "tests.Reporter" already associated with . ' + 'You can use a different registry for ' + 'or skip the global Registry with "Reporter2.Meta.skip_global_registry = True".' + ) + + +def test_registry_multiple_types_dont_collision_if_skip_global_registry(): + class Reporter(DjangoObjectType): + '''Reporter description''' + class Meta: + model = ReporterModel + + class Reporter2(DjangoObjectType): + '''Reporter2 description''' + class Meta: + model = ReporterModel + skip_global_registry = True + + assert Reporter._meta.registry != Reporter2._meta.registry + assert Reporter2._meta.registry != get_global_registry() diff --git a/graphene_django/tests/test_schema.py b/graphene_django/tests/test_schema.py index 32db172..40698e3 100644 --- a/graphene_django/tests/test_schema.py +++ b/graphene_django/tests/test_schema.py @@ -1,10 +1,14 @@ from py.test import raises -from ..registry import Registry +from ..registry import Registry, reset_global_registry from ..types import DjangoObjectType from .models import Reporter +def setup_function(function): + reset_global_registry() + + def test_should_raise_if_no_model(): with raises(Exception) as excinfo: class Character1(DjangoObjectType): diff --git a/graphene_django/tests/test_types.py b/graphene_django/tests/test_types.py index c617fe4..f6e2543 100644 --- a/graphene_django/tests/test_types.py +++ b/graphene_django/tests/test_types.py @@ -3,11 +3,12 @@ from mock import patch from graphene import Interface, ObjectType, Schema from graphene.relay import Node -from ..registry import reset_global_registry +from ..registry import Registry, reset_global_registry from ..types import DjangoObjectType from .models import Article as ArticleModel from .models import Reporter as ReporterModel + reset_global_registry() diff --git a/graphene_django/types.py b/graphene_django/types.py index ff88779..8c52298 100644 --- a/graphene_django/types.py +++ b/graphene_django/types.py @@ -58,7 +58,7 @@ class DjangoObjectTypeMeta(ObjectTypeMeta): only_fields=(), exclude_fields=(), interfaces=(), - skip_registry=False, + skip_global_registry=False, registry=None ) if DJANGO_FILTER_INSTALLED: @@ -72,6 +72,14 @@ class DjangoObjectTypeMeta(ObjectTypeMeta): attrs.pop('Meta', None), **defaults ) + # If the DjangoObjectType wants to skip the registry + # we will automatically create one, so the model is isolated + # there. + if options.skip_global_registry: + assert not options.registry, ( + "The attribute skip_global_registry requires have an empty registry in {}.Meta" + ).format(name) + options.registry = Registry() if not options.registry: options.registry = get_global_registry() assert isinstance(options.registry, Registry), (