File: mutations.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 (203 lines) | stat: -rw-r--r-- 6,384 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
---
title: Mutations
---

# Mutations

## Getting started

Mutations can be defined the same way as
[strawberry's mutations](https://strawberry.rocks/docs/general/mutations), but instead of
using `@strawberry.mutation`, use `@strawberry_django.mutation`.

Here are the differences between those:

- Strawberry Django's mutation will be sure that the mutation is executed in an async safe
  environment, meaning that if you are running ASGI and you define a `sync` resolver, it will
  automatically be wrapped in a `sync_to_async` call.
- It will better integrate with the [permission integration](./permissions.md)
- It has an option to automatically handle common django errors and return them
  in a standardized way (more on that below)

## Django errors handling

When defining a mutation you can pass `handle_django_errors=True` to make it handle
common django errors, such as `ValidationError`, `PermissionDenied` and `ObjectDoesNotExist`:

```python title="types.py"
@strawberry.type
class Mutation:
    @strawberry_django.mutation(handle_django_errors=True)
    def create_fruit(self, name: str, color: str) -> Fruit:
        if not is_valid_color(color):
            raise ValidationError("The color is not valid")

        # Creation can also raise ValidationError, if the `name` is
        # larger than its allowed `max_length` for example.
        fruit = models.Fruit.objects.create(name=name)
        return cast(Fruit, fruit)
```

The code above would generate following schema:

```graphql title="schema.graphql"
enum OperationMessageKind {
  INFO
  WARNING
  ERROR
  PERMISSION
  VALIDATION
}

type OperationInfo {
  """List of messages returned by the operation."""
  messages: [OperationMessage!]!
}

type OperationMessage {
  """The kind of this message."""
  kind: OperationMessageKind!

  """The error message."""
  message: String!

  """
  The field that caused the error, or `null` if it isn't associated with any particular field.
  """
  field: String

  """The error code, or `null` if no error code was set."""
  code: String
}

type Fruit {
  name: String!
  color: String!
}

union CreateFruitPayload = Fruit | OperationInfo

mutation {
  createFruit(
    name: String!
    color: String!
  ): CreateFruitPayload!
}
```

> [!TIP]
> If all or most of your mutations use this behaviour, you can change the
> default behaviour for `handle_django_errors` by setting
> `MUTATIONS_DEFAULT_HANDLE_ERRORS=True` in your [strawberry django settings](./settings.md)

## Input mutations

Those are defined using `@strawberry_django.input_mutation` and act the same way as
the `@strawberry_django.mutation`, the only difference being that it injects
an [InputMutationExtension](https://strawberry.rocks/docs/general/mutations#the-input-mutation-extension)
in the field, which converts its arguments in a new type (check the extension's docs
for more information).

## CUD mutations

The following CUD mutations are provided by this lib:

- `strawberry_django.mutations.create`: Will create the model using the data from the given input
- `strawberry_django.mutations.update`: Will update the model using the data from the given input
- `strawberry_django.mutations.delete`: Will delete the model using the id from the given input

A basic example would be:

```python title="types.py"
from strawberry import auto
from strawberry_django import mutations, NodeInput
from strawberry.relay import Node



@strawberry_django.type(SomeModel)
class SomeModelType(Node):
    name: auto

@strawberry_django.input(SomeModel)
class SomeModelInput:
    name: auto


@strawberry_django.partial(SomeModel)
class SomeModelInputPartial(NodeInput):
    name: auto

@strawberry.type
class Mutation:
    create_model: SomeModelType = mutations.create(SomeModelInput)
    update_model: SomeModelType = mutations.update(SomeModelInputPartial)
    delete_model: SomeModelType = mutations.delete(NodeInput)
```

Some things to note here:

- Those CUD mutations accept the same arguments as `@strawberry_django.mutation`
  accepts. This allows you to pass `handle_django_errors=True` to it for example.
- The mutation will receive the type in an argument named `"data"` by default.
  To change it to `"info"` for example, you can change it by passing
  `argument_name="info"` to the mutation, or set `MUTATIONS_DEFAULT_ARGUMENT_NAME="info"`
  in your [strawberry django settings](./settings.md) to make it the default when not provided.
- Take note that inputs using `partial` will _not_ automatically mark non-auto fields optional
  and instead will respect explicit type annotations;
  see [partial input types](./types.md#input-types) documentation for examples.
- It's also possible to update or delete a model using a unique identifier other than `id` by passing a `key_attr` argument:

```python
@strawberry_django.partial(SomeModel)
class SomeModelInputPartial:
    unique_field: strawberry.auto

@strawberry.type
class Mutation:
    update_model: SomeModelType = mutations.update(
        SomeModelInputPartial,
        key_attr="unique_field",
    )
    delete_model: SomeModelType = mutations.delete(
        SomeModelInputPartial,
        key_attr="unique_field",
    )
```

## Filtering

> [!CAUTION]
> Filtering on mutations is discouraged as it can potentially alter your entire model collection if there are issues with the filters.

Filters can be added to update and delete mutations. More information in the
[filtering](filters.md) section.

```python title="schema.py"
import strawberry
from strawberry_django import mutations

@strawberry.type
class Mutation:
    updateFruits: list[Fruit] = mutations.update(FruitPartialInput, filters=FruitFilter)
    deleteFruits: list[Fruit] = mutations.delete(filters=FruitFilter)

schema = strawberry.Schema(mutation=Mutation)
```

## Batching

If you need to make multiple creates, updates, or deletes as part of one atomic mutation you can use batching. Batching has a similar syntax except that the mutations take and return a list.

```python title="schema.py"
import strawberry
from strawberry_django import mutations

@strawberry.type
class Mutation:
    createFruits: list[Fruit] = mutations.create(list[FruitPartialInput])
    updateFruits: list[Fruit] = mutations.update(list[FruitPartialInput])
    deleteFruits: list[Fruit] = mutations.delete(list[FruitPartialInput])

schema = strawberry.Schema(mutation=Mutation)
```