-
-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] v1.2.1 breaks usage of baker.make(related_model__field=value) #136
Comments
Full pytest output:
|
Hi @cb109 thanks for opening this issue. I didn't have time to try to reproduce this and explore why this error is happening, but if you want to investigate here's a good starting point: the 1.2.1 changelog with the updates and links for the PRs. |
Looking into it, the culprit is the def generate_value(self, field: Field, commit: bool = True) -> Any:
is_content_type_fk = isinstance(field, ForeignKey) and issubclass(
self._remote_field(field).model, contenttypes.models.ContentType
)
- if field.name in self.attr_mapping:
+ if field.has_default():
+ if callable(field.default):
+ return field.default()
+ return field.default
+ elif field.name in self.attr_mapping:
generator = self.attr_mapping[field.name]
elif getattr(field, "choices"):
generator = random_gen.gen_from_choices(field.choices)
@@ -520,13 +545,12 @@ class Baker(object):
generator = generators.get(field.__class__)
elif field.__class__ in self.type_mapping:
generator = self.type_mapping[field.__class__]
- elif field.has_default():
- return field.default
else:
raise TypeError("%s is not supported by baker." % field.__class__) It changes the order of checks, checking for default first, and later for type_mapping. For my problem, swapping that order back to check type_mapping first and default afterwards solves my issue. It breaks two tests though:
Will take a closer look tomorrow, maybe there is a compromise that satisfies all use cases. |
Thanks for the help @cb109! You've saved me a lot of investigation time =) |
One more observation: The problem in my case is that I'm using def get_default_spotcategory_id():
try:
return SpotCategory.objects.get(name=settings.DEFAULT_SPOTCATEGORY_NAME).id
except SpotCategory.DoesNotExist:
return None
class TVSpot(models.Model):
category = models.ForeignKey(
"SpotCategory",
null=True,
default=get_default_spotcategory_id,
on_delete=models.SET_DEFAULT,
) AFAIK it's what Django wants you to do: https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.Field.default
model_bakery used to tolerate it, ends up with the |
Ok @cb109 now we got into the real problem! For me the initial description was very odd because I'm also using 1.2.1 in my Django projects with "field lookups" for related model and it was working fine. Now it's even easier for me to create a unit test reproducing this scenario! Thanks for the help =) If you wanna go ahead and give yourself a try, I advise you to:
If you find trouble with the third step, feel free to open a PR with the breaking test and I'll be glad to help you from there. Otherwise, it's possible that I can find some time in the weekend to fix this. |
Run test like: $ pytest tests/ -k test_filling_foreignkey_with_default_id
@berinhard Thanks for having a look at it. I gave it a first try, see #137. Reverted my failed fixing attempt, but was able to reproduce the issue as a test. |
I found issue that might be related:
then in test
It wasn't the case before 1.2.1. |
#117 introduced an issue for cases where `id` values is provided as a default for foreign keys. This PR attempts to fix that issue by checking if generated value for FK field is an instance of this model - if not it uses `_id` as a field name (https://docs.djangoproject.com/en/3.1/ref/models/fields/#database-representation).
@cb109 hey Christoph, thank you for raising this issue and providing a test case! This is very helpful and got me to the point of making a "dirty" draft fix: I still need to check if I am not missing anything there, add some code comments (as it becomes complicated) and maybe extend test cases. |
@amureki Thank you for working on this 😃 I think we are close! I have installed your branch locally and ran our tests against it: It seems that creating related model instances is now problematic, when those are optional and use # tests/generic/models.py
class DummyOptionalForeignKey(models.Model):
named_thing = models.ForeignKey(
"NamedThing",
null=True,
default=None,
on_delete=models.SET_NULL,
)
# tests/test_filling_fields.py
@pytest.mark.django_db
class TestFillingOptionalForeignKeyField:
def test_filling_optional_foreignkey_implicitly(self):
dummy = baker.make(models.DummyOptionalForeignKey, named_thing__name="Optional")
assert dummy.named_thing.name == "Optional" (It does not seem to matter whether we use The test above fails for me with your changes, as no EDIT: Fixed test class and method names. |
@cb109 great, thanks for running your tests against your setup! This helps to discover cases for which we do not have tests yet. 👍 I am trying provided test case now, however it is also related to the original issue introduced in 1.2.1 (enforcing default) and has nothing to do with my draft change. I'll try to see how can we cover this case. Update: @pytest.mark.django_db
class TestFillingForeignKeyFieldWithDefaultFunctionReturningId:
def test_filling_foreignkey_with_default_id(self):
dummy = baker.make(
models.DummyForeignKeyWithDefaultIdModel
)
assert dummy.named_thing.id == models.get_default_namedthing_id()
assert dummy.named_thing.name == "Default"
def test_filling_foreignkey_with_default_id_with_custom_arguments(self):
dummy = baker.make(
models.DummyForeignKeyWithDefaultIdModel, named_thing__name="Not default"
)
assert dummy.named_thing.__class__.objects.count() == 1
assert dummy.named_thing.id == 1
assert dummy.named_thing.name == "Not default"
@pytest.mark.django_db
class TestFillingOptionalForeignKeyField:
def test_filling_optional_foreignkey_implicitly(self):
dummy = baker.make(models.DummyOptionalForeignKey, named_thing__name="Optional")
assert dummy.named_thing.name == "Optional" |
@amureki just pulled it and ran our custom tests, all is green ✔️ Well done! And so much more elegant! |
* Allow id to be used as FK default (Fix #136) #117 introduced an issue for cases where `id` values is provided as a default for foreign keys. This PR attempts to fix that issue by checking if generated value for FK field is an instance of this model - if not it uses `_id` as a field name (https://docs.djangoproject.com/en/3.1/ref/models/fields/#database-representation). * Another fix idea: use default unless given custom argument * Remove old way, do some cleanup * Update CHANGELOG.md
Specifying field values of related model instances within the
baker.make()
signature (e.g.baker.make(my_related_model__field=value)
is not possible anymore with v1.2.1. This still works in v1.2.0.Expected behavior
The model instance AND its required related model instances should be created, populated with random defaults except the values we explicitly passed in the call.
Actual behavior
ValueError: Cannot assign "<fk-id>": "<model>.<fk-field>" must be a "<fk-model>" instance.
Reproduction Steps
Try creating a model instance with
baker.make()
using related field assignments for an FK field that has a default value/callable returning an ID as seen in the following example. Any related models will do, these are just for illustration:Run tests with different model_bakery versions like:
Versions
OS: Ubuntu 20.04
Database: MySQL 5.7.32
Python: 3.8
Django: 2.2.17
pytest: 6.1.2
Model Bakery: 1.2.1
Maybe you do have an idea which of the changes in 1.2.1 is related to this. Let me know what I can do to help fix this, thanks.
The text was updated successfully, but these errors were encountered: