Ticket #4102: 4102_Django1.3_JL20110610.patch

File 4102_Django1.3_JL20110610.patch, 6.5 KB (added by jlorieau, 4 years ago)

modified attrs save patch which deals with pre_save methods

  • django/db/models/base.py

    diff -rupN a//django/db/models/base.py b//django/db/models/base.py
    a b class Model(object): 
    275275    def __init__(self, *args, **kwargs):
    276276        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
    277277
     278        self._reset_modified_attrs()
     279
    278280        # Set up the storage for instance state
    279281        self._state = ModelState()
    280282
    281283        # There is a rather weird disparity here; if kwargs, it's set, then args
    282         # overrides it. It should be one or the other; don't duplicate the work
     284        # overrides it. It should be one or the other; don't duplicate the work.
    283285        # The reason for the kwargs check is that standard iterator passes in by
    284286        # args, and instantiation for iteration is 33% faster.
    285287        args_len = len(args)
    class Model(object): 
    365367        super(Model, self).__init__()
    366368        signals.post_init.send(sender=self.__class__, instance=self)
    367369
     370    def __setattr__(self, name, value):
     371        if name not in self.__dict__.setdefault('_modified_attrs', []):
     372            if not hasattr(self, name) or value != getattr(self, name):
     373                self._modified_attrs.append(name)
     374        super(Model, self).__setattr__(name, value)
     375
     376    def _reset_modified_attrs(self):
     377        self.__dict__['_modified_attrs'] = []
     378
    368379    def __repr__(self):
    369380        try:
    370381            u = unicode(self)
    class Model(object): 
    523534                    # It does already exist, so do an UPDATE.
    524535                    if force_update or non_pks:
    525536                        values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
     537
     538                        # Only keep modified_attrs and those whose value has changed after pre_save
     539                        if self._state.db == using:
     540                            modified_attrs = self._modified_attrs
     541                            values = [v for v in values
     542                                      if v[0].name in modified_attrs or v[0].attname in modified_attrs or
     543                                      v[2] != getattr(self, v[0].name)]
    526544                        rows = manager.using(using).filter(pk=pk_val)._update(values)
    527545                        if force_update and not rows:
    528546                            raise DatabaseError("Forced update did not affect any rows.")
    class Model(object): 
    559577                    setattr(self, meta.pk.attname, result)
    560578            transaction.commit_unless_managed(using=using)
    561579
     580            self._reset_modified_attrs()
     581
    562582        # Store the database on which the object was saved
    563583        self._state.db = using
    564584        # Once saved, this is no longer a to-be-added instance.
  • django/db/models/query.py

    diff -rupN a//django/db/models/query.py b//django/db/models/query.py
    a b class QuerySet(object): 
    284284                else:
    285285                    # Omit aggregates in object creation.
    286286                    obj = model(*row[index_start:aggregate_start])
     287                    # Models keep a track of modified attrs to choose which
     288                    # fields to save. Since we're just pulling from the
     289                    # database, nothing has changed yet.
     290                    obj._reset_modified_attrs()
    287291
    288292                # Store the source database of the object
    289293                obj._state.db = db
  • tests/regressiontests/update_fields/models.py

    diff -rupN a//tests/regressiontests/update_fields/models.py b//tests/regressiontests/update_fields/models.py
    a b  
     1"""
     2Save only modified fields on update
     3"""
     4from django.db import models
     5
     6class Article(models.Model):
     7    headline = models.CharField(max_length=100, default='Default headline')
     8    body = models.TextField()
     9    pub_date = models.DateTimeField()
     10    modify_date = models.DateTimeField(auto_now=True)
     11
     12    class Meta:
     13        ordering = ('pub_date','headline')
     14
     15    def __str__(self):
     16        return self.headline
  • tests/regressiontests/update_fields/tests.py

    diff -rupN a//tests/regressiontests/update_fields/tests.py b//tests/regressiontests/update_fields/tests.py
    a b  
     1from datetime import datetime
     2
     3from django.test import TestCase
     4
     5from models import Article
     6
     7
     8class UpdateFieldsTest(TestCase):
     9
     10    def test_basic_update_fields(self):
     11        # Create a couple of Articles.
     12        a = Article(headline=u'Article 1', body=u'Body 1', pub_date=datetime(2007, 5, 5))
     13        a.save()
     14
     15        # Change headline and body on different instances, then save both to
     16        # verify that each only updated the modified field.
     17        a1 = Article.objects.get(pk=a.id)
     18        a2 = Article.objects.get(pk=a.id)
     19        a1.headline = u'Changed article 1'
     20        a2.body = u'Changed body 1'
     21        a1.save()
     22        a2.save()
     23
     24        # The pre_save method should change the new modify_date of a1 and a2
     25        self.assertNotEqual(a1.modify_date, a2.modify_date)
     26       
     27        a3 = Article.objects.get(pk=a.id)
     28        self.assertEqual(a3.headline, u'Changed article 1')
     29        self.assertEqual(a3.body, u'Changed body 1')
     30        self.assertEqual(a3.modify_date, a2.modify_date)
     31
     32        # Fields entered at instantiation of a model which will already exists
     33        # should be saved as well.
     34        a = Article(id=a.id, headline=u'Reset article 1', body=u'Reset body 1', pub_date=datetime(2007, 5, 5))
     35        a.save()
     36        a3 = Article.objects.get(pk=a.id)
     37        self.assertEqual(a3.headline, u'Reset article 1')
     38        self.assertEqual(a3.body, u'Reset body 1')
     39
     40        # If the value doesn't change, it won't be marked as modified --
     41        # otherwise user input like forms/etc is going to mark everything
     42        # modified regardless.
     43        a1 = Article.objects.get(pk=a.id)
     44        a2 = Article.objects.get(pk=a.id)
     45        a1.headline = u'Changed article 2'
     46        a2.headline = a2.headline
     47        a2.body = u'Changed body 2'
     48        a1.save()
     49        a2.save()
     50        a3 = Article.objects.get(pk=a.id)
     51        self.assertEqual(a3.headline, u'Changed article 2')
     52        self.assertEqual(a3.body, u'Changed body 2')
Back to Top