Keeping track of the original values for a record...

7 views
Skip to first unread message

carole...@gmail.com

unread,
Aug 29, 2007, 1:18:34 PM8/29/07
to Django users
Since there isn't a load() method on a model... I'm thinking I somehow
have to do this on the manager..but not sure ... any tips would be
great.

When I load a record, either via get() or by looping through the
results of a .filter(), I'd like to keep track of the original
attribute values via setting a custom property on that model which
would contain a dict of the attribute/value at the time the record was
loaded ...

This is basically so that when I save, I can compare the current
values with those of the original to see which values have changed,
and which haven't, which I need to know for some event logs I'm
writing out at save time.

Thanks for any info.

Jure Čuhalev

unread,
Aug 29, 2007, 1:21:56 PM8/29/07
to django...@googlegroups.com

carole...@gmail.com

unread,
Aug 29, 2007, 1:36:16 PM8/29/07
to Django users
Thanks..but I'm already overriding save, and creating an event
record... I need to know how to do something extra when the data is
loaded initially..... I know I could query the record again into a new
object and use that for the original values..but that seems pretty
hackish ...so I'd prefer to try and save off the original values into
a dict when the record gets loaded...

On Aug 29, 1:21 pm, "Jure Čuhalev" <gandal...@gmail.com> wrote:

> http://www.djangoproject.com/documentation/db-api/#what-happens-when-...
>
> kr, jure

George Vilches

unread,
Aug 29, 2007, 1:48:46 PM8/29/07
to django...@googlegroups.com

But by the time pre_save occurs, all the values have been changed, and
you would have to perform another get() before you would have all the
original values. I don't think that's quite as good.

Something you could do is override the __set__ (or do the same via a
descriptor), and from there you could easily populate your dict with
original values. Once there's a set value for each key, then don't
populate the dict any more. Off the cuff code:

d = {}
def __set__(self, obj, val):
if obj not in d:
d[obj] = val
super(SomeClass, self).__set__(obj, val)

I have no idea if that works, but it's one possible starting point. If
you do manage to get this working, post your solution, I think this one
might be a good starting point for those people who want to do the
"change-only" query saves to the DB too. (I know that there's a ticket
somewhere in Trac that is related to that topic, but I'm too lazy to
look it up).

Good luck,
gav

Doug B

unread,
Aug 29, 2007, 2:24:23 PM8/29/07
to Django users
You could probably use the post_init signal to make a copy of the
model values for comparison in your save method. I'm doing something
similar to create a special manager object each time a certain model
instance is created.

Something like this...

def backup_model_data(sender, instance, signal, *args, **kwargs):
instance._original_data = instance.__dict__.copy()

class YourModel(model.Models):
...
dispatcher.connect(backup_model_data,signal=signals.post_init,
sender=YourModel)

carole...@gmail.com

unread,
Aug 29, 2007, 3:28:33 PM8/29/07
to Django users
I tried using the signals... but setting a new value on the record,
seems to also set it on the _original_data ...which is odd...maybe I'm
not seeing something stupid that I'm doing? If I change my
marketing_status_name data in my view and save, the new data is in
both the _original_data, and in the new property value.

# some extra properties removed to make it shorter to read
from django.db.models import signals
from django.dispatch import dispatcher
from django.db import models

def backup_model_data(sender, instance, signal, *args, **kwargs):
instance._original_data = instance.__dict__.copy()

class MarketingStatus(models.Model):
marketing_status_id = models.AutoField(primary_key=True)
marketing_status_name = models.CharField(blank=True, maxlength=30)
marketing_status_description = models.CharField(blank=True,
maxlength=255)

def save(self):
testing =
self._original_data['marketing_status_name']
testing2 = self.marketing_status_name

triggererror = madeupvariabletotriggererror #
just stuck this here so I can view the data in the browser

super(MarketingStatus, self).save()

class Meta:
db_table = 'marketing_status'

dispatcher.connect(backup_model_data,signal=signals.post_init,sender=MarketingStatus)

Michael

unread,
Aug 29, 2007, 3:46:01 PM8/29/07
to Django users
You can simply override the __init__ method and take a copy of the
values then:

See Malcolm's explanation here:
http://groups.google.com/group/django-users/msg/6d849eca95243371

-Mike

On Aug 30, 5:28 am, "carole.zie...@gmail.com"

carole...@gmail.com

unread,
Aug 29, 2007, 4:04:16 PM8/29/07
to Django users
Ahh..this appears to work! Thanks!

Doug B

unread,
Aug 29, 2007, 4:13:53 PM8/29/07
to Django users
Hmm. It works in shell for me, I'm not sure what the difference might
be. It only keeps _original_data for the life of the instance, so if
the view completes, the next view is a different instance. You could
make a pickle field to persist it, if you wanted to keep track of the
changed values.

In [2]: m=cm.MarketingStatus(marketing_status_name="Test Name",
marketing_status_description="Testing signals")
In [3]: m.save()
In [4]: m.marketing_status_name="Changed this"
In [5]: m.marketing_status_name
Out[5]: 'Changed this'
In [6]: m._original_data['marketing_status_name']
Out[6]: 'Test Name'

I added a couple print statements to print testing and testing 2 in
the save method:

In [2]: m=cm.MarketingStatus.objects.all()[0]
In [3]: m.marketing_status_name = "changed again"
In [4]: m.save()
testing: Test Name
testing2: changed again

----- using this code ----


def backup_model_data(sender, instance, signal, *args, **kwargs):
instance._original_data = instance.__dict__.copy()

class MarketingStatus(models.Model):
marketing_status_id = models.AutoField(primary_key=True)
marketing_status_name = models.CharField(blank=True, maxlength=30)
marketing_status_description =
models.CharField(blank=True,maxlength=255)

def save(self):
testing =self._original_data['marketing_status_name']
testing2 = self.marketing_status_name

print "testing: %s" % testing
print "testing2: %s" % testing2
super(MarketingStatus, self).save()

dispatcher.connect(backup_model_data,signal=signals.post_init,sender=MarketingStatus)


carole...@gmail.com

unread,
Aug 29, 2007, 4:34:21 PM8/29/07
to Django users
Maybe it's a bug in my branch..I'm using the multiple db branch... but
the other method Mike pointed out appears to work. Tomorrow I'm going
to tie it all together and have my save method only fire save if there
are dirty fields, and log what data has been changed into my event
table...

Thanks again everyone!

carole...@gmail.com

unread,
Aug 30, 2007, 9:59:47 AM8/30/07
to Django users
Blah... I could have sworn that worked when I tried it yesterday.....
but when I went to start working on it today..that method is returning
the changed values as well... very strange... I might have to resort
to calling the values from the DB again before doing a save :(

On Aug 29, 4:34 pm, "carole.zie...@gmail.com"

Reply all
Reply to author
Forward
0 new messages