Skip to content

Commit

Permalink
Fix circular import for PageInterface
Browse files Browse the repository at this point in the history
  • Loading branch information
mgax committed Sep 16, 2024
1 parent d9cd8f2 commit a945745
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 157 deletions.
2 changes: 1 addition & 1 deletion docs/getting-started/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@ Wagtail Page interface
Used to construct the schema for Wagtail Page-derived models. It can be overridden to provide a custom interface for all
page models.

Default: ``grapple.types.pages.PageInterface``
Default: ``grapple.types.interfaces.PageInterface``
2 changes: 1 addition & 1 deletion grapple/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def Mixin():

def GraphQLPage(field_name: str, **kwargs):
def Mixin():
from .types.pages import get_page_interface
from .types.interfaces import get_page_interface

return GraphQLField(field_name, get_page_interface(), **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion grapple/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"PAGE_SIZE": 10,
"MAX_PAGE_SIZE": 100,
"RICHTEXT_FORMAT": "html",
"PAGE_INTERFACE": "grapple.types.pages.PageInterface",
"PAGE_INTERFACE": "grapple.types.interfaces.PageInterface",
}

# List of settings that have been deprecated
Expand Down
160 changes: 160 additions & 0 deletions grapple/types/interfaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import graphene

from django.contrib.contenttypes.models import ContentType
from django.utils.module_loading import import_string
from graphql import GraphQLError
from wagtail.models import Page as WagtailPage

from ..registry import registry
from ..settings import grapple_settings
from ..utils import resolve_queryset
from .structures import QuerySetList


def get_page_interface():
return import_string(grapple_settings.PAGE_INTERFACE)


class PageInterface(graphene.Interface):
id = graphene.ID()
title = graphene.String(required=True)
slug = graphene.String(required=True)
content_type = graphene.String(required=True)
page_type = graphene.String()
live = graphene.Boolean(required=True)

url = graphene.String()
url_path = graphene.String(required=True)

depth = graphene.Int()
seo_title = graphene.String(required=True)
search_description = graphene.String()
show_in_menus = graphene.Boolean(required=True)

locked = graphene.Boolean()

first_published_at = graphene.DateTime()
last_published_at = graphene.DateTime()

parent = graphene.Field(get_page_interface)
children = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
next_siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
previous_siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
descendants = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
ancestors = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)

search_score = graphene.Float()

@classmethod
def resolve_type(cls, instance, info, **kwargs):
"""
If model has a custom Graphene Node type in registry then use it,
otherwise use base page type.
"""
from .pages import Page

return registry.pages.get(type(instance), Page)

def resolve_content_type(self, info, **kwargs):
self.content_type = ContentType.objects.get_for_model(self)
return (
f"{self.content_type.app_label}.{self.content_type.model_class().__name__}"
)

def resolve_page_type(self, info, **kwargs):
return get_page_interface().resolve_type(self.specific, info, **kwargs)

def resolve_parent(self, info, **kwargs):
"""
Resolves the parent node of current page node.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_parent
"""
try:
return self.get_parent().specific
except GraphQLError:
return WagtailPage.objects.none()

def resolve_children(self, info, **kwargs):
"""
Resolves a list of live children of this page.
Docs: https://docs.wagtail.io/en/stable/reference/pages/queryset_reference.html#examples
"""
return resolve_queryset(
self.get_children().live().public().specific(), info, **kwargs
)

def resolve_siblings(self, info, **kwargs):
"""
Resolves a list of sibling nodes to this page.
Docs: https://docs.wagtail.io/en/stable/reference/pages/queryset_reference.html?highlight=get_siblings#wagtail.query.PageQuerySet.sibling_of
"""
return resolve_queryset(
self.get_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_next_siblings(self, info, **kwargs):
"""
Resolves a list of direct next siblings of this page. Similar to `resolve_siblings` with sorting.
Source: https://github.com/wagtail/wagtail/blob/master/wagtail/core/models.py#L1384
"""
return resolve_queryset(
self.get_next_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_previous_siblings(self, info, **kwargs):
"""
Resolves a list of direct prev siblings of this page. Similar to `resolve_siblings` with sorting.
Source: https://github.com/wagtail/wagtail/blob/master/wagtail/core/models.py#L1387
"""
return resolve_queryset(
self.get_prev_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_descendants(self, info, **kwargs):
"""
Resolves a list of nodes pointing to the current page’s descendants.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_descendants
"""
return resolve_queryset(
self.get_descendants().live().public().specific(), info, **kwargs
)

def resolve_ancestors(self, info, **kwargs):
"""
Resolves a list of nodes pointing to the current page’s ancestors.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_ancestors
"""
return resolve_queryset(
self.get_ancestors().live().public().specific(), info, **kwargs
)

def resolve_seo_title(self, info, **kwargs):
"""
Get page's SEO title. Fallback to a normal page's title if absent.
"""
return self.seo_title or self.title

def resolve_search_score(self, info, **kwargs):
"""
Get page's search score, will be None if not in a search context.
"""
return getattr(self, "search_score", None)
150 changes: 1 addition & 149 deletions grapple/types/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,166 +2,18 @@

from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
from graphene_django.types import DjangoObjectType
from graphql import GraphQLError
from wagtail.models import Page as WagtailPage
from wagtail.models import Site

from ..registry import registry
from ..settings import grapple_settings
from ..utils import resolve_queryset, resolve_site_by_hostname
from .interfaces import get_page_interface
from .structures import QuerySetList


def get_page_interface():
return import_string(grapple_settings.PAGE_INTERFACE)


class PageInterface(graphene.Interface):
id = graphene.ID()
title = graphene.String(required=True)
slug = graphene.String(required=True)
content_type = graphene.String(required=True)
page_type = graphene.String()
live = graphene.Boolean(required=True)

url = graphene.String()
url_path = graphene.String(required=True)

depth = graphene.Int()
seo_title = graphene.String(required=True)
search_description = graphene.String()
show_in_menus = graphene.Boolean(required=True)

locked = graphene.Boolean()

first_published_at = graphene.DateTime()
last_published_at = graphene.DateTime()

parent = graphene.Field(get_page_interface)
children = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
next_siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
previous_siblings = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
descendants = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)
ancestors = QuerySetList(
graphene.NonNull(get_page_interface), enable_search=True, required=True
)

search_score = graphene.Float()

@classmethod
def resolve_type(cls, instance, info, **kwargs):
"""
If model has a custom Graphene Node type in registry then use it,
otherwise use base page type.
"""
return registry.pages.get(type(instance), Page)

def resolve_content_type(self, info, **kwargs):
self.content_type = ContentType.objects.get_for_model(self)
return (
f"{self.content_type.app_label}.{self.content_type.model_class().__name__}"
)

def resolve_page_type(self, info, **kwargs):
return get_page_interface().resolve_type(self.specific, info, **kwargs)

def resolve_parent(self, info, **kwargs):
"""
Resolves the parent node of current page node.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_parent
"""
try:
return self.get_parent().specific
except GraphQLError:
return WagtailPage.objects.none()

def resolve_children(self, info, **kwargs):
"""
Resolves a list of live children of this page.
Docs: https://docs.wagtail.io/en/stable/reference/pages/queryset_reference.html#examples
"""
return resolve_queryset(
self.get_children().live().public().specific(), info, **kwargs
)

def resolve_siblings(self, info, **kwargs):
"""
Resolves a list of sibling nodes to this page.
Docs: https://docs.wagtail.io/en/stable/reference/pages/queryset_reference.html?highlight=get_siblings#wagtail.query.PageQuerySet.sibling_of
"""
return resolve_queryset(
self.get_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_next_siblings(self, info, **kwargs):
"""
Resolves a list of direct next siblings of this page. Similar to `resolve_siblings` with sorting.
Source: https://github.com/wagtail/wagtail/blob/master/wagtail/core/models.py#L1384
"""
return resolve_queryset(
self.get_next_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_previous_siblings(self, info, **kwargs):
"""
Resolves a list of direct prev siblings of this page. Similar to `resolve_siblings` with sorting.
Source: https://github.com/wagtail/wagtail/blob/master/wagtail/core/models.py#L1387
"""
return resolve_queryset(
self.get_prev_siblings().exclude(pk=self.pk).live().public().specific(),
info,
**kwargs,
)

def resolve_descendants(self, info, **kwargs):
"""
Resolves a list of nodes pointing to the current page’s descendants.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_descendants
"""
return resolve_queryset(
self.get_descendants().live().public().specific(), info, **kwargs
)

def resolve_ancestors(self, info, **kwargs):
"""
Resolves a list of nodes pointing to the current page’s ancestors.
Docs: https://docs.wagtail.io/en/stable/reference/pages/model_reference.html#wagtail.models.Page.get_ancestors
"""
return resolve_queryset(
self.get_ancestors().live().public().specific(), info, **kwargs
)

def resolve_seo_title(self, info, **kwargs):
"""
Get page's SEO title. Fallback to a normal page's title if absent.
"""
return self.seo_title or self.title

def resolve_search_score(self, info, **kwargs):
"""
Get page's search score, will be None if not in a search context.
"""
return getattr(self, "search_score", None)


class Page(DjangoObjectType):
"""
Base Page type used if one isn't generated for the current model.
Expand Down
2 changes: 1 addition & 1 deletion grapple/types/redirects.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from grapple.types.sites import SiteObjectType

from .pages import get_page_interface
from .interfaces import get_page_interface


class RedirectObjectType(graphene.ObjectType):
Expand Down
3 changes: 2 additions & 1 deletion grapple/types/sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from wagtail.models import Site

from ..utils import resolve_queryset, resolve_site_by_hostname, resolve_site_by_id
from .pages import get_page_interface, get_specific_page
from .interfaces import get_page_interface
from .pages import get_specific_page
from .structures import QuerySetList


Expand Down
2 changes: 1 addition & 1 deletion grapple/types/streamfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ def resolve_items(self, info, **kwargs):
def register_streamfield_blocks():
from .documents import get_document_type
from .images import get_image_type
from .pages import get_page_interface
from .interfaces import get_page_interface
from .snippets import SnippetTypes

class PageChooserBlock(graphene.ObjectType):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from testapp.factories import BlogPageFactory, CustomInterfaceBlockFactory
from testapp.interfaces import CustomInterface

from grapple.types.pages import PageInterface, get_page_interface
from grapple.types.interfaces import PageInterface, get_page_interface


@skipIf(
Expand Down
2 changes: 1 addition & 1 deletion tests/testapp/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from wagtail.models import Page

from grapple.registry import registry
from grapple.types.pages import PageInterface
from grapple.types.interfaces import PageInterface
from grapple.types.rich_text import RichText
from testapp.models import Advert, AuthorPage

Expand Down

0 comments on commit a945745

Please sign in to comment.