diff --git a/grapple/actions.py b/grapple/actions.py index 96957ff1..3e2e154f 100644 --- a/grapple/actions.py +++ b/grapple/actions.py @@ -9,6 +9,7 @@ from django.apps import apps from django.core.exceptions import FieldDoesNotExist from django.db import models +from django.utils.module_loading import import_string from graphene_django.types import DjangoObjectType from wagtail.blocks import StructValue, stream_block from wagtail.contrib.settings.models import BaseGenericSetting, BaseSiteSetting @@ -268,7 +269,12 @@ class StubMeta: model = stub_model # Gather any interfaces, and discard None values - interfaces = {interface, *getattr(cls, "graphql_interfaces", ())} + interface_classes = getattr(cls, "graphql_interfaces", ()) + interface_classes = tuple( + import_string(i) if isinstance(i, str) else i for i in interface_classes + ) + + interfaces = {interface, *interface_classes} interfaces.discard(None) type_meta = { diff --git a/tests/test_interfaces.py b/tests/test_interfaces.py index a1276010..76c1aac6 100644 --- a/tests/test_interfaces.py +++ b/tests/test_interfaces.py @@ -106,6 +106,13 @@ def test_schema_for_django_model_with_graphql_interfaces(self): [{"name": "AdditionalInterface"}], ) + def test_imports_graphql_interfaces_defined_by_str(self): + results = self.introspect_schema_by_type("SimpleModelWithAlternativeInterface") + self.assertListEqual( + sorted(results["data"]["__type"]["interfaces"], key=lambda x: x["name"]), + [{"name": "AdditionalInterface"}, {"name": "AlternativeInterface"}], + ) + @tag("needs-custom-settings") @skipUnless( diff --git a/tests/testapp/interfaces.py b/tests/testapp/interfaces.py index 41baec05..dc11c314 100644 --- a/tests/testapp/interfaces.py +++ b/tests/testapp/interfaces.py @@ -7,6 +7,10 @@ class AdditionalInterface(graphene.Interface): additional_text = graphene.String() +class AlternativeInterface(graphene.Interface): + alternative_text = graphene.String() + + class CustomPageInterface(PageInterface): custom_text = graphene.String() diff --git a/tests/testapp/migrations/0003_simplemodelwithalternativeinterface.py b/tests/testapp/migrations/0003_simplemodelwithalternativeinterface.py new file mode 100644 index 00000000..ac12ecfd --- /dev/null +++ b/tests/testapp/migrations/0003_simplemodelwithalternativeinterface.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.14 on 2025-09-28 19:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("testapp", "0002_create_homepage"), + ] + + operations = [ + migrations.CreateModel( + name="SimpleModelWithAlternativeInterface", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ], + ), + ] diff --git a/tests/testapp/models/core.py b/tests/testapp/models/core.py index 732b1554..16048157 100644 --- a/tests/testapp/models/core.py +++ b/tests/testapp/models/core.py @@ -53,6 +53,14 @@ class SimpleModel(models.Model): graphql_interfaces = (AdditionalInterface,) +@register_singular_query_field("simpleModelWithAlternativeInterface") +class SimpleModelWithAlternativeInterface(models.Model): + graphql_interfaces = ( + AdditionalInterface, + "testapp.interfaces.AlternativeInterface", + ) + + def custom_middleware_one(next, root, info, **args): info.context.custom_middleware_one = True return next(root, info, **args)