Skip to main content

A set of tools for using dataloaders with Django and Strawberry GraphQL.

Project description

Dataloaders for Django and Strawberry

A set of tools for using dataloaders with Django and Strawberry without unnecessary boilerplate.

Installation

pip install strawberry-django-dataloaders

Usage & examples

This package provides 3 levels of generating dataloaders, each offering higher level of abstraction than the previous one.

View

In the graphql/ endpoint where you wish to use dataloaders, use (or subclass) DataloaderAsyncGraphQLView. This is necessary because created dataloaders are stored to the request context. This ensures that:

  • fresh dataloader instances are created for each request
  • a dataloader persists for the duration of the request

which are both important properties for loaded values caching.

from django.urls import path

from strawberry_django_dataloaders.views import DataloaderAsyncGraphQLView

urlpatterns = [
    path('graphql/', DataloaderAsyncGraphQLView.as_view(schema=...)),
]

Models definition

Definition of models used in the examples.

from django.db import models

class Fruit(models.Model):
    plant = models.OneToOneField("FruitPlant", ...)
    color = models.ForeignKey("Color", ...)
    varieties = models.ManyToManyField("FruitVariety", ..., related_name="fruits")

class FruitEater(models.Model):
    favourite_fruit = models.ForeignKey("Fruit", ..., related_name="eaters")

Level 1: Simple dataloader

On the first level, we're defining and using different dataloader for each relationship.

One-to-one and Many-to-one relationship

  1. Define the dataloaders
from strawberry_django_dataloaders import dataloaders

class ColorPKDataLoader(dataloaders.BasicPKDataLoader):
    model = models.Color


class FruitPlantPKDataLoader(dataloaders.BasicPKDataLoader):
    model = models.FruitPlant
  1. Use them when defining the Strawberry type
@strawberry_django.type(models.Fruit)
class FruitType:
    id: strawberry.auto
    
    ### ↓ HERE ↓ ###
    @strawberry.field
    async def color(self: "models.Fruit", info: "Info") -> ColorType | None:
        return await dataloaders.ColorPKDataLoader(context=info.context).load(self.color_id)

    @strawberry.field
    async def plant(self: "models.Fruit", info: "Info") -> FruitPlantType | None:
        return await dataloaders.FruitPlantPKDataLoader(context=info.context).load(self.plant_id)

One-to-many relationship

  1. Define the dataloader
from strawberry_django_dataloaders import dataloaders

class FruitEatersReverseFKDataLoader(dataloaders.BasicReverseFKDataLoader):
    model = models.FruitEater
    reverse_path = "favourite_fruit_id"   # <-- is the "reverse" FK field from FruitEater to Fruit model
  1. Use it when defining the Strawberry type
@strawberry_django.type(models.Fruit)
class FruitType:
    id: strawberry.auto
    
    ### ↓ HERE ↓ ###
    @strawberry.field
    async def eaters(self: "models.Fruit", info: "Info") -> list[FruitEaterType]:
        return await dataloaders.FruitEatersReverseFKDataLoader(context=info.context).load(self.pk)

Level 2: Dataloader factories

When using the dataloader factories, we no longer need to define a dataloader for each relation.

from strawberry_django_dataloaders import factories


@strawberry_django.type(models.Fruit)
class FruitTypeDataLoaderFactories:
    id: strawberry.auto
    
    ### ↓ ONE-TO-ONE AND MANY-TO-ONE DATALOADERS ↓ ###
    @strawberry.field
    async def color(self: "models.Fruit", info: "Info") -> ColorType | None:
        loader = factories.PKDataLoaderFactory.get_loader_class("tests.Color")
        return await loader(context=info.context).load(self.color_id)

    @strawberry.field
    async def plant(self: "models.Fruit", info: "Info") -> FruitPlantType | None:
        loader = factories.PKDataLoaderFactory.get_loader_class("tests.FruitPlant")
        return await loader(context=info.context).load(self.plant_id)
    
    ### ↓ ONE-TO-MANY DATALOADER ↓ ###
    @strawberry.field
    async def eaters(self: "models.Fruit", info: "Info") -> list[FruitEaterType]:
        loader = factories.ReverseFKDataLoaderFactory.get_loader_class(
            "tests.FruitEater",
            reverse_path="favourite_fruit_id",
        )
        return await loader(context=info.context).load(self.color_id)

Level 3: Auto dataloader field

A field used in a similar fashion as native Django strawberry field, but it has auto-defined correct dataloader handler based on the field relationship.

from strawberry_django_dataloaders import fields 

@strawberry_django.type(models.Fruit)
class FruitTypeAutoDataLoaderFields:
    id: strawberry.auto
    color: ColorType = fields.auto_dataloader_field()
    plant: FruitPlantType = fields.auto_dataloader_field()
    varieties: list[FruitVarietyType] = fields.auto_dataloader_field()
    eaters: list[FruitEaterType] = fields.auto_dataloader_field()

Contributing

Pull requests for any improvements are welcome.

Poetry is used to manage dependencies. To get started follow these steps:

git clone https://github.com/VojtechPetru/strawberry-django-dataloaders
cd strawberry-django-dataloaders
poetry install
poetry run pytest

Pre commit

We have a configuration for pre-commit, to add the hook run the following command:

pre-commit install

Links

Known issues/shortcomings

  • Many-to-many relation is currently not supported.

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

strawberry_django_dataloaders-0.2.0.tar.gz (8.4 kB view details)

Uploaded Source

Built Distribution

File details

Details for the file strawberry_django_dataloaders-0.2.0.tar.gz.

File metadata

File hashes

Hashes for strawberry_django_dataloaders-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f76c47c58065fe449b88d0913332ae91e919f60ef9d57ebca2f8ebcb62d7c666
MD5 6d617731fcb4b182c87691ec44dd10e5
BLAKE2b-256 47590e87b6838b165a15b3246ebb49581ae08f323f5f2a308501febd0d4597ad

See more details on using hashes here.

File details

Details for the file strawberry_django_dataloaders-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for strawberry_django_dataloaders-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0a47f61115a3596120e70d5a79cf936b55b20c5472c6b5bad59e0dc7456bc379
MD5 2a8704d6b9a69e8b9d3ab53314c99341
BLAKE2b-256 478cdf5c21509b839d41fe555fb378f682d907b4e5b058639e01d235b3eeff5e

See more details on using hashes here.

Supported by

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