Migration: BooleanField to Charfield

Hello,

I’m writing a migration script to convert a field in my database from type BooleanField to CharField.

How can I find out what the value will be in the new version of the field after the migration has run?

Also, I will need to do a further update on the values in the new version of the field, setting the rows which were previously True to “01” and False to “02”.

There are roughly 250,000 records in the table.

Is there a more efficient way of doing the update than the following?
MyModel.objects.filter(field=<post-migration value of True>).update(field="01")

I’m using Postgres version 13 and Django version 3.2.25.

Thanks in advance for any assistance.

I would do this in multiple steps:

  • Add a new field with a different name, with null=True so that the database does not have to rewrite the full table in this step.
  • Add a data migration which does what you wrote, but with different fields (...filter(old=True).update(new="01")).
  • Drop the old field and maybe rename the new field to the old name after that. You may have to edit the migration by hand if Django doesn’t detect the rename.

Rewriting all of these records may cause the database to be unavailable for several seconds or maybe a bit longer. If that’s fine, you’re finished. If not, you have to investigate options to add the nullable field, modify the code to write both fields, but only read from the old field, and then only after some time has passed and all the data has been backfilled start reading from the new field. That’s a bit harder to do. I’d test how long the migration takes, and then decide if the time required is acceptable for a production environment.

Could you explain what you mean by this, please?

In what way would I have to edit the migration?
Why would Django not detect the rename?

The migrations autodetector doesn’t always ask if you renamed a field when you’re e.g. changing the field name and the verbose_name value of the field at the same time. The field definitions have to be “similar enough” to be picked up as a rename by Django.
It’s just the way the autodetector works right now. Maybe the rename detection etc. could be improved.

If the autodetector creates a RemoveField and an AddField operation you could replace those two with a RenameField operation yourself. The migration framework will still add another AlterField operation to also update the verbose_name, choices etc., but that’s fine.

I’m a n00b at migrations, so I don’t completely understand what you’re referring to here, sorry. Why would the autodetector not pick up on changes specified in a migrations file?

My scenario is that I need to change a BooleanField into a SingleCodeField.
My plan currently is to have the migration script contain an AlterField operation, specifying the new field’s type as SingleCodeField.
I’d then run a function which converted all previously True values (whatever they ended up being after the field was altered) to “01” and all previously False values to “00”.

Is this likely to work? I’m concerned about your comment that the script’s changes may not be identified.
It’s such a destructive procedure that I only have one attempt at running it in any database.

The autodetector doesn’t always detect the intent in modified models; so if you rename a field and at the same time you’re modifying other parameters of the field, it may not detect that you intended a rename, and will do a field removal and a field addition instead. As long as migrations haven’t been applied to the database, modifying them by hand is fine.

Of course the possibility of data loss always exists. I’d definitely try applying the migration in a staging or a local environment first, so that you can verify that it actually works. Experimenting in production is a really bad idea. If there’s no way for you to set up a different environment for testing, at least ensure that you have a current backup of the database and a functioning restore procedure. That’s really really important.

My plan currently is to have the migration script contain an AlterField operation, specifying the new field’s type as SingleCodeField.

To be honest, I don’t know what the database does when you change a boolean to a string. That’s why I suggested adding another field, migrating the data and then removing the old field in my first answer.