#36061: Custom data migration for M2M with through_fields not working
-------------------------------------+-------------------------------------
Reporter: Brian Nettleton | Type: Bug
Status: new | Component:
| Migrations
Version: 4.2 | Severity: Normal
Keywords: through_fields | Triage Stage:
migration | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
When writing a data migration for a model with a field which uses through
fields the through_fields are not honored in the model retrieved from the
migration's app registry.
Here is an example of such a data migration using the models from the
Django documentation for through_fields. This data migration has an
assert at the end of the forwards function which fails. Note that issuing
the similar instructions in a Django shell works fine, the issue is
specific to retrieving a model in a migration using apps.get_model. The
models used and instructions for recreating the problem are also included
below.
{{{
# Generated by Django 4.2.17 on 2025-01-02 19:35
from django.db import migrations
def forwards(apps, schema_editor):
Person = apps.get_model('groups', 'Person')
Group = apps.get_model('groups', 'Group')
Group._meta.local_many_to_many[0].remote_field.through_field =
("group", "person")
Membership = apps.get_model('groups', 'Membership')
# Initialize some data in the database
sally, _ = Person.objects.get_or_create(name="Sally Forth")
steve, _ = Person.objects.get_or_create(name="Steve Smith")
alice, _ = Person.objects.get_or_create(name="Alice Adams")
grp1, _ = Group.objects.get_or_create(name="Group 1")
grp2, _ = Group.objects.get_or_create(name="Group 2")
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"
)
Membership.objects.get_or_create(
group=grp1,
person=steve,
inviter=admin,
invite_reason="Initial setup via migration"
)
Membership.objects.get_or_create(
group=grp1,
person=alice,
inviter=admin,
invite_reason="Initial setup via migration"
)
# Okay, now also put everyone whose name starts with an "S" and is in
Group 1 into Group 2
for s_member in grp1.members.filter(name__startswith="S"):
Membership.objects.get_or_create(
group=grp2,
person=s_member,
inviter=admin,
invite_reason="Initial setup 2: Put the initial 'S' people
from Group 1 into Group 2"
)
print(f"\n{grp2.members.count()=}\n")
assert grp2.members.count() == 2 # ===== FAILS =====
class Migration(migrations.Migration):
dependencies = [
('groups', '0001_initial'),
]
operations = [
migrations.RunPython(forwards),
]
}}}
The models used for this migration are as follows.
{{{
from django.db import models
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)
}}}
Steps to reproduce:
1. Create a new empty project
2. Create a new empty app called groups and add to INSTALLED_APPS in
project settings.py
3. Add models above to groups/models.py
4. Make initial migrations
5. Run initial migrations
6. Create empty migration for groups app
7. Put migration above into empty migration file
8. Run migrate to reproduce the problem
--
Ticket URL: <https://code.djangoproject.com/ticket/36061>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/010701942e2c9572-15d94838-a2ab-449d-9e53-b758feffad7b-000000%40eu-central-1.amazonses.com.