Ticket #4102: update_fields_django-1.4-2.patch

File update_fields_django-1.4-2.patch, 9.0 KB (added by Andrei Antoukh, 12 years ago)
  • docs/ref/signals.txt

     
    123123``using``
    124124    The database alias being used.
    125125
     126``update_fields``
     127    List of fields to update explicitly specified in the ``save()`` method.
     128
    126129post_save
    127130---------
    128131
     
    154157``using``
    155158    The database alias being used.
    156159
     160``update_fields``
     161    List of fields to update explicitly specified in the ``save()`` method.
     162
    157163pre_delete
    158164----------
    159165
  • docs/ref/models/instances.txt

     
    135135
    136136To save an object back to the database, call ``save()``:
    137137
    138 .. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS])
     138.. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None])
    139139
    140140.. versionadded:: 1.2
    141141   The ``using`` argument was added.
     
    334334<query-expressions>` and their :ref:`use in update queries
    335335<topics-db-queries-update>`.
    336336
     337Specifying which fields to save
     338-------------------------------
     339
     340If ``save()`` is passed a list of field names as keyword argument ``update_fields``,
     341only the fields named in that list will be saved to the database. This may be
     342desirable if you want to update just one or a few fields on an object, as there
     343will be a slight performance benefit from preventing all of the model fields
     344from being updated in the database. For example:
     345
     346    product.name = 'Name changed again'
     347    product.save(update_fields=['name'])
     348
     349
    337350Deleting objects
    338351================
    339352
  • django/db/models/signals.py

     
    55pre_init = Signal(providing_args=["instance", "args", "kwargs"])
    66post_init = Signal(providing_args=["instance"])
    77
    8 pre_save = Signal(providing_args=["instance", "raw", "using"])
    9 post_save = Signal(providing_args=["instance", "raw", "created", "using"])
     8pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"])
     9post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"])
    1010
    1111pre_delete = Signal(providing_args=["instance", "using"])
    1212post_delete = Signal(providing_args=["instance", "using"])
  • django/db/models/base.py

     
    449449            return getattr(self, field_name)
    450450        return getattr(self, field.attname)
    451451
    452     def save(self, force_insert=False, force_update=False, using=None):
     452    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
    453453        """
    454454        Saves the current instance. Override this in a subclass if you want to
    455455        control the saving process.
     
    460460        """
    461461        if force_insert and force_update:
    462462            raise ValueError("Cannot force both insert and updating in model saving.")
    463         self.save_base(using=using, force_insert=force_insert, force_update=force_update)
     463       
     464        if update_fields:
     465            field_names = self._meta.get_all_field_names()
     466            for field_name in update_fields:
     467                if field_name not in field_names:
     468                    raise ValueError("%s field does not exist in this model" % (field_name))
    464469
     470        self.save_base(using=using, force_insert=force_insert,
     471                        force_update=force_update, update_fields=update_fields)
     472
    465473    save.alters_data = True
    466474
    467475    def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
    468             force_update=False, using=None):
     476            force_update=False, using=None, update_fields=None):
    469477        """
    470478        Does the heavy-lifting involved in saving. Subclasses shouldn't need to
    471479        override this method. It's separate from save() in order to hide the
     
    483491            meta = cls._meta
    484492
    485493        if origin and not meta.auto_created:
    486             signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using)
     494            signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
     495                                                            update_fields=update_fields)
    487496
    488497        # If we are in a raw save, save the object exactly as presented.
    489498        # That means that we don't try to be smart about saving attributes
     
    503512                if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
    504513                    setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
    505514
    506                 self.save_base(cls=parent, origin=org, using=using)
     515                self.save_base(cls=parent, origin=org, using=using, update_fields=update_fields)
    507516
    508517                if field:
    509518                    setattr(self, field.attname, self._get_pk_val(parent._meta))
     
    513522        if not meta.proxy:
    514523            non_pks = [f for f in meta.local_fields if not f.primary_key]
    515524
     525            if update_fields:
     526                non_pks = [f for f in non_pks if f.name in update_fields]
     527
    516528            # First, try an UPDATE. If that doesn't update anything, do an INSERT.
    517529            pk_val = self._get_pk_val(meta)
    518530            pk_set = pk_val is not None
     
    561573
    562574        # Signal that the save is complete
    563575        if origin and not meta.auto_created:
    564             signals.post_save.send(sender=origin, instance=self,
    565                 created=(not record_exists), raw=raw, using=using)
     576            signals.post_save.send(sender=origin, instance=self, created=(not record_exists),
     577                                                update_fields=update_fields, raw=raw, using=using)
    566578
    567579
    568580    save_base.alters_data = True
  • tests/modeltests/update_only_fields/tests.py

     
     1from __future__ import absolute_import
     2from __future__ import with_statement
     3
     4from django.test import TestCase
     5from .models import Person, Employee, Profile
     6
     7class UpdateOnlyFieldsTests(TestCase):
     8    def test_simple_update_fields(self):
     9        s = Person.objects.create(name='Sara', gender='F')
     10        self.assertEqual(s.gender, 'F')
     11
     12        s.gender = 'M'
     13        s.name = 'Ian'
     14        s.save(update_fields=['name'])
     15
     16        s = Person.objects.get(pk=s.pk)
     17        self.assertEqual(s.gender, 'F')
     18        self.assertEqual(s.name, 'Ian')
     19
     20        s.delete()
     21
     22    def test_update_field_with_inherited(self):
     23        profile_boss = Profile.objects.create(name='Boss', salary=3000)
     24        profile_receptionist = Profile.objects.create(name='Receptionist', salary=1000)
     25
     26        e1 = Employee.objects.create(name='Sara', gender='F',
     27            employee_num=1, profile=profile_boss)
     28
     29        e1.name = 'Ian'
     30        e1.gender = 'M'
     31        e1.save(update_fields=['name'])
     32
     33        e2 = Employee.objects.get(pk=e1.pk)
     34        self.assertEqual(e2.name, 'Ian')
     35        self.assertEqual(e2.gender, 'F')
     36        self.assertEqual(e2.profile, profile_boss)
     37
     38        e2.profile = profile_receptionist
     39        e2.name = 'Sara'
     40        e2.save(update_fields=['profile'])
     41
     42        e3 = Employee.objects.get(pk=e1.pk)
     43        self.assertEqual(e3.name, 'Ian')
     44        self.assertEqual(e3.profile, profile_receptionist)
     45
     46        e1.delete()
     47        profile_boss.delete()
     48        profile_receptionist.delete()
     49
     50    def test_update_field_with_incorrect_params(self):
     51        s = Person.objects.create(name='Sara', gender='F')
     52
     53        with self.assertRaises(ValueError):
     54            s.save(update_fields=['first_name'])
     55
     56        s.delete()
  • tests/modeltests/update_only_fields/models.py

     
     1
     2from django.db import models
     3
     4GENDER_CHOICES = (
     5    ('M', 'Male'),
     6    ('F', 'Female'),
     7)
     8
     9class Person(models.Model):
     10    name = models.CharField(max_length=20)
     11    gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
     12
     13    def __unicode__(self):
     14        return self.name
     15
     16class Employee(Person):
     17    employee_num = models.IntegerField(default=0)
     18    profile = models.ForeignKey('Profile', related_name='profiles')
     19
     20
     21class Profile(models.Model):
     22    name = models.CharField(max_length=200)
     23    salary = models.FloatField(default=1000.0)
     24
     25    def __unicode__(self):
     26        return self.name
Back to Top