Skip to content

How it works

/orders/batch/   [ rows × N ]    POST → batch_create, PATCH → batch_update, PUT → batch_replace, DELETE → batch_destroy
  1. payload must be a JSON array                         → 400
  2. len(rows) > batch_max_size                           → 413
  3. validation (BatchListSerializer, one pass after another):
     extraction   shape + key/pk coercion per row         → 400 (no collection runs)
     targets      update/destroy: get_queryset().in_bulk(pks) (request thread)
     not found    target pk missing or out of scope       → 404
     permission   check_object_permissions(target)        → 403
     keys         keys of the batch fields a row did not send (read_only
                  fields never read it): derived_from, else the stored value
     collection   the replacement collector, or the batch fields as one
                  dependency graph (TaskGroup: each fetch starts when its
                  depends_on are done) on the EVENT_LOOP loop; sync fetches on
                  worker threads (request thread inside a transaction);
                  nothing to fetch → no loop; destroy: nothing
                                                          → 504 timeout, 500 error
     row validation  child.run_validation per row, zero I/O
                  (update: serializer.instance = target)  → 400
     conflicts    model + batch_unique_together rules, in payload and vs DB
                                                          → 409
  4. perform_batch_<op>(serializer) → serializer.save():
     retry policy → transaction.atomic() → (targets re-fetched FOR UPDATE)
     → bulk or per-row write; IntegrityError              → 409 (rolled back)
  5. 201 / 200 with get_batch_response(serializer)