Django

Code

Ticket #4102: 4102-dirty-set.4.patch

File 4102-dirty-set.4.patch, 5.4 kB (added by Collin Grady <cgrady@the-magi.us>, 1 year ago)

sigh, patch hates me - fixed duplicate test issue

  • django/db/models/base.py

    old new  
    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, '_modified_fields') and name != '_modified_fields' and (not hasattr(self, name) or value != getattr(self, name)): 
     107            self._modified_fields.add(name) 
     108        super(Model, self).__setattr__(name, value) 
     109 
     110    def reset_modified_fields(self): 
     111        self._modified_fields = set() 
     112 
    100113    def __init__(self, *args, **kwargs): 
    101114        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    102115 
     116        modified_fields = set() 
     117 
    103118        # 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 
     119        # overrides it. It should be one or the other; don't duplicate the work. 
    105120        # The reason for the kwargs check is that standard iterator passes in by 
    106         # args, and nstantiation for iteration is 33% faster. 
     121        # args, and instantiation for iteration is 33% faster. 
    107122        args_len = len(args) 
    108123        if args_len > len(self._meta.fields): 
    109124            # Daft, but matches old exception sans the err msg. 
     
    117132            # without changing the logic. 
    118133            for val, field in izip(args, fields_iter): 
    119134                setattr(self, field.attname, val) 
     135                modified_fields.add(field.attname) 
    120136        else: 
    121137            # Slower, kwargs-ready version. 
    122138            for val, field in izip(args, fields_iter): 
    123139                setattr(self, field.attname, val) 
     140                modified_fields.add(field.attname) 
    124141                kwargs.pop(field.name, None) 
    125142                # Maintain compatibility with existing calls. 
    126143                if isinstance(field.rel, ManyToOneRel): 
     
    130147        # keywords, or default. 
    131148 
    132149        for field in fields_iter: 
     150            if field.attname in kwargs or field.name in kwargs: 
     151                modified_fields.add(field.attname) 
    133152            if kwargs: 
    134153                if isinstance(field.rel, ManyToOneRel): 
    135154                    try: 
     
    167186                    pass 
    168187            if kwargs: 
    169188                raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] 
     189 
     190        self.reset_modified_fields() 
     191        self._modified_fields = modified_fields 
    170192        dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) 
    171193 
    172194    def add_to_class(cls, name, value): 
     
    202224    _prepare = classmethod(_prepare) 
    203225 
    204226    def save(self, raw=False): 
     227        "Save model object to database, creating if doesn't exist, otherwise updating" 
    205228        dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self) 
    206229 
    207         non_pks = [f for f in self._meta.fields if not f.primary_key] 
     230        non_pks = [f for f in self._meta.fields if not f.primary_key and (f.name in self._modified_fields or f.attname in self._modified_fields)] 
     231        self.reset_modified_fields() 
    208232        cursor = connection.cursor() 
    209233 
    210234        # First, try an UPDATE. If that doesn't update anything, do an INSERT. 
  • django/db/models/query.py

    old new  
    203203                                                    index_start=0, max_depth=self._max_related_depth) 
    204204                else: 
    205205                    obj = self.model(*row[:index_end]) 
     206                    obj.reset_modified_fields() 
    206207                for i, k in enumerate(extra_select): 
    207208                    setattr(obj, k[0], row[index_end+i]) 
    208209                yield obj 
  • tests/modeltests/update_fields/models.py

    old new  
     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"""}