_before_insert / update question

99 views
Skip to first unread message

Ian Ryder

unread,
May 28, 2015, 1:16:55 AM5/28/15
to web...@googlegroups.com
Hi, I'd like to modify the data on a record before it is inserted / updated. I was hoping I could do this with _before_insert / update but I don't think it's possible.

As an example from a previous life, in Salesforce on a before trigger you can change field values in memory on the way into the database and they will automatically get written on completion.

Am I right in thinking with web2py it's just a set of data in its own world that you can do something with? I would need to perform an update on the record after it's been inserted which seems like a waste, especially when dealing with large data sets.

Thanks
Ian

Niphlod

unread,
May 28, 2015, 6:02:52 AM5/28/15
to web...@googlegroups.com
"modify data on a record before it's inserted/updated" 


_before_insert and _before_update take your "near to be committed record" and alter it in memory, then it inserts/update it. Don't know what are you asking for that is different from what web2py does.


Ian Ryder

unread,
May 28, 2015, 3:32:01 PM5/28/15
to web...@googlegroups.com
OK, think I've solved it.

The documentation says a dict passed is into the _before_insert - seems it's a list of lists of [field, value]. I can manipulate the list and the result is what gets written.

So perfect - I can do what I need, just I think the documentation needs checking (or I need correcting)

Here's the definition I have which I think is all standard?:
db.income_line_item._before_insert.append(lambda f: trigger_ili_before_insert(f))

Ian Ryder

unread,
May 28, 2015, 3:32:01 PM5/28/15
to web...@googlegroups.com
Thanks - I couldn't find any documentation / examples and my tests didn't seem to work as expected.

Do you know of any examples anywhere I can scan over? I'll have another test in the mean time and update if I get it working

Niphlod

unread,
May 28, 2015, 5:10:21 PM5/28/15
to web...@googlegroups.com
the book shows how to print every argument passed to those functions.... I dunno how to make the book clearer :°°°D


before_insert ....


def this_is_before_insert(some_dict):
   
if 'last_name' in some_dict:
         some_dict
['last_name'] = 'altering' + some_dict['last_name']

db
.auth_user._before_insert.append(lambda f: this_is_before_insert(f))

>>>db.auth_user.insert(first_name='john')
1L
>>> db.auth_user.insert(last_name='doe')
2L
>>> rtn = db(db.auth_user.id>0).select(db.auth_user.first_name, db.auth_user.last_name)
>>> print str(rtn)
auth_user
.first_name,auth_user.last_name
john
,
,alteringdoe

before_update ...

def this_is_before_update(a_set, some_dict):
     
if 'last_name' in some_dict:
          some_dict
['last_name'] += 'was_updated'

db
.auth_user._before_update.append(lambda s,f: this_is_before_update(s,f))

>>> db.auth_user.insert(first_name='john', last_name='doe')
1L
>>> db(db.auth_user.first_name=='john').update(last_name='white')
1
>>> rtn = db(db.auth_user.id>0).select(db.auth_user.first_name, db.auth_user.last_name)
>>> print str(rtn)
auth_user
.first_name,auth_user.last_name
john
,whitewas_updated





Ian Ryder

unread,
May 29, 2015, 12:39:56 AM5/29/15
to web...@googlegroups.com
Hi, the key point is I'm not getting passed a dict, I'm getting passed a list. Here's a dump of what the _is_before handler gets passed:

 [(<pydal.objects.Field object at 0x11eb9ad10>, datetime.datetime(2015, 5, 29, 6, 32, 27, 732420)), (<pydal.objects.Field object at 0x1334a5150>, 1L), (<pydal.objects.Field object at 0x1334a5290>, 8L), (<pydal.objects.Field object at 0x1334a5390>, 2L), (<pydal.objects.Field object at 0x11c2dfcd0>, 5.0), (<pydal.objects.Field object at 0x1334a57d0>, 14093L), (<pydal.objects.Field object at 0x1334a5b10>, 'APPEAL'), (<pydal.objects.Field object at 0x1334a5050>, 1L), (<pydal.objects.Field object at 0x1334a5410>, 358001L), (<pydal.objects.Field object at 0x11eb9a4d0>, datetime.datetime(2015, 5, 29, 6, 32, 27, 732420)), (<pydal.objects.Field object at 0x1334a53d0>, 5.0), (<pydal.objects.Field object at 0x1334a5690>, True), (<pydal.objects.Field object at 0x1334a55d0>, 1L)]

I test it on a different table and it gets a dict as expected:
{'query_group': 1, 'name': 'asddasdasdasdsada', 'created_date': datetime.datetime(2015, 5, 29, 6, 27, 37, 2291), 'run_order': 0, 'type': '', 'sum_description': '', 'created_by': 1L}

I'll post again the definition for the one that isn't working:
db.income_line_item._before_insert.append(lambda f: trigger_ili_before_insert(f))

Which is essentially the same for the one that is working:
db.query._before_insert.append(lambda f: query_before_test(f))


Ian Ryder

unread,
May 29, 2015, 1:32:35 AM5/29/15
to web...@googlegroups.com
OK - I think I have the answer.

I discovered it wasn't table-specific, it worked with this same method / table elsewhere in the app. The place it was failing was using bulk_insert. I changed to insert individually and all works fine.

Bug?

Anthony

unread,
May 29, 2015, 7:43:37 AM5/29/15
to web...@googlegroups.com, i.r...@appichar.com.au
Possible bug. What does your bulk_insert code look like?

Ian Ryder

unread,
May 29, 2015, 3:21:33 PM5/29/15
to web...@googlegroups.com, i.r...@appichar.com.au
It was this:

    new_ilis = []
    ...
        new_ili = {
            'income': this_rg.template_income,
            'income_coding': rgli.income_coding,
            'amount_item': rgli.amount_item,
            'quantity': rgli.quantity,
            'income_source': rgli.income_source,
            'source_agency': rgli.source_agency,
            'source_type': rgli.source_type
        }
        new_ilis.append(new_ili)
        ...
    db.income_line_item.bulk_insert(new_ilis)
Message has been deleted

Anthony

unread,
May 29, 2015, 5:48:38 PM5/29/15
to web...@googlegroups.com, i.r...@appichar.com.au
Yes, this is a bug in bulk_insert -- it calls the _listify method before running the _before_insert callbacks instead of after (_listify changes the format from a dictionary to a list of (field, value) tuples). If you don't mind, please file a pydal github issue and reference this post.

Anthony

黄祥

unread,
May 29, 2015, 7:51:00 PM5/29/15
to web...@googlegroups.com, i.r...@appichar.com.au
i face the same situation before, but because i use bulk_insert in controller install.py so that i put the conditional if on it.
e.g.
models/db.py
# after_insert_purchase_detail
def __after_insert_purchase_detail(f, id):
db(db.dvd.id == f.dvd).update(quantity = db.dvd(f.dvd).quantity + f.quantity)

# on_define_purchase_detail
def on_define_purchase_detail(table): 
# callbacks
if not 'install' in request.controller :
# _after_insert
table._after_insert.append(__after_insert_purchase_detail)

# create table : purchase_detail
db.define_table('purchase_detail', 
Field('purchase_no', 'reference purchase_header'), 
Field('dvd', 'reference dvd'), 
Field('quantity', 'integer'),
Field('price', 'integer'), 
on_define = on_define_purchase_detail, 
format = '%(purchase_no)s')

controllers/install.py
# purchase_detail
db.purchase_detail.bulk_insert([{"purchase_no" : 1, "dvd" : 1, "quantity" : 1, 
"price" : 15000}, 
   {"purchase_no" : 2, "dvd" : 3, "quantity" : 1, 
"price" : 15000}, 
   {"purchase_no" : 2, "dvd" : 4, "quantity" : 1, 
"price" : 15000}, ])

hopefully, after the bug is fixed i don't have to put the conditional if to check the controller again.

best regards,
stifan

Ian Ryder

unread,
Jun 1, 2015, 8:32:37 AM6/1/15
to web...@googlegroups.com, i.r...@appichar.com.au
Done - thanks for your help
Reply all
Reply to author
Forward
0 new messages