Query optimizer
Warning
All the extra features provided by this lib were contributed and merged directly into the official strawberry-graphql-django lib. Since then this lib is deprecated and the official integration should be used instead.
If you were using this lib before, check out the migration guide for more information on how to migrate your code.
The automatic optimization is enabled by adding the DjangoOptimizerExtension
to your strawberry's schema config.
import strawberry
from strawberry_django_plus.optimizer import DjangoOptimizerExtension
schema = strawberry.Schema(
Query,
extensions=[
# other extensions...
DjangoOptimizerExtension,
]
)
Now consider the following:
Example
class Artist(models.Model):
name = models.CharField()
class Album(models.Moodel):
name = models.CharField()
release_date = models.DateTimeField()
artist = models.ForeignKey("Artist", related_name="albums")
class Song(models.Model):
name = model.CharField()
duration = models.DecimalField()
album = models.ForeignKey("Album", related_name="songs")
```
=== "schema"
```python
from strawberry_django_plus import gql
@gql.django.type(Artist)
class ArtistType:
name: auto
albums: "List[AlbumType]"
@gql.django.type(Album)
class AlbumType:
name: auto
release_date: auto
artist: ArtistType
songs: "List[SongType]"
@gql.django.type(Song)
class SongType:
name: auto
duration: auto
album_type: AlbumType
@gql.type
class Query:
artist: Artist = gql.django.field()
songs: List[SongType] = gql.django.field()
```
=== "query for the artist field"
```gql
query {
artist {
id
name
albums {
id
name
songs {
id
name
}
}
}
}
```
=== "optimized queryset for the artist field"
```python
Artist.objects.all().only("id", "name").prefetch_related(
Prefetch(
"albums",
queryset=Album.objects.all().only("id", "name").prefetch_related(
"songs",
Song.objects.all().only("id", "name"),
)
),
)
```
=== "query for the song field"
```gql
query {
song {
id
album
id
name
artist {
id
name
albums {
id
name
release_date
}
}
}
}
```
=== "optimized queryset for the song field"
```python
Song.objects.all().only(
"id",
"album",
"album__id",
"album__name",
"album__release_date", # Note about this below
"album__artist",
"album__artist__id",
).select_related(
"album",
"album__artist",
).prefetch_related(
"album__artist__albums",
Prefetch(
"albums",
Album.objects.all().only("id", "name", "release_date"),
)
)
```
!!! Note
Even though `album__release_date` field was not selected here, it got selected
in the prefetch query later. Since Django caches known objects, we have to select it here or
else it would trigger extra queries latter.
### Model property
It is possible to include hints for non-model fields using the field api or even our
`@model_property` (or its cached variation, `@cached_model_property`) decorator on the model
itself, for people who like to keep all the business logic at the model.
For example, the following will automatically optimize [`only`](https://docs.djangoproject.com/en/4.0/ref/models/querysets/#only) and [`select_related`](https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.query.QuerySet.select_related) if that
field gets selected:
```python
from strawberry_django_plus import gql
class Song(models.Model):
name = models.CharField()
@gql.model_property(only=["name", "album__name"], select_related=["album"])
def name_with_album(self) -> str:
return f"{self.album.name}: {self.name}"
@gql.django.type(Song)
class SongType:
name: auto
name_with_album: str
Another option would be to define that on the field itself:
@gql.django.type(Song)
class SongType:
name: auto
name_with_album: str = gql.django.field(
only=["name", "album__name"],
select_related=["album"],
)