Skip to content

Commit 2164e9d

Browse files
authored
Fix #397 -- Prevent related() from creating duplicate parents (#563)
1 parent 4021e19 commit 2164e9d

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
### Changed
13+
- Fix `related()` with FK relations creating duplicate parent entities ([#397](https://github.com/model-bakers/model_bakery/issues/397))
1314

1415
### Removed
1516

model_bakery/baker.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,11 +658,26 @@ def _handle_auto_now(self, instance: Model, attrs: dict[str, Any]):
658658
setattr(instance, k, v)
659659

660660
def _handle_one_to_many(self, instance: Model, attrs: dict[str, Any]):
661+
"""Handle reverse one-to-many relationships.
662+
663+
For related() callables, automatically injects the parent instance
664+
to the FK field to avoid duplicate creation via foreign_key() in child recipes.
665+
"""
666+
import types
667+
668+
from .recipe import related
669+
661670
for key, values in attrs.items():
662671
manager = getattr(instance, key)
663672

664673
if callable(values):
665-
values = values()
674+
if isinstance(values, types.MethodType) and isinstance(
675+
values.__self__, related
676+
):
677+
fk_field_name = manager.field.name
678+
values = values(**{fk_field_name: instance})
679+
else:
680+
values = values()
666681

667682
for value in values:
668683
# Django will handle any operation to persist nested non-persisted FK because

model_bakery/recipe.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,6 @@ def __init__(self, *args: str | Recipe[M]) -> None:
244244
else:
245245
raise TypeError("Not a recipe")
246246

247-
def make(self) -> list[M | list[M]]:
247+
def make(self, **attrs: Any) -> list[M | list[M]]:
248248
"""Persist objects to m2m relation."""
249-
return [m.make() for m in self.related]
249+
return [m.make(**attrs) for m in self.related]

tests/test_recipes.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,20 @@ def test_related_models_recipes(self):
441441
assert lady.dog_set.all()[0].breed == "Pug"
442442
assert lady.dog_set.all()[1].breed == "Basset"
443443

444+
def test_related_fk_does_not_create_duplicate_parent(self):
445+
"""Regression test for issue #397.
446+
447+
When using related() with FK, the parent should be reused,
448+
not duplicated by the child recipe's foreign_key().
449+
"""
450+
from tests.generic.models import Person
451+
452+
lady = baker.make_recipe("tests.generic.dog_lady")
453+
assert Person.objects.count() == 1
454+
assert lady.dog_set.count() == 2
455+
for dog in lady.dog_set.all():
456+
assert dog.owner == lady
457+
444458
def test_related_models_recipes_make_mutiple(self):
445459
ladies = baker.make_recipe("tests.generic.dog_lady", _quantity=2)
446460
assert ladies[0].dog_set.count() == 2

0 commit comments

Comments
 (0)