Temporarily disable auto_now / auto_now_add

后端 未结 12 1189
刺人心
刺人心 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:25

    You can't really disable auto_now/auto_now_add in another way than you already do. If you need the flexibility to change those values, auto_now/auto_now_add is not best choice. It is often more flexible to use default and/or override the save() method to do manipulation right before the object is saved.

    Using default and an overridden save() method, one way to solve your problem would be to define your model like this:

    class FooBar(models.Model):
        createtime = models.DateTimeField(default=datetime.datetime.now)
        lastupdatetime = models.DateTimeField()
    
        def save(self, *args, **kwargs):
            if not kwargs.pop('skip_lastupdatetime', False):
                self.lastupdatetime = datetime.datetime.now()
    
            super(FooBar, self).save(*args, **kwargs)
    

    In your code, where you want to skip the automatic lastupdatetime change, just use

    new_entry.save(skip_lastupdatetime=True)
    

    If your object is saved in the admin interface or other places, save() will be called without the skip_lastupdatetime argument, and it will behave just as it did before with auto_now.

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

    I went the context manager way for reusability.

    @contextlib.contextmanager
    def suppress_autotime(model, fields):
        _original_values = {}
        for field in model._meta.local_fields:
            if field.name in fields:
                _original_values[field.name] = {
                    'auto_now': field.auto_now,
                    'auto_now_add': field.auto_now_add,
                }
                field.auto_now = False
                field.auto_now_add = False
        try:
            yield
        finally:
            for field in model._meta.local_fields:
                if field.name in fields:
                    field.auto_now = _original_values[field.name]['auto_now']
                    field.auto_now_add = _original_values[field.name]['auto_now_add']
    

    Use like so:

    with suppress_autotime(my_object, ['updated']):
        my_object.some_field = some_value
        my_object.save()
    

    Boom.

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

    I needed to disable auto_now for a DateTime field during a migration and was able to do this.

    events = Events.objects.all()
    for event in events:
        for field in event._meta.fields:
            if field.name == 'created_date':
                field.auto_now = False
        event.save()
    
    0 讨论(0)
  • 2020-12-12 18:34

    I've recently faced this situation while testing my application. I needed to "force" an expired timestamp. In my case I did the trick by using a queryset update. Like this:

    # my model
    class FooBar(models.Model):
        title = models.CharField(max_length=255)
        updated_at = models.DateTimeField(auto_now=True, auto_now_add=True)
    
    
    
    # my tests
    foo = FooBar.objects.get(pk=1)
    
    # force a timestamp
    lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
    FooBar.objects.filter(pk=foo.pk).update(updated_at=lastweek)
    
    # do the testing.
    
    0 讨论(0)
  • 2020-12-12 18:34

    A bit more clean version of context manager from https://stackoverflow.com/a/35943149/1731460

    from contextlib import contextmanager
    
    @contextmanager
    def suppress_auto_now(model, field_names):
        """
        idea taken here https://stackoverflow.com/a/35943149/1731460
        """
        fields_state = {}
        for field_name in field_names:
            field = model._meta.get_field(field_name)
            fields_state[field] = {'auto_now': field.auto_now, 'auto_now_add': field.auto_now_add}
    
        for field in fields_state:
            field.auto_now = False
            field.auto_now_add = False
        try:
            yield
        finally:
            for field, state in fields_state.items():
                field.auto_now = state['auto_now']
                field.auto_now_add = state['auto_now_add']
    

    You can use it even with Factories (factory-boy)

            with suppress_autotime(Click, ['created']):
                ClickFactory.bulk_create(post=obj.post, link=obj.link, created__iter=created)
    
    0 讨论(0)
  • 2020-12-12 18:36

    copy of Django - Models.DateTimeField - Changing dynamically auto_now_add value

    Well , I spent this afternoon find out and the first problem is how fetch model object and where in code . I'm in restframework in serializer.py , for example in __init__ of serializer it could not have the Model yet . Now in to_internal_value you can get the model class , after get the Field and after modify the field properties like in this example :

    class ProblemSerializer(serializers.ModelSerializer):
    
        def to_internal_value(self, data): 
            ModelClass = self.Meta.model
            dfil = ModelClass._meta.get_field('date_update')
            dfil.auto_now = False
            dfil.editable = True
    
    0 讨论(0)
提交回复
热议问题