ok, let's elaborate.
db(db.person.id > 3).update(name='Ken')
and
db(db.person.id > 3).update(name=db.person.name+'Ken')

are actually REALLY different (from the database perspective).

The first passes a FIXED value to the database, which discards any value 
contained in the "name" column and replaces it with a FIXED value that you 
pass to it ('Ken'). Given, e.g., 4 records that have "id" > 3, ALL 4 
records will end up having "Ken" as the value hold in the "name" column.
It's an indempotent function: no matter how many times you execute it, the 
result is always the same.

The second instead asks the database to "append" the "Ken" fixed value to 
whatever the column is holding already. It's NOT idempotent. Each time you 
execute it, you end up having different results.

Now, back to the pyDAL. 
When you ask pydal to do an update of the "latter" case, you pass "down the 
pipe" that assignment
to the database. You're trying to leverage the backend's (and not python's) 
capabilities. This is labelled as an "expression" (see here 
<http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Expressions>for
 
the book docs about it)

The first statement translates roughly to

UPDATE person
SET name = 'Ken'
WHERE id > 3

The second one, instead, translates to

UPDATE person
SET name = *name + 'Ken'*
WHERE id > 3

The funny thing here can not be really obvious, but the second query (the 
"expression") actually calls a function in the backend (in bold with the 
operator in red), that is, in this case, a simple "append" (NB: different 
backend have different syntax for this simple "append" function)

Now, backend are generally MORE limited than python when dealing with 
functions, and pyDAL can "translate" only a fraction of functions.
Strings "append" and "prepend" are some of them, and also "addition" and 
"subtractions" of numeric values, as in

db(db.vote.id == 3).update(value=db.vote.value+1)

Date operations, on the other end, are REALLY easy in python and REALLY 
complicated in backend (every dialect has its own intricacies)

pyDAL can't intercept and translate correctly all of them, so you need to 
test CAREFULLY complicate function in python when passed to an update to 
pyDAL.

For a 

db(db.appointments.id == 3).update(start_date=db.appointments.start_date + 
1)

that under the hood translate to a

UPDATE appointments
SET start_date = start_date + 1
WHERE id = 3

some backend may implicitly add a second, or a day, or a month. Or throw an 
exception.
What you're doing adds yet another layer of intricacies: the backend 
doesn't know the first thing about "timedelta(hours=1)" bit. pyDAL knows 
it, and tries to stringify it.
It's easy to know what query pyDAL is actually passing down the pipe: you 
can see any update() issued to the backend in its dialect using _update() 
(see the underscore).

tl;dr: if you need to update a set of records to a FIXED value (first case 
scenario), no problems on passing a FIXED value down the pipe.
if you need to update a set of records to a CALCULATED value, which is the 
result of a function (i.e. when you have db.table.columnname on the right 
end of the assignment) BEWARE that only SIMPLE operations are 
"transparently" passed to the backend. 
Use _update() to inspect the actual query and TEST your code at every step.
Not EVERY python function (which is, at all effects, passed down the pipe 
and DEMANDED to the backend) has a proper way to be passed to the backend 
dialect to have the same result you'd have in python.

To overcome it and be sure the function is calculated in python, you need 
to fetch the value first, and then update() passing a FIXED value.
This "form" is slower (need to fetch the values first, at the very least) 
but ensures python doing the bits, and not the database

rows = db(db.person.id > 3).select()
for row in rows:
    row.update_record(name=row.name + 'Ken')

or, for the timedelta example

rows = db(db.mytable.id == 1).select()
for row in rows:
     row.update_record(start=row.start + timedelta(hours=1))

Please mind that the difference ISN'T the fact that it uses update_record() 
instead of update(). 
It could be very well rewritten as

db(db.mytable.id == row.id).update(start=row.start + timedelta(hours=1))

The key difference is that on the right end of the assignment there isn't 
db.tablename.columnname (which will trigger the "expressions" behaviour, 
relinquishing control to the backend). With no expressions, the calculation 
is computed in python (up to a fixed value) BEFORE passing it the statement 
to the backend.

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to