Skip to content

save(only=) / save(exclude=) emit columns in set order, so the UPDATE text changes between runs #776

Closed

Description

@0x054

Problem

save(only={...}) and save(exclude={...}) take a set of column names, and the UPDATE they send lists the columns in that set's iteration order, which changes between Python processes:

await account.save(only={"label", "balance"})
# one run:     UPDATE "account" SET "label" = $1, "balance" = $2 WHERE "id" = $3
# another run: UPDATE "account" SET "balance" = $1, "label" = $2 WHERE "id" = $3

The row ends up the same, but the statement text does not. That surfaced while pinning the Mutations guide's SQL blocks (#725, PR #775): the docs test has to run with PYTHONHASHSEED=0 to get a stable statement, and the page tells readers the column order is not fixed. The statement log and .sql() show a different statement for the same call from one process to the next, which makes log diffs and any test asserting on the SQL flaky.

What this asks for

A write's column order is a function of the model, never of a set's hash order: only= / exclude= columns are emitted in the model's field declaration order (the order save() without only= already uses). Then the docs test drops PYTHONHASHSEED=0, and the Mutations page's sentence about unfixed order goes.

Worth checking at the same time: any other write that takes a set (bulk_create with only=/exclude=, if it does) and whether update(**fields) follows the call's keyword order (it should: keyword order is deterministic).

Related

Activity

  1. added
    bugSomething isn't working
    needs-triageNeeds evaluation by a maintainer
    on Oct 21, 2026
  2. 0x054 commented on Oct 21, 2026

    @0x054
    ContributorAuthor

    Status: verify-passed
    Worker: fix-src (epic #698, panel round 1 on #777)
    Branch: sdd/698-fix-src-round-1
    PR: #779
    Verify: passed 88ae5e5 (reviewer re-run, SQLite and Postgres; trial merge not run, base unchanged)
    Review: Spec PASS, Quality PASS (three Minors) — #779 (review)

    Every write now lists its columns in field declaration order: save(), save(only=), save(exclude=) and bulk_create. There were three causes: the only=/exclude= set, a fetched instance's alphabetical __dict__, and bytes/secret columns appended last.
    The docs SQL gate no longer pins PYTHONHASHSEED. The Mutations blocks are regenerated and the unfixed-order sentence is gone.
    Pinned by tests/test_save_column_order.py on SQLite and Postgres.

  3. added a commit that references this issue on Oct 21, 2026
  4. 0x054 commented on Oct 21, 2026

    @0x054
    ContributorAuthor

    Fixed in #779, merged into feat/docs-rewrite (c80e3eb); reaches main with PR #777.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageNeeds evaluation by a maintainer

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions