Skip to main content

Optimize database access inside graphene queries.

Project description

graphene-django-optimizer

build status coverage PyPI version python version django version

Optimize queries executed by graphene-django automatically, using select_related, prefetch_related and only methods of Django QuerySet.

Install

pip install graphene-django-optimizer

Note: If you are using Graphene V2, please install version 0.8. v0.9 and forward will support only Graphene V3

Usage

Having the following schema based on the tutorial of graphene-django (notice the use of gql_optimizer)

# cookbook/ingredients/schema.py
import graphene

from graphene_django.types import DjangoObjectType
import graphene_django_optimizer as gql_optimizer

from cookbook.ingredients.models import Category, Ingredient


class CategoryType(DjangoObjectType):
    class Meta:
        model = Category


class IngredientType(DjangoObjectType):
    class Meta:
        model = Ingredient


class Query(graphene.ObjectType):
    all_categories = graphene.List(CategoryType)
    all_ingredients = graphene.List(IngredientType)

    def resolve_all_categories(root, info):
        return gql_optimizer.query(Category.objects.all(), info)

    def resolve_all_ingredients(root, info):
        return gql_optimizer.query(Ingredient.objects.all(), info)

We will show some graphql queries and the queryset that will be executed.

Fetching all the ingredients with the related category:

{
  allIngredients {
    id
    name
    category {
      id
      name
    }
  }
}
# optimized queryset:
ingredients = (
    Ingredient.objects
    .select_related('category')
    .only('id', 'name', 'category__id', 'category__name')
)

Fetching all the categories with the related ingredients:

{
  allCategories {
    id
    name
    ingredients {
      id
      name
    }
  }
}
# optimized queryset:
categories = (
    Category.objects
    .only('id', 'name')
    .prefetch_related(Prefetch(
        'ingredients',
        queryset=Ingredient.objects.only('id', 'name'),
    ))
)

Advanced usage

Sometimes we need to have a custom resolver function. In those cases, the field can't be auto optimized. So we need to use gql_optimizer.resolver_hints decorator to indicate the optimizations.

If the resolver returns a model field, we can use the model_field argument:

import graphene
import graphene_django_optimizer as gql_optimizer


class ItemType(gql_optimizer.OptimizedDjangoObjectType):
    product = graphene.Field('ProductType')

    @gql_optimizer.resolver_hints(
        model_field='product',
    )
    def resolve_product(root, info):
        # check if user have permission for seeing the product
        if info.context.user.is_anonymous():
            return None
        return root.product

This will automatically optimize any subfield of product.

Now, if the resolver uses related fields, you can use the select_related argument:

import graphene
import graphene_django_optimizer as gql_optimizer


class ItemType(gql_optimizer.OptimizedDjangoObjectType):
    name = graphene.String()

    @gql_optimizer.resolver_hints(
        select_related=('product', 'shipping'),
        only=('product__name', 'shipping__name'),
    )
    def resolve_name(root, info):
        return '{} {}'.format(root.product.name, root.shipping.name)

Notice the usage of the type OptimizedDjangoObjectType, which enables optimization of any single node queries.

Finally, if your field has an argument for filtering results, you can use the prefetch_related argument with a function that returns a Prefetch instance as the value.

from django.db.models import Prefetch
import graphene
import graphene_django_optimizer as gql_optimizer


class CartType(gql_optimizer.OptimizedDjangoObjectType):
    items = graphene.List(
        'ItemType',
        product_id=graphene.ID(),
    )

    @gql_optimizer.resolver_hints(
        prefetch_related=lambda info, product_id: Prefetch(
            'items',
            queryset=gql_optimizer.query(Item.objects.filter(product_id=product_id), info),
            to_attr='gql_product_id_' + product_id,
        ),
    )
    def resolve_items(root, info, product_id):
        return getattr(root, 'gql_product_id_' + product_id)

With these hints, any field can be optimized.

Optimize with non model fields

Sometimes we need to have a custom non model fields. In those cases, the optimizer would not optimize with the Django .only() method. So if we still want to optimize with the .only() method, we need to use disable_abort_only option:

class IngredientType(gql_optimizer.OptimizedDjangoObjectType):
    calculated_calories = graphene.String()

    class Meta:
        model = Ingredient

    def resolve_calculated_calories(root, info):
        return get_calories_for_ingredient(root.id)


class Query(object):
    all_ingredients = graphene.List(IngredientType)

    def resolve_all_ingredients(root, info):
        return gql_optimizer.query(Ingredient.objects.all(), info, disable_abort_only=True)

Contributing

See CONTRIBUTING.md

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graphene-django-optimizer-0.10.0.tar.gz (14.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

graphene_django_optimizer-0.10.0-py2.py3-none-any.whl (10.4 kB view details)

Uploaded Python 2Python 3

File details

Details for the file graphene-django-optimizer-0.10.0.tar.gz.

File metadata

File hashes

Hashes for graphene-django-optimizer-0.10.0.tar.gz
Algorithm Hash digest
SHA256 5a5d9d1e1abda3571bdff77a71af5e71b994e5e9d97d17a576df89181284075d
MD5 b50fae4fe57ec4c4a6b658aae27e04b4
BLAKE2b-256 fa6ccd89093083debac1c8eba62f2f1daa55caeb3a27f0f908761a7e68e3ed2e

See more details on using hashes here.

File details

Details for the file graphene_django_optimizer-0.10.0-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for graphene_django_optimizer-0.10.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 5b81aaa114514c9a89d064aaaf09c8e00c27d1cf6f734993f0ea5afe776041b9
MD5 cb4a31d2932367a50e77fefe26d6a162
BLAKE2b-256 1be32f0d01168d9c781d17c8f49a8ffe3d52a370382b9a5f4f4a756d86a46a01

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page