Temporarily disable auto_now / auto_now_add

后端 未结 12 1188
刺人心
刺人心 2020-12-12 17:49

I have a model like this:

class FooBar(models.Model):
    createtime = models.DateTimeField(auto_now_add=True)
    lastupdatetime = models.DateTimeField(auto         


        
相关标签:
12条回答
  • 2020-12-12 18:11

    You can override auto_now_add without special code.

    I came across this question when I tried to create an object with particular date:

    Post.objects.create(publication_date=date, ...)
    

    where publication_date = models.DateField(auto_now_add=True).

    So this is what I've done:

    post = Post.objects.create(...)
    post.publication_date = date
    post.save()
    

    This has successfully overridden auto_now_add.

    As a more long-term solution, overriding save method is the way to go: https://code.djangoproject.com/ticket/16583

    0 讨论(0)
  • 2020-12-12 18:12

    I needed solution that will work with update_or_create, I've came to this solution based on @andreaspelme code.

    Only change is that You can set skipping by setting modified field to skip not only by actually passing kwarg skip_modified_update to save() method.

    Just yourmodelobject.modified='skip' and update will be skipped!

    from django.db import models
    from django.utils import timezone
    
    
    class TimeTrackableAbstractModel(models.Model):
        created = models.DateTimeField(default=timezone.now, db_index=True)
        modified = models.DateTimeField(default=timezone.now, db_index=True)
    
        class Meta:
            abstract = True
    
        def save(self, *args, **kwargs):
            skip_modified_update = kwargs.pop('skip_modified_update', False)
            if skip_modified_update or self.modified == 'skip':
                self.modified = models.F('modified')
            else:
                self.modified = timezone.now()
            super(TimeTrackableAbstractModel, self).save(*args, **kwargs)
    
    0 讨论(0)
  • 2020-12-12 18:14

    You can also use the update_fields parameter of save() and pass your auto_now fields. Here's an example:

    # Date you want to force
    new_created_date = date(year=2019, month=1, day=1)
    # The `created` field is `auto_now` in your model
    instance.created = new_created_date
    instance.save(update_fields=['created'])
    

    Here's the explanation from Django's documentation: https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save

    0 讨论(0)
  • 2020-12-12 18:15

    I'm late to the party, but similar to several of the other answers, this is a solution I used during a database migration. The difference from the other answers is that this disables all auto_now fields for the model under the assumption that there's really no reason to have more than one such field.

    def disable_auto_now_fields(*models):
        """Turns off the auto_now and auto_now_add attributes on a Model's fields,
        so that an instance of the Model can be saved with a custom value.
        """
        for model in models:
            for field in model._meta.local_fields:
                if hasattr(field, 'auto_now'):
                    field.auto_now = False
                if hasattr(field, 'auto_now_add'):
                    field.auto_now_add = False
    

    Then to use it, you can simply do:

    disable_auto_now_fields(Document, Event, ...)
    

    And it will go through and nuke all of your auto_now and auto_now_add fields for all of the model classes you pass in.

    0 讨论(0)
  • 2020-12-12 18:16

    I used the suggestion made by the asker, and created some functions. Here is the use case:

    turn_off_auto_now(FooBar, "lastupdatetime")
    turn_off_auto_now_add(FooBar, "createtime")
    
    new_entry.createtime = date
    new_entry.lastupdatetime = date
    new_entry.save()
    

    Here's the implementation:

    def turn_off_auto_now(ModelClass, field_name):
        def auto_now_off(field):
            field.auto_now = False
        do_to_model(ModelClass, field_name, auto_now_off)
    
    def turn_off_auto_now_add(ModelClass, field_name):
        def auto_now_add_off(field):
            field.auto_now_add = False
        do_to_model(ModelClass, field_name, auto_now_add_off)
    
    def do_to_model(ModelClass, field_name, func):
        field = ModelClass._meta.get_field_by_name(field_name)[0]
        func(field)
    

    Similar functions can be created to turn them back on.

    0 讨论(0)
  • 2020-12-12 18:16

    For those looking at this when they are writing tests, there is a python library called freezegun which allows you to fake the time - so when the auto_now_add code runs, it gets the time you actually want. So:

    from datetime import datetime, timedelta
    from freezegun import freeze_time
    
    with freeze_time('2016-10-10'):
        new_entry = FooBar.objects.create(...)
    with freeze_time('2016-10-17'):
        # use new_entry as you wish, as though it was created 7 days ago
    

    It can also be used as a decorator - see the link above for basic docs.

    0 讨论(0)
提交回复
热议问题