I have some models with a ForeignKey field that points to another model:
class Specimen(models.Model):
...
class ResultA(models.Model):
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, ...)
...
class ResultB(models.Model):
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, ...)
...
At some point, I realized that each specimen will only ever have zero or one of each type of result, so it probably makes sense to turn that into a OneToOneField.
The plan is to:
- Create an additional
specimen_newfield that's aOneToOneFieldon each result - Create a data migration that will move the data from the
ForeignKeyfield to theOneToOneFieldfield - Delete the
ForeignKeyfield - Rename the new
specimen_newOneToOneFieldto justspecimen - Update all the code references to use the new
OneToOneFieldproperly
Step 1 was pretty simple:
class Specimen(models.Model):
...
class ResultA(models.Model):
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, ...)
specimen_new = models.OneToOneField(Specimen, on_delete=models.CASCADE, ...)
...
class ResultB(models.Model):
specimen = models.ForeignKey(Specimen, on_delete=models.CASCADE, ...)
specimen_new = models.OneToOneField(Specimen, on_delete=models.CASCADE, ...)
...
I'm stuck on step 2 - creating the data migration.
Right now, it looks like this:
from django.db import migrations
def migrate_results(apps, schema_editor):
# Get the model
Specimen = apps.get_model('ahs', 'Specimen')
# Update every specimen
for specimen in Specimen.objects.filter():
for field in Specimen._meta.get_fields():
# Look for fields that end with "_new"
if field.name.endswith("_new"):
old_field_name = field.name[:-4] # Without the suffix
new_field_name = field.name
# Try to get the result
result = getattr(specimen, old_field_name).first()
# If the field had a result, copy it to the new OneToOneField
if result:
setattr(specimen, new_field_name, result)
specimen.save()
class Migration(migrations.Migration):
dependencies = [
('ahs', '0009_auto_20220525_0827'),
]
operations = [
migrations.RunPython(migrate_results),
]
As far as I can tell, this should work, and it doesn't crash. Print statements in the migration (not shown above) appear to show that the data IS being copied. But once I run the migration, none of the data has been copied to the OneToOneField.
Is there anything I'm obviously doing wrong?
Edit: Turns out that the ForeignKey field, and the OneToOneField are similar enough that if you just change the fields in your code directly, and run ./manage.py migrate, Django will make the appropriate changes in the DB. That still doesn't explain why my data migration wasn't being saved.