django-durationfield icon indicating copy to clipboard operation
django-durationfield copied to clipboard

Django 1.8 supports DurationField natively, but there's no easy way to migrate

Open nhinkle opened this issue 9 years ago • 6 comments

It appears that as of 1.8, Django has a native DurationField model. I tried just replacing the fields on my model with the native Django version, but when I apply the migration it says:

column "the_column_with_the_durationfield" cannot be cast automatically to type interval
HINT:  Specify a USING expression to perform the conversion 

How can I go about migrating to the natively supported field? And what is the path forward for this project now that it's supported natively by Django?

nhinkle avatar Sep 18 '15 20:09 nhinkle

My personal view is that this project has served its stated purpose now that 2443 is closed. I intend to support it for the time being as Django 1.4 and 1.7 are still supported and it does still work with Django 1.8. However, providing instructions on how to migrated is on my todo list (I'm currently porting a project to 1.8, so may will be part of that, unless you care to do it first).

My first thought, without testing this is that you would actually need a multi step migration:

  1. Rename the django-durationfield to something else on the model
  2. Create a new native DurationField field on the model
  3. Copy the old integer-type microseconds into the new interval-type (unsure if it is going to require a conversion if done via ORM)
  4. Drop the renamed field

It may be possible to do some CAST or ALTER magic within the database or migration itself, but that may be backend-specific, since Postgres' implementation is a INTERVAL column, but I believe many other backends are just BIGINTs.

At the very least, I envision a how-to document on converting. If possible, a database-independent automated script or RunSQL would be welcome.

johnpaulett avatar Sep 22 '15 12:09 johnpaulett

I am just migrating to models.DurationField. One other difference to be aware of, in case someone wants to upgrade: Aggregations on this field returned an int value, where the native field from django returns an actual timedelta object, so changes in the codebase might be required.

ivome avatar May 05 '16 20:05 ivome

FYI, here's SQL for transforming the bigint to an interval on postgres when you're migrating :

ALTER TABLE <my table>
  ALTER COLUMN <my column> TYPE
    interval
    USING <my column> * interval '1 microsecond';

Thanks for the great code johnpaulett

Semprini avatar Sep 05 '16 09:09 Semprini

My personal view is that this project has served its stated purpose now that 2443 is closed. I intend to support it for the time being as Django 1.4 and 1.7 are still supported and it does still work with Django 1.8. However, providing instructions on how to migrated is on my todo list (I'm currently porting a project to 1.8, so may will be part of that, unless you care to do it first).

My first thought, without testing this is that you would actually need a multi step migration:

  1. Rename the django-durationfield to something else on the model
  2. Create a new native DurationField field on the model
  3. Copy the old integer-type microseconds into the new interval-type (unsure if it is going to require a conversion if done via ORM)
  4. Drop the renamed field

It may be possible to do some CAST or ALTER magic within the database or migration itself, but that may be backend-specific, since Postgres' implementation is a INTERVAL column, but I believe many other backends are just BIGINTs.

At the very least, I envision a how-to document on converting. If possible, a database-independent automated script or RunSQL would be welcome. @johnpaulett I followed below setps to change datatype in migration file

  1. Renamed duration to old_duration field which has int8 datatype in postgres
  2. Create new duration field with data type models.DurationField which has interval datatype in postgres
operations = [
       migrations.RenameField(
            model_name='student',
            old_name='duration',
            new_name='old_duration',
        ),
        migrations.RenameField(
            model_name='teacher',
            old_name='duration',
            new_name='old_duration',        
        ),
        migrations.AddField(
            model_name='student',
            name='duration',
            field=models.DurationField(blank=True, editable=False, null=True),
        ),
        migrations.AddField(
            model_name='teacher',
            name='duration',
            field=models.DurationField(blank=True, editable=False, null=True),
        ),
        migrations.RunSQL(
            "UPDATE student SET duration = CAST(old_duration AS INTERVAL);",
            "UPDATE teacher SET duration = CAST(old_duration AS INTERVAL);"
        )
    ]

While I run cmd python manage.py migrate error is django.db.utils.ProgrammingError: cannot cast type bigint to interval LINE 1: ...R COLUMN "duration" TYPE interval USING "duration"::interval

IshwarChincholkar avatar Jun 16 '22 12:06 IshwarChincholkar

@IshwarChincholkar I don't think you can just CAST -- possibly use make_interval or use @Semprini's suggestion of multiplying times, similar to 20 * '1 minute'::interval;

johnpaulett avatar Jun 16 '22 15:06 johnpaulett

@johnpaulett Thanks it worked

IshwarChincholkar avatar Jun 17 '22 12:06 IshwarChincholkar