8

I have a model that looks like this:

class Assignment(models.Model):
    """An assignment covers a range of years, 
    has multiple coders and one specific task"""
    title = models.CharField(blank=True, max_length=100)
    start_date = models.DateField(default=date.today)
    end_date = models.DateField(default=date.today)
    coders = models.ManyToManyField(User, related_name='assignments')

I want to change this model (which has data already in production) so that it looks like this:

class Assignment(models.Model):
    """An assignment covers a range of years, 
    has multiple coders and one specific task"""
    title = models.CharField(blank=True, max_length=100)
    start_date = models.DateField(default=date.today)
    end_date = models.DateField(default=date.today)
    coders = models.ManyToManyField(User, through = 'AssignmentProgress')

class AssignmentProgress(models.Model):
    """The progress a particular coder has made with an assignment"""
    assignment = models.ForeignKey(Assignment, related_name="assignment_progress")
    coder = models.ForeignKey(User, related_name="assignment_progress")
    articles_coded = models.IntegerField(default=0, null=False)
    progress = models.FloatField(default=0, null=False)

From what I have read so far, the solution would be to add a field assignment_coders = models.ManyToManyField(User, through = 'AssignmentProgress') to Assignment and then use a datamigration to copy the info over.

I tried that with the following data migration:

# -*- coding: utf-8 -*-
# Generated by Django 1.9.1 on 2016-11-09 16:31
from __future__ import unicode_literals

from django.db import migrations

def move_coders(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Assignment = apps.get_model("coding", "Assignment")
    for a in Assignment.objects.all():
        a.assignment_coders = a.coders
        a.save()


class Migration(migrations.Migration):

    dependencies = [
        ('coding', '0006_auto_20161109_1730'),
    ]

    operations = [
        migrations.RunPython(move_coders),
    ]

But I get the following error when I try to run that migration: Cannot set values on a ManyToManyField which specifies an intermediary model.

How can I migrate Assignment.coders from a ManyToMany field to one with through= without losing data?

LukasKawerau
  • 1,071
  • 2
  • 23
  • 42
  • 1
    http://stackoverflow.com/questions/2224410/django-data-migration-when-changing-a-field-to-manytomany?rq=1 This would be helpful to you – Mitesh Prajapati Nov 09 '16 at 17:44
  • When i try the second step, the custom migration, it again gives me an error, but slightly different: `Cannot use add() on a ManyToManyField which specifies an intermediary model` It also tells me to `Use AssignmentProgress's Manager instead` which I have no clue what that means in this context or how I would do that. – LukasKawerau Nov 09 '16 at 17:53
  • 1
    @LukasKawerau you should do what Django tells you: explicitly create `AssignmentProgress` instances, like `AssignmentProgress.objects.create(assignment=some_assigment, coder=some_user)`. "Through" model has some additional fields, as opposed to default many to many relationship. Django does not know what to do with them, so it asks you to deal with them yourself. – Назар Топольський Nov 10 '16 at 00:15
  • 2
    Possible duplicate of [Migrating data from "Many-To-Many" to "Many-To-Many Through" in django](https://stackoverflow.com/questions/11466358/migrating-data-from-many-to-many-to-many-to-many-through-in-django) – supervacuo May 11 '18 at 01:37

0 Answers0