简体   繁体   中英

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 ?

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.

If you would want to do it live, using the ORM, you could rename the Student.school field to Student.school_old , then add the Student.school ForeignKey, and use the following snippet to make sure the data is ok:

for student in Student.objects.filter(school__isnull=False):
    try:
        school = School.objects.get(name=student.school_old)
    except School.DoesNotExist:
        school = School.objects.create(name=student.school_old)
    student.school = school
    student.save()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM