Skip to content

Commit 82717eb

Browse files
authored
Replace SnippetObjectType with SnippetInterface (#405)
* Rename CustomInterface to AdditionalInterface The name `CustomInterface` is too similar with `CustomPageInterface` and the upcoming `CustomSnippetInterface` and their custom interfaces settings file. Better rename it to `AdditionalInterface` so there is less confusion. * Rename the custom interface settings file The settings file will contain an override for the default snippet interface, in addition to the default page interface, so we're renaming it up front here. * Replace SnippetObjectType with SnippetInterface Fixes #386 API clients could use a field on snippet objects to determine the type of snippet they are looking at. Therefore, we change the snippet type to an interface, similar to the page interface, so it can expose a new field called `snippetType`. * Update changelog * Add contentType field on SnippetInterface * Test for no registered snippets * Create an instance each of both snippet types
1 parent 6cebc7b commit 82717eb

19 files changed

+337
-313
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## Unreleased
22

3+
### Changed
4+
5+
- `SnippetObjectType` is replaced with `SnippetInterface` ([405](https://github.com/torchbox/wagtail-grapple/pull/405)) @mgax
6+
37
### Fixed
48

59
- `value` not being queryable on `EmbedBlock` ([#399](https://github.com/torchbox/wagtail-grapple/pull/399))@JakubMastalerz

docs/general-usage/decorators.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,12 +433,12 @@ To register additional interfaces for the block, add them with your block's ``gr
433433
from grapple.helpers import register_streamfield_block
434434
435435
436-
class CustomInterface(graphene.Interface):
436+
class MyInterface(graphene.Interface):
437437
text = graphene.String()
438438
439439
440440
@register_streamfield_block
441-
class CustomInterfaceBlock(blocks.StructBlock):
441+
class MyInterfaceBlock(blocks.StructBlock):
442442
text = blocks.TextBlock()
443443
444-
graphql_interfaces = (CustomInterface,)
444+
graphql_interfaces = (MyInterface,)

docs/general-usage/graphql-types.rst

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,33 +102,6 @@ The following fields are returned:
102102
fileHash: String
103103

104104

105-
106-
SnippetObjectType
107-
^^^^^^^^^^^^^^^^^
108-
109-
You won't see much of ``SnippetObjectType`` as it's only a Union type that
110-
groups all your Snippet models together. You can query all the available snippets
111-
under the ``snippets`` field under the root Query, The query is similar to
112-
an interface but ``SnippetObjectType`` doesn't provide any fields itself.
113-
114-
When snippets are attached to Pages you interact with your generated type itself
115-
as opposed to an interface or base type.
116-
117-
An example of querying all snippets:
118-
119-
::
120-
121-
query {
122-
snippets {
123-
...on Advert {
124-
id
125-
url
126-
text
127-
}
128-
}
129-
}
130-
131-
132105
SettingObjectType
133106
^^^^^^^^^^^^^^^^^
134107

docs/general-usage/interfaces.rst

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ the name of the model:
5050
}
5151

5252
You can change the default ``PageInterface`` to your own interface by changing the
53-
:ref:`PAGE_INTERFACE<page interface settings>` setting.
53+
:ref:`PAGE_INTERFACE<page interface setting>` setting.
5454

5555
As mentioned above there is both a plural ``pages`` and singular ``page``
5656
field on the root Query type that returns a ``PageInterface``.
@@ -108,6 +108,37 @@ in the interface:
108108

109109

110110

111+
``SnippetInterface``
112+
--------------------
113+
114+
``SnippetInterface`` is the default interface for all Wagtail snippet models. It is accessible through the
115+
``snippets`` field on the root query type. It exposes the following fields:
116+
117+
::
118+
119+
snippetType: String!
120+
contentType: String!
121+
122+
An example of querying all snippets:
123+
124+
::
125+
126+
query {
127+
snippets {
128+
snippetType
129+
contentType
130+
...on Advert {
131+
id
132+
url
133+
text
134+
}
135+
}
136+
}
137+
138+
You can change the default ``SnippetInterface`` to your own interface by changing the
139+
:ref:`SNIPPET_INTERFACE<snippet interface setting>` setting.
140+
141+
111142
Adding your own interfaces
112143
--------------------------
113144

@@ -119,49 +150,48 @@ Given the following example interface:
119150
.. code-block:: python
120151
121152
# interfaces.py
122-
from .interfaces import CustomInterface
123-
124-
125-
class CustomInterface(graphene.Interface):
153+
class MyInterface(graphene.Interface):
126154
custom_field = graphene.String()
127155
128156
you could add it to your Page model like so:
129157

130158
.. code-block:: python
131159
132160
from wagtail.models import Page
161+
from .interfaces import MyInterface
133162
134163
135164
class MyPage(Page):
136165
# ...
137166
138-
graphql_interfaces = (CustomInterface,)
167+
graphql_interfaces = (MyInterface,)
139168
140169
or any Django model:
141170

142171
.. code-block:: python
143172
144173
# models.py
145174
from django.db import models
175+
from .interfaces import MyInterface
146176
147177
148178
class MyModel(models.Model):
149179
# ...
150180
151-
graphql_interfaces = (CustomInterface,)
152-
181+
graphql_interfaces = (MyInterface,)
153182
154183
or a ``StreamField`` block:
155184

156185
.. code-block:: python
157186
158187
# blocks.py
159188
from wagtail.core import blocks
189+
from .interfaces import MyInterface
160190
161191
162192
class MyStructBlock(blocks.StructBlock):
163193
# ...
164194
165-
graphql_interfaces = (CustomInterface,)
195+
graphql_interfaces = (MyInterface,)
166196
167197
The provided interfaces will be added to the base interfaces for the model.

docs/getting-started/settings.rst

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,10 @@ Limit the maximum number of items that ``QuerySetList`` and ``PaginatedQuerySet`
141141
Default: ``100``
142142

143143

144-
.. _page interface settings:
144+
Wagtail model interfaces
145+
^^^^^^^^^^^^^^^^^^^^^^^^
145146

146-
Wagtail Page interface
147-
^^^^^^^^^^^^^^^^^^^^^^
147+
.. _page interface setting:
148148

149149
``PAGE_INTERFACE``
150150
******************
@@ -153,3 +153,14 @@ Used to construct the schema for Wagtail Page-derived models. It can be overridd
153153
page models.
154154

155155
Default: ``grapple.types.interfaces.PageInterface``
156+
157+
158+
.. _snippet interface setting:
159+
160+
``SNIPPET_INTERFACE``
161+
*********************
162+
163+
Used to construct the schema for Wagtail snippet models. It can be overridden to provide a custom interface for all
164+
snippet models.
165+
166+
Default: ``grapple.types.interfaces.SnippetInterface``

grapple/actions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .types.images import ImageObjectType, ImageRenditionObjectType
2828
from .types.pages import Page, get_page_interface
2929
from .types.rich_text import RichText as RichTextType
30+
from .types.snippets import get_snippet_interface
3031
from .types.streamfield import generate_streamfield_union
3132

3233

@@ -612,7 +613,7 @@ def register_snippet_model(cls: Type[models.Model], type_prefix: str):
612613
return
613614

614615
# Create a GQL type that implements Snippet Interface
615-
snippet_node_type = build_node_type(cls, type_prefix, None)
616+
snippet_node_type = build_node_type(cls, type_prefix, get_snippet_interface())
616617

617618
if snippet_node_type:
618619
registry.snippets[cls] = snippet_node_type

grapple/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"MAX_PAGE_SIZE": 100,
3030
"RICHTEXT_FORMAT": "html",
3131
"PAGE_INTERFACE": "grapple.types.interfaces.PageInterface",
32+
"SNIPPET_INTERFACE": "grapple.types.interfaces.SnippetInterface",
3233
}
3334

3435
# List of settings that have been deprecated

grapple/types/interfaces.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,25 @@ def resolve_raw_value(self, info, **kwargs):
227227
return self.value.source
228228

229229
return self.value
230+
231+
232+
def get_snippet_interface():
233+
return import_string(grapple_settings.SNIPPET_INTERFACE)
234+
235+
236+
class SnippetInterface(graphene.Interface):
237+
snippet_type = graphene.String(required=True)
238+
content_type = graphene.String(required=True)
239+
240+
@classmethod
241+
def resolve_type(cls, instance, info, **kwargs):
242+
return registry.snippets[type(instance)]
243+
244+
def resolve_snippet_type(self, info, **kwargs):
245+
return self.__class__.__name__
246+
247+
def resolve_content_type(self, info, **kwargs):
248+
self.content_type = ContentType.objects.get_for_model(self)
249+
return (
250+
f"{self.content_type.app_label}.{self.content_type.model_class().__name__}"
251+
)

grapple/types/snippets.py

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,19 @@
11
import graphene
22

33
from ..registry import registry
4-
5-
6-
class SnippetTypes:
7-
# SnippetObjectType class can only be created if
8-
# registry.snippets.types is non-empty, and should only be created
9-
# once (graphene complains if we register multiple type classes
10-
# with identical names)
11-
_SnippetObjectType = None
12-
13-
@classmethod
14-
def get_object_type(cls):
15-
if cls._SnippetObjectType is None and registry.snippets:
16-
17-
class SnippetObjectType(graphene.Union):
18-
class Meta:
19-
types = registry.snippets.types
20-
21-
cls._SnippetObjectType = SnippetObjectType
22-
return cls._SnippetObjectType
4+
from .interfaces import get_snippet_interface
235

246

257
def SnippetsQuery():
26-
SnippetObjectType = SnippetTypes.get_object_type()
27-
28-
if SnippetObjectType is not None:
29-
30-
class Mixin:
31-
snippets = graphene.List(graphene.NonNull(SnippetObjectType), required=True)
32-
# Return all snippets.
33-
34-
def resolve_snippets(self, info, **kwargs):
35-
snippet_objects = []
36-
for snippet in registry.snippets:
37-
for object in snippet._meta.model.objects.all():
38-
snippet_objects.append(object)
39-
40-
return snippet_objects
41-
42-
return Mixin
8+
class Mixin:
9+
snippets = graphene.List(graphene.NonNull(get_snippet_interface), required=True)
4310

44-
else:
11+
def resolve_snippets(self, info, **kwargs):
12+
snippet_objects = []
13+
for snippet in registry.snippets:
14+
for object in snippet._meta.model.objects.all():
15+
snippet_objects.append(object)
4516

46-
class Mixin:
47-
pass
17+
return snippet_objects
4818

49-
return Mixin
19+
return Mixin

grapple/types/streamfield.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,7 @@ def resolve_items(self, info, **kwargs):
353353
def register_streamfield_blocks():
354354
from .documents import get_document_type
355355
from .images import get_image_type
356-
from .interfaces import get_page_interface
357-
from .snippets import SnippetTypes
356+
from .interfaces import get_page_interface, get_snippet_interface
358357

359358
class PageChooserBlock(graphene.ObjectType):
360359
page = graphene.Field(get_page_interface(), required=False)
@@ -391,20 +390,17 @@ def resolve_image(self, info, **kwargs):
391390
}
392391
)
393392

394-
SnippetObjectType = SnippetTypes.get_object_type()
395-
if SnippetObjectType is not None:
393+
class SnippetChooserBlock(graphene.ObjectType):
394+
snippet = graphene.Field(get_snippet_interface(), required=False)
396395

397-
class SnippetChooserBlock(graphene.ObjectType):
398-
snippet = graphene.Field(SnippetObjectType, required=False)
399-
400-
class Meta:
401-
interfaces = (StreamFieldInterface,)
396+
class Meta:
397+
interfaces = (StreamFieldInterface,)
402398

403-
def resolve_snippet(self, info, **kwargs):
404-
return self.value
399+
def resolve_snippet(self, info, **kwargs):
400+
return self.value
405401

406-
registry.streamfield_blocks.update(
407-
{
408-
wagtail.snippets.blocks.SnippetChooserBlock: SnippetChooserBlock,
409-
}
410-
)
402+
registry.streamfield_blocks.update(
403+
{
404+
wagtail.snippets.blocks.SnippetChooserBlock: SnippetChooserBlock,
405+
}
406+
)

tests/settings_custom_page_interface.py renamed to tests/settings_custom_interfaces.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33

44
GRAPPLE["PAGE_INTERFACE"] = "testapp.interfaces.CustomPageInterface" # noqa: F405
5+
GRAPPLE["SNIPPET_INTERFACE"] = "testapp.interfaces.CustomSnippetInterface" # noqa: F405

0 commit comments

Comments
 (0)