mirror of https://github.com/django/django.git
Fixed #4102 -- Allow update of specific fields in model.save()
Added the ability to update only part of the model's fields in model.save() by introducing a new kwarg "update_fields". Thanks to all the numerous reviewers and commenters in the ticket
This commit is contained in:
parent
25128856f5
commit
365853da01
|
@ -11,7 +11,7 @@ from django.core import validators
|
||||||
from django.db.models.fields import AutoField, FieldDoesNotExist
|
from django.db.models.fields import AutoField, FieldDoesNotExist
|
||||||
from django.db.models.fields.related import (ManyToOneRel,
|
from django.db.models.fields.related import (ManyToOneRel,
|
||||||
OneToOneField, add_lazy_relation)
|
OneToOneField, add_lazy_relation)
|
||||||
from django.db import (connections, router, transaction, DatabaseError,
|
from django.db import (router, transaction, DatabaseError,
|
||||||
DEFAULT_DB_ALIAS)
|
DEFAULT_DB_ALIAS)
|
||||||
from django.db.models.query import Q
|
from django.db.models.query import Q
|
||||||
from django.db.models.query_utils import DeferredAttribute
|
from django.db.models.query_utils import DeferredAttribute
|
||||||
|
@ -449,7 +449,8 @@ class Model(object):
|
||||||
return getattr(self, field_name)
|
return getattr(self, field_name)
|
||||||
return getattr(self, field.attname)
|
return getattr(self, field.attname)
|
||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, using=None):
|
def save(self, force_insert=False, force_update=False, using=None,
|
||||||
|
update_fields=None):
|
||||||
"""
|
"""
|
||||||
Saves the current instance. Override this in a subclass if you want to
|
Saves the current instance. Override this in a subclass if you want to
|
||||||
control the saving process.
|
control the saving process.
|
||||||
|
@ -458,14 +459,32 @@ class Model(object):
|
||||||
that the "save" must be an SQL insert or update (or equivalent for
|
that the "save" must be an SQL insert or update (or equivalent for
|
||||||
non-SQL backends), respectively. Normally, they should not be set.
|
non-SQL backends), respectively. Normally, they should not be set.
|
||||||
"""
|
"""
|
||||||
if force_insert and force_update:
|
if force_insert and (force_update or update_fields):
|
||||||
raise ValueError("Cannot force both insert and updating in model saving.")
|
raise ValueError("Cannot force both insert and updating in model saving.")
|
||||||
self.save_base(using=using, force_insert=force_insert, force_update=force_update)
|
|
||||||
|
|
||||||
|
if update_fields is not None:
|
||||||
|
# If update_fields is empty, skip the save. We do also check for
|
||||||
|
# no-op saves later on for inheritance cases. This bailout is
|
||||||
|
# still needed for skipping signal sending.
|
||||||
|
if len(update_fields) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
update_fields = frozenset(update_fields)
|
||||||
|
field_names = set([field.name for field in self._meta.fields
|
||||||
|
if not field.primary_key])
|
||||||
|
non_model_fields = update_fields.difference(field_names)
|
||||||
|
|
||||||
|
if non_model_fields:
|
||||||
|
raise ValueError("The following fields do not exist in this "
|
||||||
|
"model or are m2m fields: %s"
|
||||||
|
% ', '.join(non_model_fields))
|
||||||
|
|
||||||
|
self.save_base(using=using, force_insert=force_insert,
|
||||||
|
force_update=force_update, update_fields=update_fields)
|
||||||
save.alters_data = True
|
save.alters_data = True
|
||||||
|
|
||||||
def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
|
def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
|
||||||
force_update=False, using=None):
|
force_update=False, using=None, update_fields=None):
|
||||||
"""
|
"""
|
||||||
Does the heavy-lifting involved in saving. Subclasses shouldn't need to
|
Does the heavy-lifting involved in saving. Subclasses shouldn't need to
|
||||||
override this method. It's separate from save() in order to hide the
|
override this method. It's separate from save() in order to hide the
|
||||||
|
@ -473,7 +492,8 @@ class Model(object):
|
||||||
('raw', 'cls', and 'origin').
|
('raw', 'cls', and 'origin').
|
||||||
"""
|
"""
|
||||||
using = using or router.db_for_write(self.__class__, instance=self)
|
using = using or router.db_for_write(self.__class__, instance=self)
|
||||||
assert not (force_insert and force_update)
|
assert not (force_insert and (force_update or update_fields))
|
||||||
|
assert update_fields is None or len(update_fields) > 0
|
||||||
if cls is None:
|
if cls is None:
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
meta = cls._meta
|
meta = cls._meta
|
||||||
|
@ -483,7 +503,8 @@ class Model(object):
|
||||||
meta = cls._meta
|
meta = cls._meta
|
||||||
|
|
||||||
if origin and not meta.auto_created:
|
if origin and not meta.auto_created:
|
||||||
signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using)
|
signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
|
||||||
|
update_fields=update_fields)
|
||||||
|
|
||||||
# If we are in a raw save, save the object exactly as presented.
|
# If we are in a raw save, save the object exactly as presented.
|
||||||
# That means that we don't try to be smart about saving attributes
|
# That means that we don't try to be smart about saving attributes
|
||||||
|
@ -503,7 +524,8 @@ class Model(object):
|
||||||
if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
|
if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
|
||||||
setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
|
setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
|
||||||
|
|
||||||
self.save_base(cls=parent, origin=org, using=using)
|
self.save_base(cls=parent, origin=org, using=using,
|
||||||
|
update_fields=update_fields)
|
||||||
|
|
||||||
if field:
|
if field:
|
||||||
setattr(self, field.attname, self._get_pk_val(parent._meta))
|
setattr(self, field.attname, self._get_pk_val(parent._meta))
|
||||||
|
@ -513,22 +535,27 @@ class Model(object):
|
||||||
if not meta.proxy:
|
if not meta.proxy:
|
||||||
non_pks = [f for f in meta.local_fields if not f.primary_key]
|
non_pks = [f for f in meta.local_fields if not f.primary_key]
|
||||||
|
|
||||||
|
if update_fields:
|
||||||
|
non_pks = [f for f in non_pks if f.name in update_fields]
|
||||||
|
|
||||||
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
||||||
pk_val = self._get_pk_val(meta)
|
pk_val = self._get_pk_val(meta)
|
||||||
pk_set = pk_val is not None
|
pk_set = pk_val is not None
|
||||||
record_exists = True
|
record_exists = True
|
||||||
manager = cls._base_manager
|
manager = cls._base_manager
|
||||||
if pk_set:
|
if pk_set:
|
||||||
# Determine whether a record with the primary key already exists.
|
# Determine if we should do an update (pk already exists, forced update,
|
||||||
if (force_update or (not force_insert and
|
# no force_insert)
|
||||||
|
if ((force_update or update_fields) or (not force_insert and
|
||||||
manager.using(using).filter(pk=pk_val).exists())):
|
manager.using(using).filter(pk=pk_val).exists())):
|
||||||
# It does already exist, so do an UPDATE.
|
|
||||||
if force_update or non_pks:
|
if force_update or non_pks:
|
||||||
values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
|
values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
|
||||||
if values:
|
if values:
|
||||||
rows = manager.using(using).filter(pk=pk_val)._update(values)
|
rows = manager.using(using).filter(pk=pk_val)._update(values)
|
||||||
if force_update and not rows:
|
if force_update and not rows:
|
||||||
raise DatabaseError("Forced update did not affect any rows.")
|
raise DatabaseError("Forced update did not affect any rows.")
|
||||||
|
if update_fields and not rows:
|
||||||
|
raise DatabaseError("Save with update_fields did not affect any rows.")
|
||||||
else:
|
else:
|
||||||
record_exists = False
|
record_exists = False
|
||||||
if not pk_set or not record_exists:
|
if not pk_set or not record_exists:
|
||||||
|
@ -541,7 +568,7 @@ class Model(object):
|
||||||
|
|
||||||
fields = meta.local_fields
|
fields = meta.local_fields
|
||||||
if not pk_set:
|
if not pk_set:
|
||||||
if force_update:
|
if force_update or update_fields:
|
||||||
raise ValueError("Cannot force an update in save() with no primary key.")
|
raise ValueError("Cannot force an update in save() with no primary key.")
|
||||||
fields = [f for f in fields if not isinstance(f, AutoField)]
|
fields = [f for f in fields if not isinstance(f, AutoField)]
|
||||||
|
|
||||||
|
@ -561,8 +588,8 @@ class Model(object):
|
||||||
|
|
||||||
# Signal that the save is complete
|
# Signal that the save is complete
|
||||||
if origin and not meta.auto_created:
|
if origin and not meta.auto_created:
|
||||||
signals.post_save.send(sender=origin, instance=self,
|
signals.post_save.send(sender=origin, instance=self, created=(not record_exists),
|
||||||
created=(not record_exists), raw=raw, using=using)
|
update_fields=update_fields, raw=raw, using=using)
|
||||||
|
|
||||||
|
|
||||||
save_base.alters_data = True
|
save_base.alters_data = True
|
||||||
|
|
|
@ -5,8 +5,8 @@ class_prepared = Signal(providing_args=["class"])
|
||||||
pre_init = Signal(providing_args=["instance", "args", "kwargs"])
|
pre_init = Signal(providing_args=["instance", "args", "kwargs"])
|
||||||
post_init = Signal(providing_args=["instance"])
|
post_init = Signal(providing_args=["instance"])
|
||||||
|
|
||||||
pre_save = Signal(providing_args=["instance", "raw", "using"])
|
pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"])
|
||||||
post_save = Signal(providing_args=["instance", "raw", "created", "using"])
|
post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"])
|
||||||
|
|
||||||
pre_delete = Signal(providing_args=["instance", "using"])
|
pre_delete = Signal(providing_args=["instance", "using"])
|
||||||
post_delete = Signal(providing_args=["instance", "using"])
|
post_delete = Signal(providing_args=["instance", "using"])
|
||||||
|
|
|
@ -135,7 +135,7 @@ Saving objects
|
||||||
|
|
||||||
To save an object back to the database, call ``save()``:
|
To save an object back to the database, call ``save()``:
|
||||||
|
|
||||||
.. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS])
|
.. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None])
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
.. versionadded:: 1.2
|
||||||
The ``using`` argument was added.
|
The ``using`` argument was added.
|
||||||
|
@ -289,6 +289,8 @@ almost always do the right thing and trying to override that will lead to
|
||||||
errors that are difficult to track down. This feature is for advanced use
|
errors that are difficult to track down. This feature is for advanced use
|
||||||
only.
|
only.
|
||||||
|
|
||||||
|
Using ``update_fields`` will force an update similarly to ``force_update``.
|
||||||
|
|
||||||
Updating attributes based on existing fields
|
Updating attributes based on existing fields
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
@ -334,6 +336,26 @@ For more details, see the documentation on :ref:`F() expressions
|
||||||
<query-expressions>` and their :ref:`use in update queries
|
<query-expressions>` and their :ref:`use in update queries
|
||||||
<topics-db-queries-update>`.
|
<topics-db-queries-update>`.
|
||||||
|
|
||||||
|
Specifying which fields to save
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
If ``save()`` is passed a list of field names in keyword argument
|
||||||
|
``update_fields``, only the fields named in that list will be updated.
|
||||||
|
This may be desirable if you want to update just one or a few fields on
|
||||||
|
an object. There will be a slight performance benefit from preventing
|
||||||
|
all of the model fields from being updated in the database. For example:
|
||||||
|
|
||||||
|
product.name = 'Name changed again'
|
||||||
|
product.save(update_fields=['name'])
|
||||||
|
|
||||||
|
The ``update_fields`` argument can be any iterable containing strings. An
|
||||||
|
empty ``update_fields`` iterable will skip the save. A value of None will
|
||||||
|
perform an update on all fields.
|
||||||
|
|
||||||
|
Specifying ``update_fields`` will force an update.
|
||||||
|
|
||||||
Deleting objects
|
Deleting objects
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,12 @@ Arguments sent with this signal:
|
||||||
``using``
|
``using``
|
||||||
The database alias being used.
|
The database alias being used.
|
||||||
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
``update_fields``
|
||||||
|
The set of fields to update explicitly specified in the ``save()`` method.
|
||||||
|
``None`` if this argument was not used in the ``save()`` call.
|
||||||
|
|
||||||
post_save
|
post_save
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -154,6 +160,12 @@ Arguments sent with this signal:
|
||||||
``using``
|
``using``
|
||||||
The database alias being used.
|
The database alias being used.
|
||||||
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
``update_fields``
|
||||||
|
The set of fields to update explicitly specified in the ``save()`` method.
|
||||||
|
``None`` if this argument was not used in the ``save()`` call.
|
||||||
|
|
||||||
pre_delete
|
pre_delete
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,17 @@ version compatible with Python 2.6.
|
||||||
What's new in Django 1.5
|
What's new in Django 1.5
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
Support for saving a subset of model's fields
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The method :meth:`Model.save() <django.db.models.Model.save()>` has a new
|
||||||
|
keyword argument ``update_fields``. By using this argument it is possible to
|
||||||
|
save only a select list of model's fields. This can be useful for performance
|
||||||
|
reasons or when trying to avoid overwriting concurrent changes.
|
||||||
|
|
||||||
|
See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for
|
||||||
|
more details.
|
||||||
|
|
||||||
Minor features
|
Minor features
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
GENDER_CHOICES = (
|
||||||
|
('M', 'Male'),
|
||||||
|
('F', 'Female'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Account(models.Model):
|
||||||
|
num = models.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
class Person(models.Model):
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Employee(Person):
|
||||||
|
employee_num = models.IntegerField(default=0)
|
||||||
|
profile = models.ForeignKey('Profile', related_name='profiles', null=True)
|
||||||
|
accounts = models.ManyToManyField('Account', related_name='employees', blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Profile(models.Model):
|
||||||
|
name = models.CharField(max_length=200)
|
||||||
|
salary = models.FloatField(default=1000.0)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyEmployee(Employee):
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
|
@ -0,0 +1,146 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.db.models.signals import pre_save, post_save
|
||||||
|
from .models import Person, Employee, ProxyEmployee, Profile, Account
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateOnlyFieldsTests(TestCase):
|
||||||
|
def test_update_fields_basic(self):
|
||||||
|
s = Person.objects.create(name='Sara', gender='F')
|
||||||
|
self.assertEqual(s.gender, 'F')
|
||||||
|
|
||||||
|
s.gender = 'M'
|
||||||
|
s.name = 'Ian'
|
||||||
|
s.save(update_fields=['name'])
|
||||||
|
|
||||||
|
s = Person.objects.get(pk=s.pk)
|
||||||
|
self.assertEqual(s.gender, 'F')
|
||||||
|
self.assertEqual(s.name, 'Ian')
|
||||||
|
|
||||||
|
def test_update_fields_m2m(self):
|
||||||
|
profile_boss = Profile.objects.create(name='Boss', salary=3000)
|
||||||
|
e1 = Employee.objects.create(name='Sara', gender='F',
|
||||||
|
employee_num=1, profile=profile_boss)
|
||||||
|
|
||||||
|
a1 = Account.objects.create(num=1)
|
||||||
|
a2 = Account.objects.create(num=2)
|
||||||
|
|
||||||
|
e1.accounts = [a1,a2]
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
e1.save(update_fields=['accounts'])
|
||||||
|
|
||||||
|
def test_update_fields_inheritance(self):
|
||||||
|
profile_boss = Profile.objects.create(name='Boss', salary=3000)
|
||||||
|
profile_receptionist = Profile.objects.create(name='Receptionist', salary=1000)
|
||||||
|
|
||||||
|
e1 = Employee.objects.create(name='Sara', gender='F',
|
||||||
|
employee_num=1, profile=profile_boss)
|
||||||
|
|
||||||
|
e1.name = 'Ian'
|
||||||
|
e1.gender = 'M'
|
||||||
|
e1.save(update_fields=['name'])
|
||||||
|
|
||||||
|
e2 = Employee.objects.get(pk=e1.pk)
|
||||||
|
self.assertEqual(e2.name, 'Ian')
|
||||||
|
self.assertEqual(e2.gender, 'F')
|
||||||
|
self.assertEqual(e2.profile, profile_boss)
|
||||||
|
|
||||||
|
e2.profile = profile_receptionist
|
||||||
|
e2.name = 'Sara'
|
||||||
|
e2.save(update_fields=['profile'])
|
||||||
|
|
||||||
|
e3 = Employee.objects.get(pk=e1.pk)
|
||||||
|
self.assertEqual(e3.name, 'Ian')
|
||||||
|
self.assertEqual(e3.profile, profile_receptionist)
|
||||||
|
|
||||||
|
def test_update_fields_inheritance_with_proxy_model(self):
|
||||||
|
profile_boss = Profile.objects.create(name='Boss', salary=3000)
|
||||||
|
profile_receptionist = Profile.objects.create(name='Receptionist', salary=1000)
|
||||||
|
|
||||||
|
e1 = ProxyEmployee.objects.create(name='Sara', gender='F',
|
||||||
|
employee_num=1, profile=profile_boss)
|
||||||
|
|
||||||
|
e1.name = 'Ian'
|
||||||
|
e1.gender = 'M'
|
||||||
|
e1.save(update_fields=['name'])
|
||||||
|
|
||||||
|
e2 = ProxyEmployee.objects.get(pk=e1.pk)
|
||||||
|
self.assertEqual(e2.name, 'Ian')
|
||||||
|
self.assertEqual(e2.gender, 'F')
|
||||||
|
self.assertEqual(e2.profile, profile_boss)
|
||||||
|
|
||||||
|
e2.profile = profile_receptionist
|
||||||
|
e2.name = 'Sara'
|
||||||
|
e2.save(update_fields=['profile'])
|
||||||
|
|
||||||
|
e3 = ProxyEmployee.objects.get(pk=e1.pk)
|
||||||
|
self.assertEqual(e3.name, 'Ian')
|
||||||
|
self.assertEqual(e3.profile, profile_receptionist)
|
||||||
|
|
||||||
|
def test_update_fields_signals(self):
|
||||||
|
p = Person.objects.create(name='Sara', gender='F')
|
||||||
|
pre_save_data = []
|
||||||
|
def pre_save_receiver(**kwargs):
|
||||||
|
pre_save_data.append(kwargs['update_fields'])
|
||||||
|
pre_save.connect(pre_save_receiver)
|
||||||
|
post_save_data = []
|
||||||
|
def post_save_receiver(**kwargs):
|
||||||
|
post_save_data.append(kwargs['update_fields'])
|
||||||
|
post_save.connect(post_save_receiver)
|
||||||
|
p.save(update_fields=['name'])
|
||||||
|
self.assertEqual(len(pre_save_data), 1)
|
||||||
|
self.assertEqual(len(pre_save_data[0]), 1)
|
||||||
|
self.assertTrue('name' in pre_save_data[0])
|
||||||
|
self.assertEqual(len(post_save_data), 1)
|
||||||
|
self.assertEqual(len(post_save_data[0]), 1)
|
||||||
|
self.assertTrue('name' in post_save_data[0])
|
||||||
|
|
||||||
|
def test_update_fields_incorrect_params(self):
|
||||||
|
s = Person.objects.create(name='Sara', gender='F')
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
s.save(update_fields=['first_name'])
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
s.save(update_fields="name")
|
||||||
|
|
||||||
|
def test_empty_update_fields(self):
|
||||||
|
s = Person.objects.create(name='Sara', gender='F')
|
||||||
|
pre_save_data = []
|
||||||
|
def pre_save_receiver(**kwargs):
|
||||||
|
pre_save_data.append(kwargs['update_fields'])
|
||||||
|
pre_save.connect(pre_save_receiver)
|
||||||
|
post_save_data = []
|
||||||
|
def post_save_receiver(**kwargs):
|
||||||
|
post_save_data.append(kwargs['update_fields'])
|
||||||
|
post_save.connect(post_save_receiver)
|
||||||
|
# Save is skipped.
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
s.save(update_fields=[])
|
||||||
|
# Signals were skipped, too...
|
||||||
|
self.assertEqual(len(pre_save_data), 0)
|
||||||
|
self.assertEqual(len(post_save_data), 0)
|
||||||
|
|
||||||
|
def test_num_queries_inheritance(self):
|
||||||
|
s = Employee.objects.create(name='Sara', gender='F')
|
||||||
|
s.employee_num = 1
|
||||||
|
s.name = 'Emily'
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
s.save(update_fields=['employee_num'])
|
||||||
|
s = Employee.objects.get(pk=s.pk)
|
||||||
|
self.assertEqual(s.employee_num, 1)
|
||||||
|
self.assertEqual(s.name, 'Sara')
|
||||||
|
s.employee_num = 2
|
||||||
|
s.name = 'Emily'
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
s.save(update_fields=['name'])
|
||||||
|
s = Employee.objects.get(pk=s.pk)
|
||||||
|
self.assertEqual(s.name, 'Emily')
|
||||||
|
self.assertEqual(s.employee_num, 1)
|
||||||
|
# A little sanity check that we actually did updates...
|
||||||
|
self.assertEqual(Employee.objects.count(), 1)
|
||||||
|
self.assertEqual(Person.objects.count(), 1)
|
||||||
|
with self.assertNumQueries(2):
|
||||||
|
s.save(update_fields=['name', 'employee_num'])
|
Loading…
Reference in New Issue