torchbox / wagtail-grapple

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

Need to work via a through model #68

Open thclark opened 4 years ago

thclark commented 4 years ago

I have an explicit through model, with which I define a Many-to-Many relationship to allow the related items to be orderable in wagtail.

I've succeeded in getting this to work with grapple as shown below (for posterity should anyone need the same).

However, the query and response are messy, you have to go all the way through the through model. I'm wondering if there's a more elegant way of achieving this (see below).

The example:

@register_snippet
class Section(index.Indexed, Model):
    name = CharField(max_length=80)
    # ... and other fields, and panels etc etc ...

    graphql_fields = [
        GraphQLString("name"),
    ]

class PageSectionsOrderable(Orderable):
    """ This through-model allows us to select and reorder one or more Sections from the *Section snippets
    """
    page = ParentalKey('cms_site.SitePage', on_delete=CASCADE, related_name='sections')
    section = ForeignKey('cms_site.Section', on_delete=CASCADE, related_name='pages')

    panels = [
        SnippetChooserPanel('section'),
    ]

    graphql_fields = [
        GraphQLInt('section_id'),
        GraphQLForeignKey('section', 'cms_site.Section')
    ]

class SitePage(Page):
    """ SitePage is used to manage SEO data and general website content including hero banners
    """
    # ... fields, content panels, etc ...

    graphql_fields = [
        GraphQLString("subtitle_text"),
        GraphQLCollection(
            GraphQLForeignKey,
            "sections",
            "cms_site.PageSectionsOrderable"
        ),
    ]

We'd use the following query to get the Sections with the pages:

{
    pages {
        ...on SitePage {
            subtitleText
            sections {
              section {
                id
                name
              }
            }

        }
    }
}

Which gives the response:

{
  "data": {
    "pages": [
      {},
      {
        "title": "Its a page with two sections",
        "sections": [
          {
            "section": {
              "id": "1",
              "name": "Section The First"
            }
          },
          {
            "section": {
              "id": "2",
              "name": "Section The Second"
            }
          }
        ]
      }
    ]
  }
}

Is there a sensible way to collapse the sections to remove the extra { "section": {} } in the graph structure? My first instinct is to start dabbling with creating a GraphQLThrough field in grapple but being a total newbie with GraphQL, I'm wondering if this will massively screw up some fundamental paradigms?

Would be pleased to hear any thoughts anyone has on that approach?

timmysmalls commented 3 years ago

Interesting question... I generally create a property on the model that looks something like this:

class SitePage(Page):

    ...

    @property
    def direct_accessor_for_sections(self):  # normally, I'd just call this "sections", and give the association model a name indicative of it being an association model.
        return Section.objects.filter(pages__page=self)

If there's a way to add a graphql field containing this property instead of a direct database field you should be able to achieve your goal, it would seem? Maybe try something like:

GraphQLCollection(    
     GraphQLForeignKey,    
     'direct_accessor_for_sections',  # Terrible name, but for now this refers to our property containing the queryset of Sections.
     'cms_site.Section',    
),
dopry commented 1 year ago

@thclark were you ever able to figure this our?