Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add 'auto_now' DateTimeField to existing Django model

Is it possible to add to existing model auto_now and auto_now_add DateTime fields?

class ExistingModel(models.Model):
    # ... model stuff
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

We can't add this fields without default, but adding the default value gives an error:

./manage.py makemigrations

returns:

(fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present.
like image 326
Alex T Avatar asked Feb 16 '18 13:02

Alex T


1 Answers

You can make that happen in 2 separate migrations.

First, add a default=<some datetime> to both of your new datetime fields and create that migration.

Then remove the default and add auto_now parameters and create the 2nd migration.

Update -

It's possible to have both of these combined into a single migration (preserving the order) by generating the 2 migrations and simply cut-paste the 2nd migrations' content into the first (and then delete the 2nd migration file).

example migration file 00XY_existing_model_add_timestamps -

from django.db import migrations, models
from django.utils import timezone


def update_datetimes(app, schema_editor):
    print()  # new line
    ExistingModel = app.get_model("my_app", "ExistingModel")
    for instance in ExistingModel.objects.all():
        instance.created_at = # calculate & set something based on the instance attributes
        instance.updated_at = # calculate & set something based on the instance attributes
    print("\tUpdated created_at & updated_at for all rows in ExistingModel")


class Migration(migrations.Migration):

    dependencies = [("my_app", "00XX_previous_migration")]

    operations = [
        migrations.AddField(
            model_name="ExistingModel",
            name="created_at",  # name of your new field
            field=models.DateTimeField(default=timezone.now),  # replace timezone.now with any datetime value you want 
            preserve_default=False,
        ),
        migrations.AddField(
            model_name="ExistingModel",
            name="updated_at",  # name of your new field
            field=models.DateTimeField(default=timezone.now),  # replace timezone.now with any datetime value you want 
            preserve_default=False,
        ),
        # uncomment the below migration operation if you want to dynamically set old datetimes
        # migrations.RunPython(
        #     update_datetimes, reverse_code=migrations.RunPython.noop
        # ),
        migrations.AlterField(
            model_name="ExistingModel",
            name="created_at",
            field=models.DateTimeField(auto_now_add=True),
        ),
        migrations.AlterField(
            model_name="ExistingModel",
            name="updated_at",
            field=models.DateTimeField(auto_now=True),
        ),
    ]
like image 129
shad0w_wa1k3r Avatar answered Nov 14 '22 23:11

shad0w_wa1k3r