class Article(models.Model):
title = models.IntegerField()
Then when you do case 1):
art = Article.objects.get(pk=1)
art.title = "New title"
art.save()
or case 2):
art = Article.objects.get(pk=1)
art.save()
Is there a way that the model, before saving, knows whether the title
(or any other field) has been reassigned? I need to know because for a
datefield I want to use now if no explicit date is given, otherwise
the given date.
mydate = models.DateField(null=True)
You can conditionally assign it if null:
import datetime
art = Article.objects.get(pk=1)
if not art.mydate:
art.mydate = datetime.date.today()
art.save()
--
Jeff Bauer
Rubicon, Inc.
There's no one-line method, but it's pretty easy to do: write a custom
save() method on the Article class that retrieves the instance again and
compares the fields. Something like
def save(self):
if self.id:
original = Article.objects.get(id=self.id)
# Compare fields you care about
super(Article, self).save()
The reason this can't really be done any more simply than that is that
the attributes on an Article instance are just normal Python attributes
-- there is nothing Field-like about them until you do the saving.
Python has no automatic way to tell if a class's attributes have changed
between two points in time.
Reagrds,
Malcolm
It just occurred to me that there's another way to do this if you're
going to need it fairly often...
Write your own __init__() method on the Article class and make sure you
call Model.__init__ (or super(Article, self).__init__) as the very first
thing. The Model __init__ will initialise your class by loading any data
into the attributes. Then your own __init__ code can copy those original
values to another attribute somewhere in the object. Inside your save()
method, you compare the attributes you are saving to the ones you copied
aside in the __init__ method to work out any differences.
Normally we don't need to write __init__ methods on models, so I wasn't
thinking along these lines. Whilst pushing the vacuum cleaner around the
living room just now I realised I'd missed something obvious.
Regards,
Malcolm
-----------------------------------
_magic_dt_create_counter = {}
class DateTimeFieldHelper(object):
def __init__(self, *args, **kwargs):
global _magic_dt_create_counter
#print "init me!"
self._magic_dt_fields = {}
super(DateTimeFieldHelper, self).__init__(*args, **kwargs)
c = str(self.__class__)
if c not in _magic_dt_create_counter:
#print "Enhanceing %s", c
_magic_dt_create_counter[c] = 1
opts = self._meta
for f in opts.fields:
if isinstance(f, DateTimeField):
setattr(DateTimeFieldHelper , f.attname,
property(partial(_get_magic_dt_field, dt_field=f.attname),
partial(_set_magic_dt_field, dt_field=f.attname)))
# when supplied via kwargs, set to 1..
opts = self._meta
for f in opts.fields:
if isinstance(f, DateTimeField):
if f.attname in kwargs:
self._magic_dt_fields[f.attname] =
(kwargs[f.attname] ,1)
def _get_magic_dt_field(self, dt_field):
#print "get field: %s" %dt_field
return self._magic_dt_fields[dt_field][0] if dt_field in
self._magic_dt_fields else None
def _set_magic_dt_field(self, val, *args, **kwargs):
dt_field = kwargs["dt_field"]
#print "set field: %s" %dt_field
if dt_field in self._magic_dt_fields:
count = self._magic_dt_fields[dt_field][1] + 1
#print "%s has been set %s times now to %s" %
(dt_field,count,val)
else:
count = 0
self._magic_dt_fields[dt_field] = [val , count]
-----------------------------------
This is my new DateTimeField:
-----------------------------------
class DateTimeField(DateTimeField):
def __init__(self, verbose_name=None, name=None, auto_now=False,
auto_now_add=False, auto_override=False, **kwargs):
self.auto_now, self.auto_now_add, self.auto_override =
auto_now, auto_now_add, auto_override
if (auto_now or auto_now_add) and not auto_override:
kwargs['editable'] = False
kwargs['blank'] = True
Field.__init__(self, verbose_name, name, **kwargs)
def pre_save(self, model_instance, add):
if (not self.auto_override) and (self.auto_now or
(self.auto_now_add and add)):
value = datetime.datetime.now()
elif self.auto_override and (self.auto_now):
# somehow set currenttime if no time has explicitly been
set
# the somehow has been solved... be scared, very
hackish
if self.attname in model_instance._magic_dt_fields and
model_instance._magic_dt_fields[self.attname][1] >= 1:
# value has been explicitly set
#print "Value for %s has been set %s time" %
(self.attname, model_instance._magic_dt_fields[self.attname][1])
value = super(DateField,
self).pre_save(model_instance, add)
else:
# no value is set, use now
value = datetime.datetime.now()
else:
value = super(DateField, self).pre_save(model_instance,
add)
setattr(model_instance, self.attname, value)
return value
-----------------------------------
It all is very hackish, simply forcing myself to set the correct date
on an update would have been simpler :)