Code

Ticket #4102: 4102_Django1.3_JL20110610.patch

File 4102_Django1.3_JL20110610.patch, 6.5 KB (added by jlorieau, 3 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')