File: relay.md

package info (click to toggle)
strawberry-graphql-django 0.62.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,968 kB
  • sloc: python: 27,530; sh: 17; makefile: 16
file content (104 lines) | stat: -rw-r--r-- 4,584 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
---
title: Relay
---

# Relay Support

You can use the [official strawberry relay integration](https://strawberry.rocks/docs/guides/relay)
directly with django types like this:

```python title="types.py"
import strawberry
import strawberry_django
from strawberry_django.relay import DjangoListConnection


class Fruit(models.Model):
    ...


@strawberry_django.type(Fruit)
class FruitType(relay.Node):
    ...


@strawberry.type
class Query:
    # Option 1: Default relay without totalCount
    # This is the default strawberry relay behaviour.
    # NOTE: you need to use strawberry_django.connection() - not the default strawberry.relay.connection()
    fruit: strawberry.relay.ListConnection[FruitType] = strawberry_django.connection()

    # Option 2: Strawberry django also comes with DjangoListConnection
    # this will allow you to get total-count on your query.
    fruit_with_total_count: DjangoListConnection[
        FruitType
    ] = strawberry_django.connection()

    # Option 3: You can manually create resolver by your method manually.
    @strawberry_django.connection(DjangoListConnection[FruitType])
    def fruit_with_custom_resolver(self) -> list[SomeModel]:
        return Fruit.objects.all()
```

Behind the scenes this extension is doing the following for you:

- Automatically resolve the `relay.NodeID` field using the [model's pk](https://docs.djangoproject.com/en/4.2/ref/models/fields/#django.db.models.Field.primary_key)
- Automatically generate resolves for connections that doesn't define one. For example,
  `some_model_conn` and `some_model_conn_with_total_count` will both define a custom resolver
  automatically that returns `SomeModel.objects.all()`.
- Integrate connection resolution with all other features available in this lib. For example,
  [filters](filters.md), [ordering](ordering.md) and
  [permissions](permissions.md) can be used together with connections defined
  by strawberry django.

You can also define your own `relay.NodeID` field and your resolve, in the same way as
`some_model_conn_with_resolver` is doing. In those cases, they will not be overridden.

> [!TIP]
> If you are only working with types inheriting from `relay.Node` and `GlobalID`
> for identifying objects, you might want to set `MAP_AUTO_ID_AS_GLOBAL_ID=True`
> in your [strawberry django settings](./settings.md) to make sure `auto` fields gets
> mapped to `GlobalID` on types and filters.

Also, this lib exposes a `strawberry_django.relay.DjangoListConnection`, which works
the same way as `strawberry.relay.ListConnection` does, but also exposes a
`totalCount` attribute in the connection.

For more customization options, like changing the pagination algorithm, adding extra fields
to the `Connection`/`Edge` type, take a look at the
[official strawberry relay integration](https://strawberry.rocks/docs/guides/relay)
as those are properly explained there.

## Cursor based connections

As an alternative to the default `ListConnection`, `DjangoCursorConnection` is also available.
It supports pagination through a Django `QuerySet` via "true" cursors.
`ListConnection` uses slicing to achieve pagination, which can negatively affect performance for huge datasets,
because large page numbers require a large `OFFSET` in SQL.
Instead, `DjangoCursorConnection` uses range queries such as `Q(due_date__gte=...)` for pagination. In combination
with an Index, this makes for more efficient queries.

`DjangoCursorConnection` requires a _strictly_ ordered `QuerySet`, that is, no two entries in the `QuerySet`
must be considered equal by its ordering. `order_by('due_date')` for example is not strictly ordered, because two
items could have the same due date. `DjangoCursorConnection` will automatically resolve such situations by
also ordering by the primary key.

When the order for the connection is configurable by the user (for example via
[`@strawberry_django.order`](./ordering.md)) then cursors created by `DjangoCursorConnection` will not be compatible
between different orders.

The drawback of cursor based pagination is that users cannot jump to a particular page immediately. Therefor
cursor based pagination is better suited for special use-cases like an infinitely scrollable list.

Otherwise `DjangoCursorConnection` behaves like other connection classes:

```python
@strawberry.type
class Query:
    fruit: DjangoCursorConnection[FruitType] = strawberry_django.connection()

    @strawberry_django.connection(DjangoCursorConnection[FruitType])
    def fruit_with_custom_resolver(self) -> list[Fruit]:
        return Fruit.objects.all()
```