Ticket #4102: update_fields_django-1.4.patch

File update_fields_django-1.4.patch, 6.8 KB (added by Andrei Antoukh, 12 years ago)
  • 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/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
     
    503511                if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
    504512                    setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
    505513
    506                 self.save_base(cls=parent, origin=org, using=using)
     514                self.save_base(cls=parent, origin=org, using=using, update_fields=update_fields)
    507515
    508516                if field:
    509517                    setattr(self, field.attname, self._get_pk_val(parent._meta))
     
    513521        if not meta.proxy:
    514522            non_pks = [f for f in meta.local_fields if not f.primary_key]
    515523
     524            if update_fields:
     525                non_pks = [f for f in non_pks if f.name in update_fields]
     526
    516527            # First, try an UPDATE. If that doesn't update anything, do an INSERT.
    517528            pk_val = self._get_pk_val(meta)
    518529            pk_set = pk_val is not None
  • tests/modeltests/update_only_fields/__init__.py

     
     1# -*- coding: utf-8 -*-
     2
     3
  • 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
     48    def test_update_field_with_incorrect_params(self):
     49        s = Person.objects.create(name='Sara', gender='F')
     50
     51        with self.assertRaises(ValueError):
     52            s.save(update_fields=['first_name'])
     53
     54        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