model-bakers/model_bakery

m2m, through fields and _bulk_create seems broken

mm-matthias opened this issue · 0 comments

Describe the issue
Bakery fails with an exception when trying to bulk create models that are using ManyToManyField with a through attribute.

To Reproduce

Suppose you have models like these:

class MyGroup(models.Model):
    id = models.CharField(max_length=255, primary_key=True)

class MyUser(models.Model):
    id = models.CharField(max_length=255, primary_key=True)
    groups = models.ManyToManyField(MyGroup, through="MyUserGroupMembership", related_name="users")

class MyUserGroupMembership(models.Model):
    user = models.ForeignKey(MyUser, on_delete=models.PROTECT, related_name="memberships")
    group = models.ForeignKey(MyGroup, on_delete=models.PROTECT, related_name="memberships")

And then you run code similar to

recipe = Recipe(MyUser, groups=(baker.make(MyGroup),))
recipe.make(_quantity=10, _bulk_create=True)

You will receive an exception like this:

  File "python3.12/site-packages/model_bakery/baker.py", line 131, in make
    return bulk_create(baker, _quantity, _save_kwargs=_save_kwargs, **attrs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "python3.12/site-packages/model_bakery/baker.py", line 840, in bulk_create
    through_model(
  File "python3.12/site-packages/django/db/models/base.py", line 567, in __init__
    raise TypeError(
TypeError: MyUserGroupMembership() got unexpected keyword arguments: 'users', 'mygroup'

Obviously these fields are not valid to create an instance of MyUserGroupMembership.

The offending code in bakery is in baker.bulk_create:

                through_model = getattr(entry, field.name).through
                through_model.objects.bulk_create(
                    [
                        through_model(
                            **{
                                field.remote_field.name: entry,
                                field.related_model._meta.model_name: obj,
                            }
                        )
                        for obj in kwargs[field.name]
                    ]
                )

You can see that the arguments used to create the through model are field.remote_field.name and field.related_model._meta.model_name. These are incorrect as shown above and need to be fixed.
Maybe through fields are best used for this (or pose an additional complication, not sure without further digging).

Expected behavior
Bakery can actually bulk create models that are using ManyToManyField with a through attribute.

Versions

  • Python: 3.12
  • Django 4.2
  • Model Bakery 1.18.0