Fixed #11527 -- Added unit tests and documentation for the use of F() expressions in single object updates. Thanks to Zachary Voase for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11322 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2009-07-24 13:38:36 +00:00
parent 007bfddc1f
commit b2f72fc040
3 changed files with 81 additions and 1 deletions

View File

@ -439,6 +439,7 @@ answer newbie questions, and generally made Django that much better:
viestards.lists@gmail.com
George Vilches <gav@thataddress.com>
Vlado <vlado@labath.org>
Zachary Voase <zacharyvoase@gmail.com>
Milton Waddams
Chris Wagner <cw264701@ohio.edu>
Rick Wagner <rwagner@physics.ucsd.edu>

View File

@ -188,6 +188,46 @@ 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
only.
Updating attributes based on existing fields
--------------------------------------------
Sometimes you'll need to perform a simple arithmetic task on a field, such
as incrementing or decrementing the current value. The obvious way to
achieve this is to do something like::
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()
If the old ``number_sold`` value retrieved from the database was 10, then
the value of 11 will be written back to the database.
This can be optimized slightly by expressing the update relative to the
original field value, rather than as an explicit assignment of a new value.
Django provides :ref:`F() expressions <query-expressions>` as a way of
performing this kind of relative update. Using ``F()`` expressions, the
previous example would be expressed as::
>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()
This approach doesn't use the initial value from the database. Instead, it
makes the database do the update based on whatever value is current at the
time that the save() is executed.
Once the object has been saved, you must reload the object in order to access
the actual value that was applied to the updated field::
>>> product = Products.objects.get(pk=product.pk)
>>> print product.number_sold
42
For more details, see the documentation on :ref:`F() expressions
<query-expressions>` and their :ref:`use in update queries
<topics-db-queries-update>`.
Deleting objects
================
@ -196,7 +236,7 @@ Deleting objects
Issues a SQL ``DELETE`` for the object. This only deletes the object in the
database; the Python instance will still be around, and will still have data
in its fields.
For more details, including how to delete objects in bulk, see
:ref:`topics-db-queries-delete`.

View File

@ -80,4 +80,43 @@ Traceback (most recent call last):
...
FieldError: Joined field references are not permitted in this query
# F expressions can be used to update attributes on single objects
>>> test_gmbh = Company.objects.get(name='Test GmbH')
>>> test_gmbh.num_employees
32
>>> test_gmbh.num_employees = F('num_employees') + 4
>>> test_gmbh.save()
>>> test_gmbh = Company.objects.get(pk=test_gmbh.pk)
>>> test_gmbh.num_employees
36
# F expressions cannot be used to update attributes which are foreign keys, or
# attributes which involve joins.
>>> test_gmbh.point_of_contact = None
>>> test_gmbh.save()
>>> test_gmbh.point_of_contact is None
True
>>> test_gmbh.point_of_contact = F('ceo')
Traceback (most recent call last):
...
ValueError: Cannot assign "<django.db.models.expressions.F object at ...>": "Company.point_of_contact" must be a "Employee" instance.
>>> test_gmbh.point_of_contact = test_gmbh.ceo
>>> test_gmbh.save()
>>> test_gmbh.name = F('ceo__last_name')
>>> test_gmbh.save()
Traceback (most recent call last):
...
FieldError: Joined field references are not permitted in this query
# F expressions cannot be used to update attributes on objects which do not yet
# exist in the database
>>> acme = Company(name='The Acme Widget Co.', num_employees=12, num_chairs=5,
... ceo=test_gmbh.ceo)
>>> acme.num_employees = F('num_employees') + 16
>>> acme.save()
Traceback (most recent call last):
...
TypeError: int() argument must be a string or a number, not 'ExpressionNode'
"""}