torchbox / wagtail-grapple

A Wagtail app that makes building GraphQL endpoints a breeze!
https://wagtail-grapple.readthedocs.io/en/latest/
Other
155 stars 58 forks source link

Wagtail 4.1.1+ compatibility for Snippets #307

Open dopry opened 1 year ago

dopry commented 1 year ago

I just ran into an issue with https://github.com/wagtail/wagtail/pull/9605

I have a field definition like

categories = graphene.List(SnippetTypes.get_object_type())

My graphql interface started spitting out the error

lib\site-packages\graphene\types\schema.py", line 122, in add_type
    name = graphene_type._meta.name
AttributeError: 'NoneType' object has no attribute '_meta'

I don't import my grapple type definitions until my app.ready(), but I guess that is running before snippets.app.ready because get_object type() now returns None since snippets are not registered yet.

I am able to make it work by moving graphene_django, grapple, and my app after wagtail.snippets... This obviously isn't very ideal since now the template priority favors wagtail over my apps... :(

zerolab commented 1 year ago

Any chance you could add a minimal reproducible code snippet (or snippets)?

I don't have a good answer for this yet as we are always at the mercy of app interdependency.

dopry commented 1 year ago

I would be something along the lines of

settings.py

INSTALLED_APPS = [
    # graphql to make it all headless
    "graphene_django",
    "grapple",
    "myapp",

    # core wagtail stuff
    "wagtail.contrib.forms",
    "wagtail.contrib.redirects",
    "wagtail.embeds",
    "wagtail.sites",
    "wagtail.users",
    "wagtail.snippets",
    "wagtail.documents",
    "wagtail.images",
    "wagtail.search",
    "wagtail.admin",
    "wagtail",

app.py


@register_snippet
class PageCategory(Model):
    name = CharField(max_length=255)
    slug = CharField(max_length=255, unique=True)
    panels = [ FieldPanel("name"),    FieldPanel("slug")   ]

    def __str__(self):
        return self.name

    graphql_fields = [  GraphQLString("name"),    GraphQLString("slug"), ]

class MySnippetBlock(StructBlock):
    categories = ListBlock(SnippetChooserBlock("myapp.Category", required=False))

class MyPage(Page): 
    body = StreamField([MySnippetBlock])

class MyApp(AppConfig):
    name = "MyApp"

    def ready(self):

        class MySnippetType: 
              from grapple.types.snippets import SnippetTypes
              snippet_relationship = graphene.List(SnippetTypes.get_object_type())

The gist is registry.snippets.types is empty when SnippetTypes.get_object_type() is called.

I can work on a more formal test setup next week if it's necessary, but I'm launching a new property this week. This was just something I ran into trying to get on latest wagtail before launch, but isn't critical.

dopry commented 9 months ago

@zerolab this hit me again today as I was trying to upgrade my wagtail instance. I figured I should get on 5 before 6 gets releases. I don't want to be too far behind the current. ultimately what I had to do to resolve this was move my grapple implementation to it's own app and move grapple and my graphql app to after wagtail core... I ended up with

INSTALLED_APPS = [
    # graphql to make it all headless
    "graphene_django",
    "myapp",

    # core wagtail stuff
    "wagtail.contrib.forms",
    "wagtail.contrib.redirects",
    "wagtail.embeds",
    "wagtail.sites",
    "wagtail.users",
    "wagtail.snippets",
    "wagtail.documents",
    "wagtail.images",
    "wagtail.search",
    "wagtail.admin",
    "wagtail",

    "grapple",
    "myapp_grapple"

if the setup documentation isn't explicit about making the grapple integration it's own app and grapple's location in installed apps relative to wagtail that seems like it might be something worth doing to help other users avoid the headache I just went through working that out.

dopry commented 2 months ago

I was revisting my wagtail 5.x updates and I've ran into this again... It seems like snippets are hunting for hooks

  File "...\venv\lib\site-packages\grapple\actions.py", line 102, in add_app
    for snippet in get_snippet_models():
  File "...\venv\lib\site-packages\wagtail\snippets\models.py", line 32, in get_snippet_models
    search_for_hooks()
  File "...\venv\lib\site-packages\wagtail\hooks.py", line 106, in search_for_hooks
    list(get_app_submodules("wagtail_hooks"))

and I have

@hooks.register("register_schema_query")
def add_my_custom_query(query_mixins):
    query_mixins.append(CustomQueriesMixin)

Where I have

class CustomSearchPaginatedType(BasePaginatedType):
    class Meta:
        name = "CustomSearchPaginatedType"

    items = graphene.List(registry.models[CustomPage])
    pagination = graphene.Field(PaginationType)

I'm getting a keyError because registry.models isn't populated yet. So now whether I put my grapple app before or after wagtail/snippets it's not launching.

I've got a circular dependency here where grapple needs snippets to be registered to generate snippet types... but snippets are calling hooks that need grapple types to be registered....

Mostly documenting this here... don't have much time to debug it just now... I'll poke at it over the next few days.