@@ -230,3 +230,121 @@ This argument is also sent back to the client with the mutation result
230230(you do not have to do anything). For services that manage
231231a pool of many GraphQL requests in bulk, the ``clientIDMutation ``
232232allows 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