Reputation: 2528
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 ?
Upvotes: 6
Views: 4313
Reputation: 20539
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.
Upvotes: 4
Reputation: 3120
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()
Upvotes: 2