django - unique_together - Deshabilitar temporalmente auto_now/auto_now_add
forms datetime django (9)
Tengo un modelo como este:
class FooBar(models.Model):
createtime = models.DateTimeField(auto_now_add=True)
lastupdatetime = models.DateTimeField(auto_now=True)
Quiero sobrescribir los dos campos de fecha para algunas instancias de modelo (utilizadas al migrar datos). La solución actual se ve así:
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
¿Hay una mejor solución?
Fui el gestor de contexto para la reutilización.
@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'']
Úselo así:
with suppress_autotime(my_object, [''updated'']):
my_object.some_field = some_value
my_object.save()
Auge.
Llego tarde a la fiesta, pero, al igual que muchas de las otras respuestas, esta es una solución que utilicé durante la migración de una base de datos. La diferencia con las otras respuestas es que esto deshabilita todos los campos auto_now para el modelo bajo la suposición de que realmente no hay motivo para tener más de un campo.
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
Entonces, para usarlo, simplemente puede hacer:
disable_auto_now_fields(Document, Event, ...)
Y auto_now
y auto_now_add
todos auto_now_add
campos auto_now
y auto_now_add
para todas las clases de modelos que pases.
Necesitaba desactivar auto_now para un campo DateTime durante una migración y pude hacer esto.
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()
Para aquellos que lo están viendo cuando están escribiendo pruebas, hay una biblioteca de Python llamada freezegun que te permite simular el tiempo, de modo que cuando se auto_now_add
código auto_now_add
, obtiene el tiempo que realmente deseas. Asi que:
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
También se puede usar como decorador; consulte el enlace anterior para ver los documentos básicos.
Realmente no puedes deshabilitar auto_now / auto_now_add de otra manera que ya lo haces. Si necesita flexibilidad para cambiar esos valores, auto_now
/ auto_now_add
no es la mejor opción. A menudo es más flexible utilizar el default
y / o anular el método save()
para realizar la manipulación justo antes de que se guarde el objeto.
Usando default
método default
y el método save()
anulado, una forma de resolver su problema sería definir su modelo así:
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)
En su código, donde desea omitir el cambio automático de lastupdatetime, solo use
new_entry.save(skip_lastupdatetime=True)
Si su objeto se guarda en la interfaz de administración u otros lugares, se invocará a save () sin el argumento skip_lastupdatetime, y se comportará tal como lo hacía antes con auto_now
.
Recientemente me he enfrentado a esta situación al probar mi aplicación. Necesitaba "forzar" una marca de tiempo caducada. En mi caso, hice el truco al usar una actualización de queryset. Me gusta esto:
# 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.
También puede usar el parámetro update_fields
para save()
si sabe a qué campos desea limitar y excluir los campos auto_now / auto_now_add:
https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save
Usé la sugerencia hecha por el asker, y creé algunas funciones. Aquí está el caso de uso:
turn_off_auto_now(FooBar, "lastupdatetime")
turn_off_auto_now_add(FooBar, "createtime")
new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()
Aquí está la implementación:
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)
Se pueden crear funciones similares para volver a activarlas.
copia de Django - Models.DateTimeField - Cambiando dinámicamente el valor de auto_now_add
Bueno, pasé esta tarde averiguando y el primer problema es cómo obtener el objeto modelo y dónde está el código. Estoy en restframework en serializer.py, por ejemplo en __init__
del serializador, todavía no podría tener el modelo. Ahora en to_internal_value puede obtener la clase de modelo, después de obtener el campo y después de modificar las propiedades del campo como en este ejemplo:
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