Decorators ========== Grapple exposes a few fields for the root schema such as ``pages``, ``images``, ``documents``, ``media`` and ``redirects``. Here are some useful decorators that allow you to expand your GraphQL schema further: ``@register_query_field`` ------------------------- .. module:: grapple.helpers .. class:: register_query_field(field_name, plural_field_name=None, query_params=None, required=False, plural_required=False, plural_item_required=False, middleware=None) You can easily expose any Django model from your codebase by adding the ``@register_query_field`` decorator like so: .. code-block:: python from django.db import models from wagtail.admin.edit_handlers import FieldPanel from grapple.helpers import register_query_field from grapple.models import GraphQLString @register_query_field("advert") class Advert(models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [FieldPanel("url"), FieldPanel("text")] graphql_fields = [GraphQLString("url"), GraphQLString("text")] def __str__(self): return self.text You can now query your adverts with the following query: :: query { # Get all adverts adverts { url text } # Get a specific advert advert(id: 1) { url text } } You can add custom query parameters like so: .. code-block:: python @register_query_field( "advert", "adverts", {"id": graphene.Int(), "url": graphene.String()} ) class Advert(models.Model): pass # your actual implementation here and then use it in your queries: :: query { # Get a specific advert advert(url: "some-unique-url") { url text } } You can make the singular query return type required like so: .. code-block:: python @register_query_field("advert", required=True) class Advert(models.Model): pass # your actual implementation here and then should look like this on your schema: :: advert(id: Int): Advert! instead of: :: advert(id: Int): Advert You can can also make the plural query return list type required: .. code-block:: python @register_query_field("advert", plural_required=True) class Advert(models.Model): pass # your actual implementation here making the plural query look like this on your schema: :: adverts(id: Int, ...): [Advert]! instead of the default: :: adverts(id: Int, ...): [Advert] If you want to make the plural query return list item type required: .. code-block:: python @register_query_field("advert", plural_item_required=True) class Advert(models.Model): pass # your actual implementation here making the plural query look like this: :: adverts(id: Int, ...): [Advert!] instead of the default: :: adverts(id: Int, ...): [Advert] You can add a middleware to the queries generated by the ``register_query_field`` decorator: .. code-block:: python from grapple.middleware import IsAuthenticatedMiddleware @register_query_field("advert", middleware=[IsAuthenticatedMiddleware]) class Advert(models.Model): pass # your actual implementation here Note that you must add ``GrappleMiddleware`` to the Graphene ``MIDDLEWARE`` setting. More information can be found in the :doc:`middleware docs `. ``@register_paginated_query_field`` ----------------------------------- .. module:noindex: grapple.helpers .. class:: register_paginated_query_field(field_name, plural_field_name=None, query_params=None, required=False, plural_required=False, plural_item_required=False, middleware=None) You can easily expose any Django model from your codebase by adding the ``@register_paginated_query_field`` decorator like so: .. code-block:: python from grapple.helpers import register_paginated_query_field @register_paginated_query_field("advert") class Advert(models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [FieldPanel("url"), FieldPanel("text")] graphql_fields = [GraphQLString("url"), GraphQLString("text")] def __str__(self): return self.text You can now query your adverts with the following query: :: query { # Get adverts paginated adverts(page: 1, perPage: 10) { items { url text } pagination { total count perPage currentPage prevPage nextPage totalPages } } # Get a specific advert advert(id: 1) { url text } } The default ``per_page`` value is 10 and can be changed with the ``GRAPPLE["PAGE_SIZE"]`` setting. The ``per_page`` has a maximum value of 100 by default and can be changed with the ``GRAPPLE["MAX_PAGE_SIZE"]`` setting. .. code-block:: python # settings.py GRAPPLE = { # ... "PAGE_SIZE": 10, "MAX_PAGE_SIZE": 100, } You can add custom query parameters like so: .. code-block:: python @register_paginated_query_field( "advert", "adverts", { "id": graphene.Int(), "url": graphene.String(), }, ) class Advert(models.Model): pass # your actual implementation here and then use it in your queries: :: query { # Get a specific advert advert(url: "some-unique-url") { url text } } You can make the singular query return type required like so: .. code-block:: python @register_paginated_query_field("advert", required=True) class Advert(models.Model): pass # your actual implementation here and then should look like this on your schema: :: advert(id: Int): Advert! instead of: :: advert(id: Int): Advert You can can also make the plural query return list type required: .. code-block:: python @register_paginated_query_field("advert", plural_required=True) class Advert(models.Model): pass # your actual implementation here making the plural query look like this on your schema: :: adverts(page: Int, perPage: Int, ...): AdvertPaginatedType! Type AdvertPaginatedType { items: [Advert]! pagination: PaginationType! } instead of the default: :: adverts(page: Int, perPage: Int, ...): AdvertPaginatedType Type AdvertPaginatedType { items: [Advert] pagination: PaginationType } If you want to make the plural query return list item type required: .. code-block:: python @register_paginated_query_field("advert", plural_item_required=True) class Advert(models.Model): pass # your actual implementation here making the plural query look like this: :: adverts(page: Int, perPage: Int, ...): AdvertPaginatedType Type AdvertPaginatedType { items: [Advert!] pagination: PaginationType } instead of the default: :: adverts(page: Int, perPage: Int, ...): AdvertPaginatedType Type AdvertPaginatedType { items: [Advert] pagination: PaginationType } You can add middleware to the queries generated by the ``register_paginated_query_field`` decorator: .. code-block:: python from grapple.middleware import IsAuthenticatedMiddleware @register_paginated_query_field("advert", middleware=[IsAuthenticatedMiddleware]) class Advert(models.Model): pass # your actual implementation here More information can be found on :doc:`middleware docs `. ``@register_singular_query_field`` ----------------------------------- .. module:noindex: grapple.helpers .. class:: register_singular_query_field(field_name, query_params=None, required=False, middleware=None) Returns the first item of the given type using the ``Model`` ordering. You can expose any Django model by decorating it with ``@register_singular_query_field``. This is especially useful when you have Wagtail Pages with ``max_count`` of one(`Ref: Wagtail documentation `_), thus there is no need to query by id. .. code-block:: python from grapple.helpers import register_singular_query_field @register_singular_query_field("first_advert") class Advert(models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [FieldPanel("url"), FieldPanel("text")] graphql_fields = [GraphQLString("url"), GraphQLString("text")] def __str__(self): return self.text and then use it in your queries: :: query { # Get the first advert firstAdvert { url text } } If you have multiple items, you could change the order: :: query { # Get the first advert firstAdvert(order: "-id") { url text } } You can add middleware to the queries generated by the ``register_singular_query_field`` decorator: .. code-block:: python from grapple.middleware import IsAuthenticatedMiddleware @register_singular_query_field("first_advert", middleware=[IsAuthenticatedMiddleware]) class Advert(models.Model): pass # your actual implementation here More information can be found on :doc:`middleware docs `. ``@register_streamfield_block`` ------------------------------- .. module:noindex: grapple.helpers .. class:: register_streamfield_block(cls) The ``register_streamfield_block`` decorator can be used to extend the schema with custom StreamField block types. .. code-block:: python from wagtail import blocks from grapple.helpers import register_streamfield_block @register_streamfield_block class CustomStreamBlock(blocks.StreamBlock): text = blocks.TextBlock() class Meta: graphql_description = "This is a streamblock with a textblock child" If a block's ``Meta`` class has a ``graphql_description`` attribute, this value will be exposed as the ``description`` in introspection queries. To register additional interfaces for the block, add them with your block's ``graphql_interfaces`` attribute: .. code-block:: python import graphene from wagtail import blocks from grapple.helpers import register_streamfield_block class CustomInterface(graphene.Interface): text = graphene.String() @register_streamfield_block class CustomInterfaceBlock(blocks.StructBlock): text = blocks.TextBlock() graphql_interfaces = (CustomInterface,)