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)
```
|