FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Made change to allow models to update primary key values by dhumphreys · Pull Request #15 · wheels-dev/wheels · GitHub

Made change to allow models to update primary key values - #15

Closed
dhumphreys wants to merge 1 commit into
wheels-dev:new-masterfrom
dhumphreys:update-primary-keys
Closed

Made change to allow models to update primary key values#15
dhumphreys wants to merge 1 commit into
wheels-dev:new-masterfrom
dhumphreys:update-primary-keys

Conversation

Copy link
Copy Markdown

This was a problem in some places where numeric or character IDs needed to be changed, but Wheels was not putting the properties in the SET for the UPDATE to run.

Based on my findings, the hasChanged() call should be a sufficient check to decide whether or not to update any property value. The $addKeyWhereClause() function called from within $update() even considers that primary key values may have been changed. It doesn't make sense to not allow the model to send these changed values to the SQL server if the application required it.

This is reopening pull request #9, which I accidentally closed.

This was a problem in some places where numeric or character IDs needed
to be changed, but Wheels was not putting the properties in the SET for the
UPDATE to run.

Copy link
Copy Markdown
Author

Say you have a model, users, with a primary key of 'username' and an additional 'password' field.
When you run:

user = model("user").findByKey("dhumphreys");
user.update(username="dhumphreys2");

The update will go through without error, but the 'username' primary key is not actually updated. The changed piece of code was keeping any properties that are part of the primary key from ever being updated. In some situations, such as when an auto incremented numeric id is used, this is appropriate. However, there are cases where some applications may need to update primary key values, especially in tables with composite keys. My change allows any property to be updated, as long as it has actually been changed in the model object.

rip747 commented Sep 11, 2011

Copy link
Copy Markdown
Contributor

don,

you're part of core now so you can commit this directly. I still think there are some areas with this patch that we need to consider such as security. rails doesn't allow you to update primary keys through direct access or mass assignment. personally i would like to have some sort of setting this feature to be turned on both globally and at the model level.

Copy link
Copy Markdown
Author

Yes, I know I can commit directly. I just knew that this issue required some discussion, so I have not merged it yet. I also already had the changes in my own branch and saw no reason to put an extra feature branch in the main wheels repository.

We could enable this with a setting pretty easily. It only really comes into play when not using an auto increment integer as primary key.

rip747 commented Jul 28, 2012

Copy link
Copy Markdown
Contributor

looking at this again, i'm still thinking that this can cause quite a bit of havoc and security problems. including this patch would allow any primary key to be updated through mass assignment. the only way around that would be to use protectedProperties() and state the primary keys as protected properties on each model, which would be a pain.

this still leaves the problem though of when you actually need to update a primary key. for that a work around is to use the updateAll() method which doesn't have the restrictions of using update().

rip747 closed this Jul 28, 2012
bpamiri added a commit that referenced this pull request Apr 29, 2026
TestBox silently skips bundles it cannot instantiate (e.g. CFML parse
errors) — its JSON response shows totalPass: 0 with no failures and no
errors, indistinguishable from "all clear" or "no specs found." The
fresh-VM tutorial run lost ~10 minutes when an unescaped `#` inside a
CSS selector crashed Lucee's parser silently. The user only discovered
the broken spec by loading /wheels/app/tests in a browser.

Add countSpecsOnDisk() and listSpecsOnDisk() helpers to TestRunner
that walk the project's filesystem under variables.projectRoot and
return *Spec.cfc counts/names as dotted bundle names. Wire them into
Module.cfc::displayTestResults: if disk count exceeds TestBox's loaded
bundle count, emit a "WARN  N spec file(s) failed to compile and were
silently skipped:" block listing the unloaded paths, and append "X
failed to load" to the summary line.

The exit code is unchanged — strict-loading mode is intentionally out
of scope. The new code path is best-effort: any probe error is logged
in verbose mode and never crashes the test report.

Closes finding #2 in
docs/superpowers/plans/2026-04-29-fresh-vm-onboarding-findings.md
(subsumes April 19 #15).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bpamiri added a commit that referenced this pull request Apr 29, 2026
Records the four CLI fixes landed in batch B (gitkeep, migrate output,
test runner compile-error surfacing, reload hint) and adds a Batch D
shipped entry now that PR #2361 is merged. Marks #7 closed via the
out-of-band PR #2360 (Rails-style argument order). Crosses out
April 19's #15 ("test runner output format needs verification") which
is subsumed by 2026-04-29 finding #2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bpamiri added a commit that referenced this pull request Apr 29, 2026
* docs(docs): add batch B plan for CLI output polish

Plan covers findings #2, #3, #8, and a new sub-finding (.gitkeep files
not copied by scaffolder) from the 2026-04-29 fresh-VM onboarding
triage. Implementation lands in subsequent commits on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): copy .gitkeep files so empty test dirs survive git commit

copyTemplateDir() previously skipped any file named .gitkeep with a
comment that they "exist only to keep empty dirs in git" — but skipping
them meant the scaffolded app's tests/specs/{controllers,functional,
models}/ directories vanished on first git commit, contradicting the
tutorial's chapter 1 file tree. Same problem hits app/lib, app/jobs,
app/mailers, public/{files,images,javascripts,stylesheets}, and other
intentionally-empty directories that ship with .gitkeep markers in the
template tree (14 in total).

Copy .gitkeep files byte-for-byte (no placeholder processing — they're
empty by design). Extend NewCommandTemplateSpec to assert the .gitkeep
files exist on disk for three representative paths.

Note on test coverage: a deeper test that scaffolds via the production
copyTemplateDir code path would require reflective invocation of a
private method on Module.cfc; the existing template-existence check
plus CI's full integration run cover the regression sufficiently.

Closes the new sub-finding from
docs/superpowers/plans/2026-04-29-fresh-vm-onboarding-findings.md
(top of the "Shipped" section, surfaced during batch A's Task 0
reconnaissance).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(migration): emit CRLF (not bare CR) so migrator output renders correctly

Migrator.cfc and migrator/Base.cfc::announce() built their output using
bare Chr(13). On macOS, Linux, and the LuCLI out() pipe, bare CR moves
the cursor to column 0 without advancing the line, so subsequent text
overwrites. On a fresh `wheels migrate latest`, the section header,
divider, and per-table summary all collapsed onto a single line where
the tutorial promised three.

Switch every CR to CRLF (Chr(13) & Chr(10)) — 29 occurrences in
Migrator.cfc plus the single line in migrator/Base.cfc::announce().

Add MigratorOutputSpec to pin the announce() contract so future
migrator hacks can't drop the LF again. Update the existing
"is appending announcements" assertion in migrationSpec.cfc that was
previously asserting the buggy bare-CR concatenation.

Closes finding #3 in
docs/superpowers/plans/2026-04-29-fresh-vm-onboarding-findings.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): surface specs that fail to compile in wheels test output

TestBox silently skips bundles it cannot instantiate (e.g. CFML parse
errors) — its JSON response shows totalPass: 0 with no failures and no
errors, indistinguishable from "all clear" or "no specs found." The
fresh-VM tutorial run lost ~10 minutes when an unescaped `#` inside a
CSS selector crashed Lucee's parser silently. The user only discovered
the broken spec by loading /wheels/app/tests in a browser.

Add countSpecsOnDisk() and listSpecsOnDisk() helpers to TestRunner
that walk the project's filesystem under variables.projectRoot and
return *Spec.cfc counts/names as dotted bundle names. Wire them into
Module.cfc::displayTestResults: if disk count exceeds TestBox's loaded
bundle count, emit a "WARN  N spec file(s) failed to compile and were
silently skipped:" block listing the unloaded paths, and append "X
failed to load" to the summary line.

The exit code is unchanged — strict-loading mode is intentionally out
of scope. The new code path is best-effort: any probe error is logged
in verbose mode and never crashes the test report.

Closes finding #2 in
docs/superpowers/plans/2026-04-29-fresh-vm-onboarding-findings.md
(subsumes April 19 #15).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): hint at cold reload from wheels reload output

`wheels reload` re-fires the framework reload path (?reload=true) but
does NOT re-run onApplicationStart — surprising to users coming from
Rails or Django where restart is the default. Append a one-line cyan
note pointing readers at `wheels stop && wheels start` whenever they
need init code to re-execute.

Pairs with the chapter 6 doc fix in batch A: the contract is now
visible at both surfaces a fresh-VM user encounters (the auth tutorial
and the CLI itself).

Closes finding #8 in
docs/superpowers/plans/2026-04-29-fresh-vm-onboarding-findings.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): mark batch B + D items shipped, close April 19 #15

Records the four CLI fixes landed in batch B (gitkeep, migrate output,
test runner compile-error surfacing, reload hint) and adds a Batch D
shipped entry now that PR #2361 is merged. Marks #7 closed via the
out-of-band PR #2360 (Rails-style argument order). Crosses out
April 19's #15 ("test runner output format needs verification") which
is subsumed by 2026-04-29 finding #2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bpamiri added a commit that referenced this pull request May 1, 2026
Bundles 9 fixes uncovered by the 2026-04-30 fresh-VM tutorial bake.
Each chapter is a focused edit; the bundle ships one PR / one CI run
since they all live under web/sites/guides/.

Chapter 1 (Hello, Wheels):
- Drop `Controller.cfc` and `Model.cfc` from the "What got created"
  file tree — `wheels new` doesn't emit those base classes; the tree
  was aspirational. Add `rewrite.config` at the repo root, which IS
  emitted but wasn't listed. Finding #15.
- Add a "First start downloads ~74 MB" Aside before the welcome-page
  step so a 30-second wait isn't a surprise. Finding #16.

Chapter 2 (Your First Model):
- Replace the made-up `Migration complete.` Expected output with the
  shape the runner actually emits today (`Migrating from 0 up to <ts>`
  + `Created table posts`), and note that the wording can drift across
  snapshots so readers verify on the `Created table` line. Finding #10.
- Update the `t.timestamps()` description to mention all three columns
  it adds (`createdAt`, `updatedAt`, `deletedAt`) and link forward to
  Part 5's soft-delete callout. Previously claimed only two columns.

Chapter 3 (CRUD Scaffold):
- Rephrase the scaffold caution: it does NOT unconditionally append
  `.resources("posts")` to routes; it only appends when no resources
  line for the model exists. Chapter 2 already adds one, so the file
  is left alone. Finding #13.
- Update the "generator emits findByKey(params.key) everywhere" claim
  — actually it uses `params.post` (route model binding) on key-scoped
  actions; only positional helpers fall back to `findByKey`. Finding #14.

Chapter 4 (Validations + Turbo Frames):
- Drop the manual `<label>` wrappers around helpers in `_form.cfm` and
  use the helpers' own `label="..."` argument instead. The previous
  shape produced nested `<label>` elements — exactly what chapter 3's
  caution about object-bound helpers warned against. Finding #11.

Chapter 6 (Authentication, Part 6b):
- Name `app/events/onapplicationstart.cfm` explicitly as the place to
  register the session strategy, and explain why `config/app.cfm` is
  the wrong place (`Application.cfc` this-scope, DI container not yet
  initialized). The previous "or equivalent on-init block" hedge cost
  the runner real time. Finding #6.
- Recommend scraping `<meta name="csrf-token">` uniformly for curl
  smoke tests instead of mixing meta-tag-vs-form-input rules. Some
  forms (login raw inputs, comments, basecoat-rewritten views) don't
  always include the hidden input. Finding #7.
- Replace the `--data-urlencode` recipe with a known-working raw
  `--data` form that pre-encodes brackets and `@`. The previous "use
  the `=` separator" advice was incomplete — Lucee's form parser
  treats any bracketed key as a nested-struct path regardless of
  encoding form. Finding #8.

Findings deliberately not addressed in this PR:
- #9 (soft-delete callout) — verified against current code: `t.timestamps()`
  emits all three columns including `deletedAt`, and `softDeleteProperty`
  defaults to `deletedAt`. Chapter 5's callout matches reality. The
  runner's snapshot-1660 observation likely predates the current behavior.
- #12 (cosmetic migration drift) — aligning the docs to the generator's
  uglier emitted shape is value-negative; either source can drift again
  in a future release.
- #17 (silent `brew tap`) — `exit 0` with no output is brew convention;
  adding a "no output expected" note is noise.
- #19 (chapter 6 Compare table renders as wall) — markdown source is
  well-formed GFM; if the rendered output is broken it's a build/CSS
  issue not a source issue. Investigate in a follow-up if reproducible.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bpamiri added a commit that referenced this pull request May 23, 2026
…flag (#2781) (#2802)

* feat(migrator): t.references() columnNames alias + underscore suffix flag (#2781)

`t.references()` has shipped since the 2017 dbmigrate merge with two
quirks that bite anyone writing FK columns: the parameter is
`referenceNames` (every sibling helper uses `columnNames` via
`$combineArguments`), and the generated column suffix is `<x>id`
(no underscore), breaking Wheels model `belongsTo` defaults which
expect `<x>_id`.

This PR resolves both quirks without changing default behavior for
existing apps.

## Changes

- **`t.references()` accepts `columnNames`** as an alias for
  `referenceNames` via `$combineArguments(combine="referenceNames,columnNames",
  required=true)`. Both keep working; `columnNames` is the modern form.
- **New setting `useUnderscoreReferenceColumns`** governs the column
  suffix. Framework default: `false` (legacy `<x>id` preserved). New apps
  generated via `wheels new` get `set(useUnderscoreReferenceColumns=true)`
  pre-wired in `config/settings.cfm`. When true, both the FK column and
  the polymorphic `<x>type` companion become `<x>_id` / `<x>_type`.
- **Scaffold template** flipped to `t.references(columnNames="vacation")`
  reflecting the modern convention.
- **Conventions documented**: new `vendor/wheels/migrator/CLAUDE.md`
  formalizes the singular/plural alias pattern + the suffix flag.
  Anti-pattern #15 added to top-level CLAUDE.md so AI agents pick up
  the rule. Doc-block on `$combineArguments` expanded to position it as
  the framework's canonical alias convention with citations.

## Tests

5 new specs in `vendor/wheels/tests/specs/migrator/referencesSpec.cfc`
covering alias acceptance, both flag states, and the polymorphic suffix
path. Full suite: 3761 pass / 0 fail (was 3756 pre-PR).

## Scope

Focused PR1 of a planned 4-PR series. Migration.cfc command-version
consistency (`addReference`, `dropReference`, `removeColumn`, `addColumn`,
`changeColumn`, `addForeignKey` argument aliases + suffix flag in those
sites) lands in PR2 to keep this surface tight. `wheels upgrade check`
advisory-tier and specific advisories for opt-in conventions follow in
PR3 and PR4.

Closes #2781

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>

* fix(migrator): address Reviewer A/B consensus findings (round 1)

- Patch Migration.cfc::removeColumn(referenceName=) and addReference() to
  read $get("useUnderscoreReferenceColumns") so the wheels new template
  default (true) no longer produces wrong column names for these helpers
- referencesSpec.cfc: correct file header to describe what the spec
  actually covers; sibling Migration.cfc DB-roundtrip coverage stays in
  migrationSpec.cfc
- referencesSpec.cfc: add explicit useUnderscoreReferenceColumns=false
  reset to the alias describe block so test order can't false-fail it
- referencesSpec.cfc: add direct backward-compat test asserting
  t.references(referenceNames="user") still produces the expected column
- vendor/wheels/migrator/CLAUDE.md: drop the now-stale "lines 258, 278
  hard-code & 'id'" anti-pattern note; both sites now route through $get
- CHANGELOG.md [Unreleased] ### Added: document the columnNames alias
  and the useUnderscoreReferenceColumns setting (#2781)
- web/sites/guides v4-0-0 + v4-0-1-snapshot migrations.mdx: document the
  columnNames argument as the preferred form, the legacy referenceNames
  back-compat, and the useUnderscoreReferenceColumns suffix flag

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>

* test(migrator): address Reviewer A/B round-2 — addReference test coverage

Round-1 left two gaps:
1. `referencesSpec.cfc` header claimed Migration.cfc DB-roundtrip coverage
   in migrationSpec.cfc that didn't actually exist.
2. `Migration.cfc::addReference()` was patched to read $get("useUnderscore
   ReferenceColumns") but had zero direct tests anywhere.

This commit closes both.

## Changes

- `vendor/wheels/tests/specs/migrator/migrationSpec.cfc`: new
  "Tests addReference" describe block with two DB-roundtrip tests
  (`creates a FK on <name>id when flag is false` and `creates a FK on
  <name>_id when flag is true`). Both follow the existing
  "Tests addForeignKey" pattern and skip on SQLite (which doesn't
  support altering CONSTRAINTS), so they run against MySQL/PG/MSSQL/H2
  in CI.

- `vendor/wheels/tests/specs/migrator/referencesSpec.cfc`: header
  rewritten to accurately describe coverage — addReference DB-roundtrip
  now correctly points at migrationSpec.cfc, removeColumn(referenceName=)
  is noted as having TableDefinition-layer coverage only (PR2 follow-up
  adds direct DB-roundtrip via migrationCommandsSpec.cfc).

- `vendor/wheels/migrator/CLAUDE.md`: Tests section updated to match
  the new spec organization — explicit about which file covers what,
  why TableDefinition-layer tests are preferred for argument plumbing,
  and that FK-touching tests skip on SQLite.

## Verification

- `bash tools/test-local.sh migrator` (Lucee 7 + SQLite): 224 pass, 0
  fail, 6 skipped (the 4 existing FK-altering tests plus my 2 new ones
  on the same SQLite skip path). No regressions.
- The new tests fire on every non-SQLite engine in CI.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>

* test(migrator): add removeColumn(referenceName=) flag tests to close R2 coverage gap

Round-2 follow-up commit. The bot's address-review described
removeColumn(referenceName=) DB-roundtrip tests but didn't actually
push them (my round-2 commit was already at HEAD). This commit
makes the bot's address-review description retrospectively accurate
and closes the coverage gap that finding 1 originally cited.

## Changes

- `vendor/wheels/tests/specs/migrator/migrationSpec.cfc`: extend the
  existing "Tests removeColumn" describe block with two new it()
  blocks — one per flag state — that verify
  `removeColumn(referenceName=...)` computes the right column name to
  drop under both `useUnderscoreReferenceColumns=false` (legacy
  `<name>id`) and `=true` (modern `<name>_id`). These don't need the
  SQLite skip the addReference tests have because removeColumn just
  emits `ALTER TABLE DROP COLUMN`, which SQLite supports.

- `vendor/wheels/tests/specs/migrator/referencesSpec.cfc`: header
  updated to drop the "no direct DB-roundtrip coverage yet" caveat
  for removeColumn(referenceName=) since that coverage now exists in
  migrationSpec.cfc.

## Verification

- `bash tools/test-local.sh migrator`: 226 pass / 0 fail / 6 skipped
  (gained 2 from the new removeColumn tests; the addReference tests
  still skip on SQLite).
- `bash tools/test-local.sh` (full): 3764 pass / 0 fail.

No regressions. The new tests follow the same pattern as the
existing "drops column from table" test in the same describe block.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>

* test(migrator): save/restore useUnderscoreReferenceColumns in migrationSpec beforeAll/afterAll

Reviewer A round-3 robustness note: the flag-true tests in
migrationSpec.cfc reset the flag at the end of the test body, but if
addReference() or $dbinfo() throws between the set and the cleanup,
the dirty global leaks into every subsequent spec in the file.

Mirror the existing migratorObjectCase pattern — snapshot the flag in
beforeAll and restore it in afterAll. The in-test resets stay as
belt-and-suspenders; afterAll is the safety net for the throw case.

Reviewer A's second item (misleading header comment in referencesSpec)
was already fixed in dd2963c (previous commit) — the header now just
points at "Tests addReference" and "Tests removeColumn" describe blocks
without overclaiming what the TableDefinition-layer tests cover.

## Verification

- `bash tools/test-local.sh migrator`: 226 pass / 0 fail / 6 skipped.
  No regressions; the addReference and removeColumn tests still
  pass under the new save/restore contract.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>

* test(migrator): capture removeColumn state before assert so cleanup always runs

Reviewer A round-4 nit: the legacy and underscore removeColumn tests
had a mid-test sanity expect() between createTable and dropTable. If
the sanity assert fires, the test fails AND leaves the table in the
database (next run recovers via force=true, but the dirty state is
ugly).

Refactor to the established addForeignKey / addReference pattern in
the same file: capture before/after column lists, run cleanup, then
do both asserts at the end. Cleanup is guaranteed regardless of
which assert fails.

Round-3 finding 1 (no beforeAll/afterAll save/restore for
useUnderscoreReferenceColumns) was already fixed in 53034f9 — the
reviewer was looking at dd2963c, one commit older. The in-test
flag reset in the underscore test stays as belt-and-suspenders; the
afterAll() guard added in 53034f9 is the safety net for an
exception above that line.

## Verification

- `bash tools/test-local.sh migrator`: 226 pass / 0 fail / 6 skipped.
  No regressions.

The Reviewer B check failure on the prior SHA was a transient
GitHub Actions auth error (HTTP 401: Bad credentials calling
GraphQL), not a test or code failure. The new commit will trigger
a fresh Reviewer B run.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>

---------

Signed-off-by: Peter Amiri <peter@alurium.com>
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL