일시적으로 auto_now / auto_now_add 비활성화
다음과 같은 모델이 있습니다.
class FooBar(models.Model):
createtime = models.DateTimeField(auto_now_add=True)
lastupdatetime = models.DateTimeField(auto_now=True)
일부 모델 인스턴스 (데이터 마이그레이션시 사용)에 대해 두 개의 날짜 필드를 덮어 쓰고 싶습니다. 현재 솔루션은 다음과 같습니다.
for field in new_entry._meta.local_fields:
if field.name == "lastupdatetime":
field.auto_now = False
elif field.name == "createtime":
field.auto_now_add = False
new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()
for field in new_entry._meta.local_fields:
if field.name == "lastupdatetime":
field.auto_now = True
elif field.name == "createtime":
field.auto_now_add = True
더 나은 해결책이 있습니까?
최근에 애플리케이션을 테스트하는 동안 이러한 상황에 직면했습니다. 만료 된 타임 스탬프를 "강제"해야했습니다. 제 경우에는 queryset 업데이트를 사용하여 트릭을 수행했습니다. 이렇게 :
# 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.
auto_now / auto_now_add를 이미 사용하는 것과 다른 방식으로 비활성화 할 수는 없습니다. 이러한 값을 변경할 수있는 유연성이 필요한 경우 auto_now
/ auto_now_add
는 최선의 선택이 아닙니다. 객체를 저장하기 직전에 조작을 수행하기 default
위해 save()
메서드 를 사용 및 / 또는 재정의하는 것이 더 유연합니다 .
default
및 재정의 된 save()
메서드를 사용 하여 문제를 해결하는 한 가지 방법은 다음과 같이 모델을 정의하는 것입니다.
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)
자동 lastupdatetime 변경을 건너 뛰려는 코드에서
new_entry.save(skip_lastupdatetime=True)
객체가 관리 인터페이스 또는 다른 위치에 저장되면 save ()가 skip_lastupdatetime 인수없이 호출되고 이전과 같이 auto_now
.
나는 질문자가 제시 한 제안을 사용하여 몇 가지 기능을 만들었습니다. 사용 사례는 다음과 같습니다.
turn_off_auto_now(FooBar, "lastupdatetime")
turn_off_auto_now_add(FooBar, "createtime")
new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()
구현은 다음과 같습니다.
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)
유사한 기능을 만들어 다시 켤 수 있습니다.
update_fields
매개 변수를 사용하고 필드를 save()
전달할 수도 있습니다 auto_now
. 예를 들면 다음과 같습니다.
# 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
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.
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.
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
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()
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.
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
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)
참고URL : https://stackoverflow.com/questions/7499767/temporarily-disable-auto-now-auto-now-add
'programing tip' 카테고리의 다른 글
LINQ Distinct 연산자, 대소 문자 무시? (0) | 2020.09.09 |
---|---|
aspx 페이지의 if 문 (0) | 2020.09.09 |
불법 배포로부터 소프트웨어를 어떻게 보호합니까? (0) | 2020.09.09 |
전략 패턴의 실제 사례 (0) | 2020.09.09 |
스프링 보안 표현 언어 주석에서 사용할 사용자 지정 메서드를 만드는 방법 (0) | 2020.09.09 |