Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit b2f72fc0405e966a5c4dbce23e84a43d80db3614 1 parent 007bfdd
Russell Keith-Magee authored July 24, 2009
1  AUTHORS
@@ -439,6 +439,7 @@ answer newbie questions, and generally made Django that much better:
439 439
     viestards.lists@gmail.com
440 440
     George Vilches <gav@thataddress.com>
441 441
     Vlado <vlado@labath.org>
  442
+    Zachary Voase <zacharyvoase@gmail.com>
442 443
     Milton Waddams
443 444
     Chris Wagner <cw264701@ohio.edu>
444 445
     Rick Wagner <rwagner@physics.ucsd.edu>
42  docs/ref/models/instances.txt
@@ -188,6 +188,46 @@ almost always do the right thing and trying to override that will lead to
188 188
 errors that are difficult to track down. This feature is for advanced use
189 189
 only.
190 190
 
  191
+Updating attributes based on existing fields
  192
+--------------------------------------------
  193
+
  194
+Sometimes you'll need to perform a simple arithmetic task on a field, such
  195
+as incrementing or decrementing the current value. The obvious way to
  196
+achieve this is to do something like::
  197
+
  198
+    >>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
  199
+    >>> product.number_sold += 1
  200
+    >>> product.save()
  201
+
  202
+If the old ``number_sold`` value retrieved from the database was 10, then
  203
+the value of 11 will be written back to the database.
  204
+
  205
+This can be optimized slightly by expressing the update relative to the
  206
+original field value, rather than as an explicit assignment of a new value.
  207
+Django provides :ref:`F() expressions <query-expressions>` as a way of
  208
+performing this kind of relative update. Using ``F()`` expressions, the
  209
+previous example would be expressed as::
  210
+
  211
+    >>> from django.db.models import F
  212
+    >>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
  213
+    >>> product.number_sold = F('number_sold') + 1
  214
+    >>> product.save()
  215
+
  216
+This approach doesn't use the initial value from the database. Instead, it
  217
+makes the database do the update based on whatever value is current at the
  218
+time that the save() is executed.
  219
+
  220
+Once the object has been saved, you must reload the object in order to access
  221
+the actual value that was applied to the updated field::
  222
+
  223
+    >>> product = Products.objects.get(pk=product.pk)
  224
+    >>> print product.number_sold
  225
+    42
  226
+
  227
+For more details, see the documentation on :ref:`F() expressions
  228
+<query-expressions>` and their :ref:`use in update queries
  229
+<topics-db-queries-update>`.
  230
+
191 231
 Deleting objects
192 232
 ================
193 233
 
@@ -196,7 +236,7 @@ Deleting objects
196 236
    Issues a SQL ``DELETE`` for the object. This only deletes the object in the
197 237
    database; the Python instance will still be around, and will still have data
198 238
    in its fields.
199  
-   
  239
+
200 240
    For more details, including how to delete objects in bulk, see
201 241
    :ref:`topics-db-queries-delete`.
202 242
 
39  tests/modeltests/expressions/models.py
@@ -80,4 +80,43 @@ def __unicode__(self):
80 80
 ...
81 81
 FieldError: Joined field references are not permitted in this query
82 82
 
  83
+# F expressions can be used to update attributes on single objects
  84
+>>> test_gmbh = Company.objects.get(name='Test GmbH')
  85
+>>> test_gmbh.num_employees
  86
+32
  87
+>>> test_gmbh.num_employees = F('num_employees') + 4
  88
+>>> test_gmbh.save()
  89
+>>> test_gmbh = Company.objects.get(pk=test_gmbh.pk)
  90
+>>> test_gmbh.num_employees
  91
+36
  92
+
  93
+# F expressions cannot be used to update attributes which are foreign keys, or
  94
+# attributes which involve joins.
  95
+>>> test_gmbh.point_of_contact = None
  96
+>>> test_gmbh.save()
  97
+>>> test_gmbh.point_of_contact is None
  98
+True
  99
+>>> test_gmbh.point_of_contact = F('ceo')
  100
+Traceback (most recent call last):
  101
+...
  102
+ValueError: Cannot assign "<django.db.models.expressions.F object at ...>": "Company.point_of_contact" must be a "Employee" instance.
  103
+
  104
+>>> test_gmbh.point_of_contact = test_gmbh.ceo
  105
+>>> test_gmbh.save()
  106
+>>> test_gmbh.name = F('ceo__last_name')
  107
+>>> test_gmbh.save()
  108
+Traceback (most recent call last):
  109
+...
  110
+FieldError: Joined field references are not permitted in this query
  111
+
  112
+# F expressions cannot be used to update attributes on objects which do not yet
  113
+# exist in the database
  114
+>>> acme = Company(name='The Acme Widget Co.', num_employees=12, num_chairs=5,
  115
+...     ceo=test_gmbh.ceo)
  116
+>>> acme.num_employees = F('num_employees') + 16
  117
+>>> acme.save()
  118
+Traceback (most recent call last):
  119
+...
  120
+TypeError: int() argument must be a string or a number, not 'ExpressionNode'
  121
+
83 122
 """}

0 notes on commit b2f72fc

Please sign in to comment.
Something went wrong with that request. Please try again.