@@ -230,3 +230,121 @@ This argument is also sent back to the client with the mutation result
230
230
(you do not have to do anything). For services that manage
231
231
a pool of many GraphQL requests in bulk, the ``clientIDMutation ``
232
232
allows you to match up a specific mutation with the response.
233
+
234
+
235
+
236
+ Django Database Transactions
237
+ ----------------------------
238
+
239
+ Django gives you a few ways to control how database transactions are managed.
240
+
241
+ Tying transactions to HTTP requests
242
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
243
+
244
+ A common way to handle transactions in Django is to wrap each request in a transaction.
245
+ Set ``ATOMIC_REQUESTS `` settings to ``True `` in the configuration of each database for
246
+ which you want to enable this behavior.
247
+
248
+ It works like this. Before calling ``GraphQLView `` Django starts a transaction. If the
249
+ response is produced without problems, Django commits the transaction. If the view, a
250
+ ``DjangoFormMutation `` or a ``DjangoModelFormMutation `` produces an exception, Django
251
+ rolls back the transaction.
252
+
253
+ .. warning ::
254
+
255
+ While the simplicity of this transaction model is appealing, it also makes it
256
+ inefficient when traffic increases. Opening a transaction for every request has some
257
+ overhead. The impact on performance depends on the query patterns of your application
258
+ and on how well your database handles locking.
259
+
260
+ Check the next section for a better solution.
261
+
262
+ Tying transactions to mutations
263
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
264
+
265
+ A mutation can contain multiple fields, just like a query. There's one important
266
+ distinction between queries and mutations, other than the name:
267
+
268
+ ..
269
+
270
+ `While query fields are executed in parallel, mutation fields run in series, one
271
+ after the other. `
272
+
273
+ This means that if we send two ``incrementCredits `` mutations in one request, the first
274
+ is guaranteed to finish before the second begins, ensuring that we don't end up with a
275
+ race condition with ourselves.
276
+
277
+ On the other hand, if the first ``incrementCredits `` runs successfully but the second
278
+ one does not, the operation cannot be retried as it is. That's why is a good idea to
279
+ run all mutation fields in a transaction, to guarantee all occur or nothing occurs.
280
+
281
+ To enable this behavior for all databases set the graphene ``ATOMIC_MUTATIONS `` settings
282
+ to ``True `` in your settings file:
283
+
284
+ .. code :: python
285
+
286
+ GRAPHENE = {
287
+ # ...
288
+ " ATOMIC_MUTATIONS" : True ,
289
+ }
290
+
291
+ On the contrary, if you want to enable this behavior for a specific database, set
292
+ ``ATOMIC_MUTATIONS `` to ``True `` in your database settings:
293
+
294
+ .. code :: python
295
+
296
+ DATABASES = {
297
+ " default" : {
298
+ # ...
299
+ " ATOMIC_MUTATIONS" : True ,
300
+ },
301
+ # ...
302
+ }
303
+
304
+ Now, given the following example mutation:
305
+
306
+ .. code ::
307
+
308
+ mutation IncreaseCreditsTwice {
309
+
310
+ increaseCredits1: increaseCredits(input: { amount: 10 }) {
311
+ balance
312
+ errors {
313
+ field
314
+ messages
315
+ }
316
+ }
317
+
318
+ increaseCredits2: increaseCredits(input: { amount: -1 }) {
319
+ balance
320
+ errors {
321
+ field
322
+ messages
323
+ }
324
+ }
325
+
326
+ }
327
+
328
+ The server is going to return something like:
329
+
330
+ .. code :: json
331
+
332
+ {
333
+ "data" : {
334
+ "increaseCredits1" : {
335
+ "balance" : 10.0 ,
336
+ "errors" : []
337
+ },
338
+ "increaseCredits2" : {
339
+ "balance" : null ,
340
+ "errors" : [
341
+ {
342
+ "field" : " amount" ,
343
+ "message" : " Amount should be a positive number"
344
+ }
345
+ ]
346
+ },
347
+ }
348
+ }
349
+
350
+ But the balance will remain the same.
0 commit comments