如何从用'through'创建的 M2M 过渡到 Django 管理的 M2M



我在一个项目中工作,有多个字段使用through相关模型创建为M2M,但当时创建错误,相关模型没有额外的字段,所以我想使用Django管理的常规M2M。

我现有的型号有:

cass Student(models.Model):
# ... other fields..."
courses = models.ManyToManyField(
Course,
related_name='students',
through='StudentCourse'
)
class StudentCourse(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
cass Course(models.Model):
# ... other fields..."

我想要:

class Student(models.Model):
# ... other fields..."
course = models.ManyToManyField(Course, related_name='students')
class Course(models.Model):
# ... other fields..."

我无法在Django中找到一种方法来做到这一点而不丢失已经插入的数据。

我想以Django的方式重命名表,并操作Django迁移元数据,但这是一种丑陋的解决方法

Django有没有一种方法可以在不丢失数据(或创建备份表(的情况下解决这个问题?

要做到这一点,您需要创建一个迁移文件,将数据从旧的直通表复制到新的直通表。

首先创建一个空的迁移文件,该文件将填充我们的迁移操作

python manage.py makemigrations <app> --empty

然后,该迁移需要填充以下内容(<app>应替换为应用程序的应用程序名称(

from django.db import migrations, models

def move_courses(apps, schema_editor):
Student = apps.get_model('<app>', 'Student')
StudentCourse = apps.get_model('<app>', 'StudentCourse')
for student in Student.objects.all():
student.courses.set(
StudentCourse.objects.filter(student=student).values_list('course', flat=True)
)

class Migration(migrations.Migration):
dependencies = [
('<app>', '<XXXX_previous_migration_name>'),
]
operations = [
# Remove the old ManyToManyField
# This won't delete the through table or it's data
migrations.RemoveField(
model_name='Student',
name='courses',
),
# Add the new ManyToManyField
migrations.AddField(
model_name='Student',
name='courses',
field=models.ManyToManyField(related_name='students', to='<app>.Course'),
),
# Run a script that copies data from the old through table to the new one
migrations.RunPython(move_courses),
# Delete the old through table
migrations.DeleteModel(
name='StudentCourse',
),
]

然后以您喜欢的方式更新您的模型:

class Student(models.Model):
# ... other fields..."
courses = models.ManyToManyField(
Course,
related_name='students',
)
# Since through='StudentCourse' was removed, below is removed to (keeping it as comment to represent the removal)
# class StudentCourse(models.Model):
#    student = models.ForeignKey(Student, on_delete=models.CASCADE)
#    course = models.ForeignKey(Course, on_delete=models.CASCADE)
class Course(models.Model):
# ... other fields..."

更新模型和空创建的迁移后,可以运行migrate

python manage.py migrate

重要提示:不要再运行makemigrations,只运行migrate,以便Django运行移动数据的迁移。

最新更新