Skip to content

Fix reverse OneToOneField relations not being persisted to the database - #592

Open
benaduo wants to merge 3 commits into
model-bakers:mainfrom
benaduo:fix/reverse-one-to-one
Open

Fix reverse OneToOneField relations not being persisted to the database#592
benaduo wants to merge 3 commits into
model-bakers:mainfrom
benaduo:fix/reverse-one-to-one

Conversation

@benaduo

@benaduo benaduo commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #473

When a reverse OneToOneField accessor was passed as a kwarg (e.g. baker.make(User, person=some_person)), the relationship existed in memory but was never persisted — the FK on the related object was never set and never saved. Querying from the DB after the fact found nothing.

Root cause: Baker.instance() only detected ForeignRelatedObjectsDescriptor (reverse FK). Reverse OneToOne uses ReverseOneToOneDescriptor, which was not handled. Django's __init__ silently wrote it into instance.__dict__ without ever updating the related object.

Fix: detect ReverseOneToOneDescriptor in instance(), extract those attrs, and handle them in a new _handle_reverse_one_to_one() method that sets the FK on the related object and saves it — consistent with how _handle_one_to_many() works for reverse FK relations.

Changes

  • model_bakery/baker.py — new ReverseOneToOneDescriptor import; detection added to instance() loop; new _handle_reverse_one_to_one() method
  • tests/test_baker.py — regression test in TestBakerCreatesAssociatedModels verifying DB persistence via refresh_from_db()
  • CHANGELOG.md — entry under Changed

Summary by CodeRabbit

  • Bug Fixes
    • Fixed reverse One-to-One relationships not being persisted to the database when provided as keyword arguments to baker.make().
  • Tests
    • Added regression tests verifying reverse One-to-One persistence and retrieval, including coverage for multi-database usage.
  • Documentation
    • Updated CHANGELOG.md under Unreleased → Changed to note the fix.

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds detection and persistence for reverse OneToOneField relations passed as keyword arguments to baker.make(). Reverse descriptors are extracted during instantiation and handled during commit by setting and saving the related object’s forward foreign key.

Changes

Reverse one-to-one persistence

Layer / File(s) Summary
Attribute classification and commit handling
model_bakery/baker.py
Classifies reverse one-to-one descriptors separately, then assigns the created instance to the related object’s forward field and saves it while honoring _using.
Regression tests and changelog
tests/test_baker.py, CHANGELOG.md
Tests persistence on the default and selected databases, and documents the fix in the unreleased changelog.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Caller as baker.make()
    participant Baker as Baker.instance()
    participant Main as Created instance
    participant Related as Related object
    participant DB as Database

    Caller->>Baker: provide reverse one-to-one keyword argument
    Baker->>Baker: classify reverse descriptor
    Baker->>Main: commit created instance
    Baker->>Related: set forward relation to Main
    Related->>DB: save using selected database
    DB-->>Caller: return persisted relation
Loading

Possibly related PRs

Suggested reviewers: amureki, berinhard

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: persisting reverse OneToOneField relations.
Linked Issues check ✅ Passed The code and tests implement #473 by detecting reverse one-to-one descriptors, saving the related FK, and verifying database persistence.
Out of Scope Changes check ✅ Passed The only extra change is the changelog entry, which is directly related to the fix and not out of scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@benaduo
benaduo force-pushed the fix/reverse-one-to-one branch from 2872d8a to 88f408a Compare March 27, 2026 20:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@model_bakery/baker.py`:
- Around line 722-737: The reverse OneToOne handler ( _handle_reverse_one_to_one
), as well as _handle_m2m and _handle_one_to_many, are saving/adding related
objects without honoring the baker DB alias; update each handler to propagate
self._using (following the pattern in _save_related_objs) by replacing plain
value.save() / related_obj.save() calls with value.save(using=self._using) (or
related_obj.save(using=self._using)) and ensure any creation/add operations in
_handle_m2m and _handle_one_to_many also use self._using (e.g., save the
through/related instances with using=self._using or pass the alias to creation
helpers) instead of relying on instance._state.db.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e309325-0937-40eb-af5a-172f31b2133f

📥 Commits

Reviewing files that changed from the base of the PR and between b094155 and 2872d8a.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • model_bakery/baker.py
  • tests/test_baker.py

Comment thread model_bakery/baker.py Outdated
@benaduo

benaduo commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Hi @amureki, quick note on this one:

  • This fixes a silent bug where passing a reverse OneToOneField accessor as a kwarg (e.g. baker.make(User, person=some_person)) appeared to work in memory but was never persisted to the DB, querying after the fact found nothing.

  • Root cause: Baker.instance() already handled ForeignRelatedObjectsDescriptor (reverse FK) but ReverseOneToOneDescriptor was never detected, so Django's __init__ silently stored it in instance.__dict__ without updating the related object.

  • Fix mirrors the existing _handle_one_to_many() pattern, detect the descriptor, pop it from attrs, then after save set the FK on the related object and save it.

  • Regression test verifies DB persistence using refresh_from_db() so it can't pass just from in-memory state.

Happy to make any changes. Thanks for your time.

@benaduo
benaduo force-pushed the fix/reverse-one-to-one branch 2 times, most recently from fcf7b6e to 526df55 Compare April 1, 2026 23:39
@benaduo

benaduo commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

Hi @amureki, this PR is ready for review. All CI checks are passing.

Quick summary:

  • Fixes reverse OneToOneField relations not being persisted to the DB when passed as kwargs
  • Adds _handle_reverse_one_to_one() method mirroring the existing _handle_one_to_many() pattern
  • Propagates self._using correctly for multi-database support
  • 2 tests: regression test for the original bug + multi-DB test

Happy to make any changes based on your feedback.

@amureki amureki left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good contribution, thanks for diving into the complex topic!.
The change is looking good to me. And extra thanks for adding the tests! I left a couple of comments.

Comment thread tests/test_baker.py Outdated
Comment thread model_bakery/baker.py Outdated
@benaduo
benaduo force-pushed the fix/reverse-one-to-one branch from 526df55 to 218e385 Compare July 13, 2026 00:53
@benaduo

benaduo commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Hi @amureki, just a friendly bump on this one. I've rebased the branch against upstream/main to resolve the conflicts that accumulated over the past few months. All CI checks are passing. Would appreciate another look when you have time — happy to make any changes. Thanks!

@amureki

amureki commented Jul 13, 2026

Copy link
Copy Markdown
Member

Hi @amureki, just a friendly bump on this one. I've rebased the branch against upstream/main to resolve the conflicts that accumulated over the past few months. All CI checks are passing. Would appreciate another look when you have time — happy to make any changes. Thanks!

Hey hey!

Do I miss something? My comments seem to be not addressed, plus CI is actually failing. Do I need to look into it or is it something you could still address?

Thanks!

@benaduo

benaduo commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review comments:

  • Removed the commented-out lines from \ est_reverse_one_to_one_is_persisted.
  • Reworded the _handle_reverse_one_to_one\ docstring so it no longer restates the method name, and noted it is consistent with how _handle_one_to_many\ persists reverse relations.

@amureki

amureki commented Aug 10, 2026

Copy link
Copy Markdown
Member

Addressed the review comments:

  • Removed the commented-out lines from \ est_reverse_one_to_one_is_persisted.
  • Reworded the _handle_reverse_one_to_one\ docstring so it no longer restates the method name, and noted it is consistent with how _handle_one_to_many\ persists reverse relations.

Something is still odd - tests and linters are failing.

Fixes model-bakers#473. When a reverse OneToOneField accessor was passed as a kwarg
(e.g. baker.make(User, person=some_person)), Django's __init__ silently
wrote it into the instance's __dict__ without setting the FK on the
related object or saving it. Querying from the DB found no relation.

Root cause: Baker.instance() only detected ForeignRelatedObjectsDescriptor
(reverse FK). Reverse OneToOne uses ReverseOneToOneDescriptor, which was
not handled.

Fix: detect ReverseOneToOneDescriptor in instance(), extract those attrs,
and handle them in a new _handle_reverse_one_to_one() method that sets the
FK on the related object and saves it.

# Conflicts:
#	CHANGELOG.md
#	model_bakery/baker.py
Address review: drop redundant commented-out lines from the regression
test and reword the _handle_reverse_one_to_one docstring so it no longer
restates the method name, and note consistency with _handle_one_to_many.

Signed-off-by: Benjamin Aduo <manizza14@gmail.com>
The reverse OneToOne change made _classify_attrs return a four-tuple, but
the existing TestClassifyAttrs tests still unpacked three values, so the
full suite raised ValueError on every CI matrix job. Update the unpacking
in the four tests to match the new signature and run black over the
changed code.

Signed-off-by: Benjamin Aduo <manizza14@gmail.com>
@benaduo
benaduo force-pushed the fix/reverse-one-to-one branch from 9ba69ae to 484b6a7 Compare August 16, 2026 17:16
@benaduo

benaduo commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@amureki Following up on your Aug 10 note that tests and linters were still failing — I've since rebased the branch onto the latest upstream/main (Aug 16), and that fully resolved it. All checks are now green:

  • linters: ruff, black, ty, zizmor all pass
  • SQLite + PostgreSQL matrix across Python 3.10–3.14 / Django 5.2–6.1: all pass
  • Docs, Coverage, Socket Security: all pass

The two review points you raised are also addressed in the current head (commented-out lines removed from the regression test, docstring reworded). Would appreciate another look whenever you get a chance. Thanks!

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.

Reverse one-to-one relation not persisting in database

2 participants