johnpaulett / django-durationfield

Temporary reusable Django application for adding a DurationField, until Django issue #2443 is resolved.
http://django-durationfield.readthedocs.org
67 stars 24 forks source link

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

Open nhinkle opened 9 years ago

nhinkle commented 9 years ago

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?

johnpaulett commented 9 years ago

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.

ivome commented 8 years ago

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.

Semprini commented 8 years ago

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

IshwarChincholkar commented 2 years ago

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

johnpaulett commented 2 years ago

@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;

IshwarChincholkar commented 2 years ago

@johnpaulett Thanks it worked