Code

Ticket #4102: 4102-dirty-set.patch

File 4102-dirty-set.patch, 9.5 KB (added by Collin Grady <cgrady@…>, 7 years ago)

converted patch to use set on suggestion of SmileyChris, also found another bug when you used a kwargs of a field name instead of attname

  • django/db/models/base.py

     
    1919import sys 
    2020import os 
    2121 
     22try: 
     23    set 
     24except NameError: 
     25    from sets import Set as set   # Python 2.3 fallback 
     26 
    2227class ModelBase(type): 
    2328    "Metaclass for all models" 
    2429    def __new__(cls, name, bases, attrs): 
     
    97102    def __ne__(self, other): 
    98103        return not self.__eq__(other) 
    99104 
     105    def __setattr__(self, name, value): 
     106        if hasattr(self, '_dirty') and name != '_dirty' and (not hasattr(self, name) or value != getattr(self, name)): 
     107            self._dirty.add(name) 
     108        super(Model, self).__setattr__(name, value) 
     109 
     110    def _wash(self): 
     111        self._dirty = set() 
     112 
    100113    def __init__(self, *args, **kwargs): 
    101114        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    102115 
     116        wash = kwargs.pop('wash', False) 
     117        dirty = set() 
     118 
    103119        # There is a rather weird disparity here; if kwargs, it's set, then args 
    104         # overrides it. It should be one or the other; don't duplicate the work 
     120        # overrides it. It should be one or the other; don't duplicate the work. 
    105121        # The reason for the kwargs check is that standard iterator passes in by 
    106         # args, and nstantiation for iteration is 33% faster. 
     122        # args, and instantiation for iteration is 33% faster. 
    107123        args_len = len(args) 
    108124        if args_len > len(self._meta.fields): 
    109125            # Daft, but matches old exception sans the err msg. 
     
    117133            # without changing the logic. 
    118134            for val, field in izip(args, fields_iter): 
    119135                setattr(self, field.attname, val) 
     136                print field.attname, val 
     137                dirty.add(field.attname) 
    120138        else: 
    121139            # Slower, kwargs-ready version. 
    122140            for val, field in izip(args, fields_iter): 
    123141                setattr(self, field.attname, val) 
     142                print field.attname, val 
     143                dirty.add(field.attname) 
    124144                kwargs.pop(field.name, None) 
    125145                # Maintain compatibility with existing calls. 
    126146                if isinstance(field.rel, ManyToOneRel): 
     
    130150        # keywords, or default. 
    131151 
    132152        for field in fields_iter: 
     153            if field.attname in kwargs or field.name in kwargs: 
     154                dirty.add(field.attname) 
    133155            if kwargs: 
    134156                if isinstance(field.rel, ManyToOneRel): 
    135157                    try: 
     
    167189                    pass 
    168190            if kwargs: 
    169191                raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] 
     192 
     193        self._wash() 
     194        if not wash: 
     195            self._dirty = dirty 
    170196        dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) 
    171197 
    172198    def add_to_class(cls, name, value): 
     
    202228    _prepare = classmethod(_prepare) 
    203229 
    204230    def save(self, raw=False): 
     231        "Save model object to database, creating if doesn't exist, otherwise updating" 
    205232        dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self) 
    206233 
    207         non_pks = [f for f in self._meta.fields if not f.primary_key] 
     234        non_pks = [f for f in self._meta.fields if not f.primary_key and (f.name in self._dirty or f.attname in self._dirty)] 
     235        self._wash() 
    208236        cursor = connection.cursor() 
    209237 
    210238        # First, try an UPDATE. If that doesn't update anything, do an INSERT. 
  • django/db/models/query.py

     
    202202                    obj, index_end = get_cached_row(klass=self.model, row=row, 
    203203                                                    index_start=0, max_depth=self._max_related_depth) 
    204204                else: 
    205                     obj = self.model(*row[:index_end]) 
     205                    obj = self.model(wash=True, *row[:index_end]) 
    206206                for i, k in enumerate(extra_select): 
    207207                    setattr(obj, k[0], row[index_end+i]) 
    208208                yield obj 
  • tests/modeltests/update_fields/models.py

     
     1""" 
     2#. Save only modified fields on update 
     3""" 
     4from django.db import models 
     5 
     6class Article(models.Model): 
     7    headline = models.CharField(maxlength=100, default='Default headline') 
     8    body = models.TextField() 
     9    pub_date = models.DateTimeField() 
     10 
     11    class Meta: 
     12        ordering = ('pub_date','headline') 
     13 
     14    def __str__(self): 
     15        return self.headline 
     16 
     17__test__ = {'API_TESTS':""" 
     18# Create a couple of Articles. 
     19>>> from datetime import datetime 
     20>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5)) 
     21>>> a.save() 
     22 
     23# Change headline and body on different instances, then save both to verify 
     24# that each only updated the modified field 
     25>>> a1 = Article.objects.get(pk=a.id) 
     26>>> a2 = Article.objects.get(pk=a.id) 
     27>>> a1.headline = 'Changed article 1' 
     28>>> a2.body = 'Changed body 1' 
     29>>> a1.save() 
     30>>> a2.save() 
     31>>> a3 = Article.objects.get(pk=a.id) 
     32>>> a3.headline 
     33'Changed article 1' 
     34>>> a3.body 
     35'Changed body 1' 
     36"""} 
     37""" 
     38#. Save only modified fields on update 
     39""" 
     40from django.db import models 
     41 
     42class Article(models.Model): 
     43    headline = models.CharField(maxlength=100, default='Default headline') 
     44    body = models.TextField() 
     45    pub_date = models.DateTimeField() 
     46 
     47    class Meta: 
     48        ordering = ('pub_date','headline') 
     49 
     50    def __str__(self): 
     51        return self.headline 
     52 
     53__test__ = {'API_TESTS':""" 
     54# Create a couple of Articles. 
     55>>> from datetime import datetime 
     56>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5)) 
     57>>> a.save() 
     58 
     59# Change headline and body on different instances, then save both to verify 
     60# that each only updated the modified field 
     61>>> a1 = Article.objects.get(pk=a.id) 
     62>>> a2 = Article.objects.get(pk=a.id) 
     63>>> a1.headline = 'Changed article 1' 
     64>>> a2.body = 'Changed body 1' 
     65>>> a1.save() 
     66>>> a2.save() 
     67>>> a3 = Article.objects.get(pk=a.id) 
     68>>> a3.headline 
     69'Changed article 1' 
     70>>> a3.body 
     71'Changed body 1' 
     72"""} 
     73""" 
     74#. Save only modified fields on update 
     75""" 
     76from django.db import models 
     77 
     78class Article(models.Model): 
     79    headline = models.CharField(maxlength=100, default='Default headline') 
     80    body = models.TextField() 
     81    pub_date = models.DateTimeField() 
     82 
     83    class Meta: 
     84        ordering = ('pub_date','headline') 
     85 
     86    def __str__(self): 
     87        return self.headline 
     88 
     89__test__ = {'API_TESTS':""" 
     90# Create a couple of Articles. 
     91>>> from datetime import datetime 
     92>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5)) 
     93>>> a.save() 
     94 
     95# Change headline and body on different instances, then save both to verify 
     96# that each only updated the modified field 
     97>>> a1 = Article.objects.get(pk=a.id) 
     98>>> a2 = Article.objects.get(pk=a.id) 
     99>>> a1.headline = 'Changed article 1' 
     100>>> a2.body = 'Changed body 1' 
     101>>> a1.save() 
     102>>> a2.save() 
     103>>> a3 = Article.objects.get(pk=a.id) 
     104>>> a3.headline 
     105'Changed article 1' 
     106>>> a3.body 
     107'Changed body 1' 
     108"""} 
     109""" 
     110#. Save only modified fields on update 
     111""" 
     112from django.db import models 
     113 
     114class Article(models.Model): 
     115    headline = models.CharField(maxlength=100, default='Default headline') 
     116    body = models.TextField() 
     117    pub_date = models.DateTimeField() 
     118 
     119    class Meta: 
     120        ordering = ('pub_date','headline') 
     121 
     122    def __str__(self): 
     123        return self.headline 
     124 
     125__test__ = {'API_TESTS':""" 
     126# Create a couple of Articles. 
     127>>> from datetime import datetime 
     128>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5)) 
     129>>> a.save() 
     130 
     131# Change headline and body on different instances, then save both to verify 
     132# that each only updated the modified field 
     133>>> a1 = Article.objects.get(pk=a.id) 
     134>>> a2 = Article.objects.get(pk=a.id) 
     135>>> a1.headline = 'Changed article 1' 
     136>>> a2.body = 'Changed body 1' 
     137>>> a1.save() 
     138>>> a2.save() 
     139>>> a3 = Article.objects.get(pk=a.id) 
     140>>> a3.headline 
     141'Changed article 1' 
     142>>> a3.body 
     143'Changed body 1' 
     144"""} 
     145""" 
     146#. Save only modified fields on update 
     147""" 
     148from django.db import models 
     149 
     150class Article(models.Model): 
     151    headline = models.CharField(maxlength=100, default='Default headline') 
     152    body = models.TextField() 
     153    pub_date = models.DateTimeField() 
     154 
     155    class Meta: 
     156        ordering = ('pub_date','headline') 
     157 
     158    def __str__(self): 
     159        return self.headline 
     160 
     161__test__ = {'API_TESTS':""" 
     162# Create a couple of Articles. 
     163>>> from datetime import datetime 
     164>>> a = Article(headline='Article 1', body='Body 1', pub_date=datetime(2007, 5, 5)) 
     165>>> a.save() 
     166 
     167# Change headline and body on different instances, then save both to verify 
     168# that each only updated the modified field 
     169>>> a1 = Article.objects.get(pk=a.id) 
     170>>> a2 = Article.objects.get(pk=a.id) 
     171>>> a1.headline = 'Changed article 1' 
     172>>> a2.body = 'Changed body 1' 
     173>>> a1.save() 
     174>>> a2.save() 
     175>>> a3 = Article.objects.get(pk=a.id) 
     176>>> a3.headline 
     177'Changed article 1' 
     178>>> a3.body 
     179'Changed body 1' 
     180"""}