Skip to content
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

Do not bypass many-to-many related fields. #299

Merged
merged 2 commits into from Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed
- Extend type hints in `model_bakery.recipe` module, make `Recipe` class generic [PR #292](https://github.com/model-bakers/model_bakery/pull/292)
- Explicitly add _fill_optional parameters to baker.make and baker.prepare to aid IDE autocomplete function. [PR #264](https://github.com/model-bakers/model_bakery/pull/264)
- Fixed errors with reverse M2M relationships [PR #299](https://github.com/model-bakers/model_bakery/pull/299)

### Removed

Expand Down
10 changes: 7 additions & 3 deletions model_bakery/baker.py
Expand Up @@ -30,7 +30,11 @@
from django.db.models.fields.related import (
ReverseManyToOneDescriptor as ForeignRelatedObjectsDescriptor,
)
from django.db.models.fields.reverse_related import ManyToOneRel, OneToOneRel
from django.db.models.fields.reverse_related import (
ManyToManyRel,
ManyToOneRel,
OneToOneRel,
)

from . import generators, random_gen
from ._types import M, NewM
Expand Down Expand Up @@ -404,8 +408,8 @@ def get_fields(self) -> Any:

def get_related(
self,
) -> List[Union[ManyToOneRel, OneToOneRel]]:
return [r for r in self.model._meta.related_objects if not r.many_to_many]
) -> List[Union[ManyToOneRel, OneToOneRel, ManyToManyRel]]:
return [r for r in self.model._meta.related_objects]

def _make(
self,
Expand Down
10 changes: 10 additions & 0 deletions tests/generic/models.py
Expand Up @@ -231,6 +231,16 @@ class Classroom(models.Model):
active = models.BooleanField(null=True)


class ClassroomM2MRelated(models.Model):
"""
This model was created in order to reproduce the scenario described
at issue 248 that is: a model with a M2M field (Classroom) being also used
as a M2M field from another model (ClassroomM2MRelated)
"""

related_classrooms = models.ManyToManyField(Classroom)
Comment on lines +234 to +241
Copy link
Member Author

Choose a reason for hiding this comment

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

Just by adding this model I was able to break 2 tests with the same error message as described by @a-edakin in #248. Here's the traceback of the failing tests to prove it:

______________________________________________________________ TestFillNullsTestCase.test_create_nullable_many_to_many_if_flagged_and_fill_optional _______________________________________________________________
tests/test_baker.py:621: in test_create_nullable_many_to_many_if_flagged_and_fill_optional
    classroom = baker.make(models.Classroom, make_m2m=True, _fill_optional=True)
model_bakery/baker.py:133: in make
    return baker.make(
model_bakery/baker.py:389: in make
    return self._make(**params)
model_bakery/baker.py:457: in _make
    self.model_attrs[field.name] = self.generate_value(
model_bakery/baker.py:660: in generate_value
    if field.has_default() and field.name not in self.rel_fields:
E   AttributeError: 'ManyToManyRel' object has no attribute 'has_default'
________________________________________________________ TestFillNullsTestCase.test_nullable_many_to_many_is_not_created_if_not_flagged_and_fill_optional _________________________________________________________
tests/test_baker.py:627: in test_nullable_many_to_many_is_not_created_if_not_flagged_and_fill_optional
    classroom = baker.make(models.Classroom, make_m2m=False, _fill_optional=True)
model_bakery/baker.py:133: in make
    return baker.make(
model_bakery/baker.py:389: in make
    return self._make(**params)
model_bakery/baker.py:457: in _make
    self.model_attrs[field.name] = self.generate_value(
model_bakery/baker.py:660: in generate_value
    if field.has_default() and field.name not in self.rel_fields:
E   AttributeError: 'ManyToManyRel' object has no attribute 'has_default'



class Store(models.Model):
customers = models.ManyToManyField(Person, related_name="favorite_stores")
employees = models.ManyToManyField(Person, related_name="employers")
Expand Down