Commits

russ...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed 8795501

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

  • Participants
  • Parent commits 371530b

Comments (0)

Files changed (3)

     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>

File docs/ref/models/instances.txt

 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
 ================
 
    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`.
 

File tests/modeltests/expressions/models.py

 ...
 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'
+
 """}