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
- Define the dataloaders
from strawberry_django_dataloaders import dataloaders
class ColorPKDataLoader(dataloaders.BasicPKDataLoader):
model = models.Color
class FruitPlantPKDataLoader(dataloaders.BasicPKDataLoader):
model = models.FruitPlant
- 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
- 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
- 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
- Inspired and builds on top of a great article at: https://alexcleduc.com/posts/graphql-dataloader-composition/
- Repository: https://github.com/VojtechPetru/strawberry-django-dataloaders
- Issue tracker: https://github.com/VojtechPetru/strawberry-django-dataloaders/issues. In case of sensitive bugs (e.g. security vulnerabilities) please contact me at petru.vojtech@gmail.com directly.
Known issues/shortcomings
Many-to-manyrelation is currently not supported.
Metadata
Release files for strawberry-django-dataloaders 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| strawberry_django_dataloaders-0.2.0.tar.gz | 8.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| strawberry_django_dataloaders-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.7 kB
Release files / strawberry_django_dataloaders-0.2.0.tar.gz
| Download URL | strawberry_django_dataloaders-0.2.0.tar.gz |
|---|---|
| Size | 8.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f76c47c58065fe449b88d0913332ae91e919f60ef9d57ebca2f8ebcb62d7c666
|
|
BLAKE2b-256 checksum How to use checksums |
47590e87b6838b165a15b3246ebb49581ae08f323f5f2a308501febd0d4597ad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.7.0 CPython/3.10.13 Linux/6.2.0-1015-azure
|
Release files / strawberry_django_dataloaders-0.2.0-py3-none-any.whl
| Download URL | strawberry_django_dataloaders-0.2.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0a47f61115a3596120e70d5a79cf936b55b20c5472c6b5bad59e0dc7456bc379
|
|
BLAKE2b-256 checksum How to use checksums |
478cdf5c21509b839d41fe555fb378f682d907b4e5b058639e01d235b3eeff5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.7.0 CPython/3.10.13 Linux/6.2.0-1015-azure
|