Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Convert CharField to TextField with data intact

Tags:

python

django

Is there a way to change a CharField to a TextField and keep the data from this column intact?

Right now I have the following:

class TestLog(models.Model):
    failed_reqs = models.CharField(max_length=DB_MAX_CHAR_LENGTH, blank=True)
    passed_reqs = models.CharField(max_length=DB_MAX_CHAR_LENGTH, blank=True)

But the DB_MAX_CHAR_LENGTH is 500, and as it turns out this field can sometimes exceed that, so I want to go to:

class TestLog(models.Model):
    failed_reqs = models.TextField(blank=True)
    passed_reqs = models.TextField(blank=True)

How do I do this and keep my data intact on the production database?

Djangobook details how to add and remove fields, and I've tried using South to do this as well, but South gave me an error, and looking at the output from before the error it appears as though South is dropping and recreating the DB. Is this what south's data migrations is all about?

like image 403
Nathan Avatar asked May 02 '11 22:05

Nathan


3 Answers

This is solved with django migrations (1.7+). If you change the type from CharField to TextField, the migration produced is AlterField, which does the right thing.

like image 191
Stephen Farrell Avatar answered Nov 19 '22 01:11

Stephen Farrell


Assuming that you have access to the database, as you mentioned the way Django talks about adding and removing columns, I've listed methods for both Postgresql and MySQL since you didn't mention what you were using.

Postgresql:
BEGIN;
    ALTER TABLE "TestLog"
        ALTER COLUMN "failed_reqs" TYPE TEXT,
        ALTER COLUMN "passed_reqs" TYPE TEXT;
COMMIT;

MySQL:
BEGIN;
    ALTER TABLE TestLog
        MODIFY failed_reqs TEXT NULL,
        MODIFY passed_reqs TEXT NULL;
COMMIT;

I would highly recommend backing up your database before doing these changes.

like image 5
Bryce Siedschlaw Avatar answered Nov 19 '22 03:11

Bryce Siedschlaw


Off hand, I would continue to look into a South approach. This is the workflow that I would try:

1) South schema migration to create two new TextField fields called something like "failed_reqs_txt" and "passed_reqs_txt".

2) Create a data migration to migrate the data from the old fields to the new fields

3) Create a schema migration to delete the original "failed_reqs" and "passed_reqs" fields.

---- if you need the fields to be the same name as the original, I would then proceed to:

4) Create a schema migration to add "failed_reqs", and "passed_reqs" as TextFields

5) Create a data migration to migrate from the "failed_reqs_txt" and "passed_reqs_txt" to the "failed_reqs", and "passed_reqs" fields.

6) Create a schema migration to drop the "failed_reqs_txt" and "passed_reqs_txt" fields.

Though this is a lot of migrations, it breaks up each change into atomic migrations. I'd try this first. I'm not sure why South would be dropping and recreating the db. Did you run the convert_to_south option when adding south to your project? I think this fakes a migration and lets south know that it's working with an existing project and not a new one.

As an alternative, you could do some direct ALTERS to the database to alter the column type and then update the model.py from CharField to TextField. Postgres, supposedly supports implicitly changing data types this way. (See Section 5.5.6.) I'm not sure about mysql, but I think it works the same. (CharField to TextFiled should be a compatible conversion)

Regardless, I'd backup my data before making any such changes. Hope this helps.

like image 5
Joe J Avatar answered Nov 19 '22 02:11

Joe J