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

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

sigh, patch hates me - fixed duplicate test issue

  • 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, '_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

     
    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

     
     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"""}
Back to Top