I’ve got a recursive M2M field in my project’s AUTH_USER_MODEL (called “Users”). The through table includes several extra fields including an additional field which notes the user who created the entry in the through table. So the through table contains three fields which reference Users, two of which are through_fields and a third field which simply records some historical info. When I create a custom migration which calls apps.get_model on my model the through table doesn’t seem to work correctly. It seems to be ignoring the through_fields and using the third field as the second through_field.
I’ve reproduced the problem with a simpler example derived from the Django documentation for through_fields. Here’s the models straight from Django docs on the through_table:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through="Membership",
through_fields=("group", "person"),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
And here’s a sample migration I wrote which seems to show the problem. Note that the assertion fails in the migration, however if I run the same commands in the Django shell things work better.
from django.db import migrations
def forwards(apps, schema_editor):
Person = apps.get_model('groups', 'Person')
Group = apps.get_model('groups', 'Group')
Membership = apps.get_model('groups', 'Membership')
sally, _ = Person.objects.get_or_create(name="Sally Forth")
grp1, _ = Group.objects.get_or_create(name="Group 1")
admin, _ = Person.objects.get_or_create(name="Administrator")
Membership.objects.get_or_create(
group=grp1,
person=sally,
inviter=admin,
invite_reason="Initial setup via migration"
)
assert grp1.members.filter(name="Sally Forth").exists() # FAILS!!!
class Migration(migrations.Migration):
dependencies = [
('groups', '0001_initial'),
]
operations = [
migrations.RunPython(forwards),
]