Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django new ForeignKey field based on existing field

I am trying to do the following but can't find a good solution on the documentation or on stackoverflow.

I have an existing database with ~1000 users with this model:

class Student(AbstractBaseUser):
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
    school = models.CharField(max_length=255,null=True, blank=True)
    ...

But I finally decided to use a separate model to handle schools :

class Student(AbstractBaseUser):
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
    school = models.ForeignKey('School')

class School(models.Model):
    name = models.CharField(max_length=255,unique=True)

How can I handle all the existing students, knowing that for the future students, I will be asking the school foreignkey directly in the suscription form ?

like image 510
Lucas B Avatar asked Aug 10 '15 07:08

Lucas B


1 Answers

Simplest solution wolud be to create auto migration, that will be something like this (listing only operations):

    operations = [
        migrations.CreateModel(
            name='School',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('name', models.CharField(unique=True, max_length=255)),
            ],
        ),
        migrations.AlterField(
            model_name='Student',
            name='school',
            field=models.ForeignKey(blank=True, to='testapp.School', null=True),
        ),
    ]

I've edited model Student, so now ForeignKey can be null, you can remove null value in AlterField on the end of migration if you're sure that every Student will have School assigned.

Now you should edit this migration by splitting AlterField into 3 separate operations (in sequence): RenameField (to rename existing school char field to for example school_name), AddField (to add ForeignKey to school model) and RemoveField (to remove old, unneeded char field).

Now, insert RunPython operation between AddField and RemoveField and inside that operation run code that will create new School objects (if there is no such object in database) and assign object to your Student.

In simple words: migration will create new model, named School, rename old field school to school_name, create new field school (ForeignKey to School) in model Student, iterate through all studens creating new School objects and assigning it into students and on last step, remove old school_name field (previously named school).

You can also provide reverse code into RunPython so you can reverse that migration without data loss.

like image 52
GwynBleidD Avatar answered Sep 20 '22 05:09

GwynBleidD