Как перейти от M2M, созданного с помощью 'through', к M2M, управляемому Django

Я работаю в проекте, где было несколько полей, созданных как M2M с помощью through связанной модели, однако они были созданы неправильно, а связанные модели не имеют дополнительных полей, поэтому я хотел бы использовать обычный M2M, управляемый Django.

Существующие модели, которые у меня есть:

cass Student(models.Model):
    # ... other fields..."
    course = 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.course.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='course',
        ),
        # Add the new ManyToManyField
        migrations.AddField(
            model_name='student',
            name='course',
            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',
        ),
    ]

Вернуться на верх