You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If we have a recipe where we want to override the creation of a ForeignKey, by setting it to None, this does not work as expected. The ForeignKey is created, and then afterwards set to None. This makes it hard if we're interested in doing counts of objects in test, as there'll be "phantom" objects floating around.
Expected behavior
I expected this test to pass:
Example:
#mommy_recipes.py:order=Recipe(Order, location=foreign_key(location))
offloading=Recipe(
Offloading, facility=foreign_key(facility), standard_order=foreign_key(order), amount=201.88
)
#test.pydeftest_model_mommy():
service=mommy.make_recipe('facilities.offloading', standard_order=None, template_order=template)
assertservice.standard_orderisNone# TrueassertOrder.objects.count() is0# FALSE! One order has been created!
Actual behavior
A phantom object is created, and the test fails. This is really troublesome if you're doing counts in your tests, as sometimes they won't be right and it's very hard to figure out why.
After looking at the code I've determined where the issue is - it's in Recipe::_mapping:
def_mapping(self, new_attrs):
_save_related=new_attrs.get('_save_related', True)
rel_fields_attrs=dict((k, v) fork, vinnew_attrs.items() if'__'ink)
new_attrs=dict((k, v) fork, vinnew_attrs.items() if'__'notink)
mapping=self.attr_mapping.copy()
fork, vinself.attr_mapping.items():
# do not generate values if field value is provided# <<<<--- The mistake is on the next line. attrs.get() will return None both if the field# does not exist, AND if the field is None. So if I set the field to None, it will *not* skip # model creationifnew_attrs.get(k):
continueelifmommy.is_iterator(v):
ifisinstance(self._model, string_types):
m=finder.get_model(self._model)
else:
m=self._modelifknotinself._iterator_backupsorm.objects.count() ==0:
self._iterator_backups[k] =itertools.tee(
self._iterator_backups.get(k, [v])[0]
)
mapping[k] =self._iterator_backups[k][1]
elifisinstance(v, RecipeForeignKey):
a= {}
forkey, valueinlist(rel_fields_attrs.items()):
ifkey.startswith('%s__'%k):
a[key] =rel_fields_attrs.pop(key)
recipe_attrs=mommy.filter_rel_attrs(k, **a)
if_save_related:
mapping[k] =v.recipe.make(**recipe_attrs)
else:
mapping[k] =v.recipe.prepare(**recipe_attrs)
elifisinstance(v, related):
mapping[k] =v.make()
# Here we update the mapping with the new_attrs, that also transfers None. This is why# the final object has the attribute correctly set to Nonemapping.update(new_attrs)
mapping.update(rel_fields_attrs)
returnmapping
A fix I've monkeypatched my own recipe is reasonably simple, just use an empty class to distinguish from user-supplied None, and "no-field", DRF does the same:
# empty classclassempty:
pass#recipe::_mapping check then looks like thisifnew_attrs.get(k, empty) isnotempty:
continue
I've confirmed this fixes the issue. I'm willing to submit a PR if you'll accept it.
Versions
Python: 3.7
Django: 2.2
Model Mommy: 1.6.0
The text was updated successfully, but these errors were encountered:
Short summary
If we have a recipe where we want to override the creation of a ForeignKey, by setting it to None, this does not work as expected. The ForeignKey is created, and then afterwards set to None. This makes it hard if we're interested in doing counts of objects in test, as there'll be "phantom" objects floating around.
Expected behavior
I expected this test to pass:
Example:
Actual behavior
A phantom object is created, and the test fails. This is really troublesome if you're doing counts in your tests, as sometimes they won't be right and it's very hard to figure out why.
After looking at the code I've determined where the issue is - it's in Recipe::_mapping:
A fix I've monkeypatched my own recipe is reasonably simple, just use an
empty
class to distinguish from user-supplied None, and "no-field", DRF does the same:I've confirmed this fixes the issue. I'm willing to submit a PR if you'll accept it.
Versions
Python: 3.7
Django: 2.2
Model Mommy: 1.6.0
The text was updated successfully, but these errors were encountered: