I use SQLFORM.smartgrid to show a list of records from a table (service_types)
. In each row of the smartgrid there is a delete link/button to delete the record. I want to executive some code before smartgrid/web2py actually deletes the record, for example I want to know if there are child records (services
table) referencing this record, and if any, flash a message telling user that record cannot be deleted. How is this done?
db.py
db.define_table('service_types',
Field('type_name', requires=[IS_NOT_EMPTY(), IS_ALPHANUMERIC()]),
format='%(type_name)s',
)
db.define_table('services',
Field('service_name',requires=[IS_NOT_EMPTY(),IS_NOT_IN_DB(db,'services.service_name')]),
Field('service_type','reference service_types',requires=IS_IN_DB(db,db.service_types.id,
'%(type_name)s',
error_message='not in table',
zero=None),
ondelete='RESTRICT',
),
Field('interest_rate','decimal(15,2)',requires=IS_DECIMAL_IN_RANGE(0,100)),
Field('max_term','integer'),
auth.signature,
format='%(service_name)s',
)
db.services._plural='Services'
db.services._singular='Service'
if db(db.service_types).count() < 1:
db.service_types.insert(type_name='Loan')
db.service_types.insert(type_name='Contribution')
db.service_types.insert(type_name='Other')
controller
def list_services():
grid = SQLFORM.smartgrid(db.services
, fields = [db.services.service_name,db.services.service_type]
)
return locals()
view
{{extend 'layout.html'}}
{{=grid}}
From Anthony's answer I chose the second option and came up with the following:
But, if I do this...
I get this error:
And if I do this:
I get the flash error message
Cant delete
but the record appears deleted from the list, and reappears after a page refresh with F5 (apparently because the delete was not allowed in the database, which is intended).Which one should I fix and how?
Note If any of these issue is resolved I can accept Anthony's answer.
There are two options. First, the
deletable
argument can be a function that takes theRow
object of a given record and returnsTrue
orFalse
to indicate whether the record is deletable. If it returnsFalse
, the "Delete" button will not be shown for that record, nor the delete operation be allowed on the server.Second, there is an
ondelete
argument that takes thedb
Table
object and the record ID. It is called right before the delete operation, so to prevent the delete, you can do a redirect within that function:Note, if the grid is loaded via an Ajax component and its actions are therefore performed via Ajax, using
redirect
within theondelete
method as shown above will not work well, as the redirect will have no effect and the table row will still be deleted from the grid in the browser (even though the database record was not deleted). In that case, an alternative approach is to return a non-200 HTTP response to the browser, which will prevent the client-side Javascript from deleting the row from the table (the delete happens only on success of the Ajax request). We should also setresponse.flash
instead ofsession.flash
(because we are not redirecting/reloading the whole page):Note, both the
deletable
andondelete
arguments can be dictionaries with table names as keys, so you can specify different values for different tables that might be linked from the smartgrid.Finally, notice the delete URLs look like
/appname/list_services/services/delete/services/[record ID]
. So, in the controller, you can determine if a delete is being requested by checking if'delete' in request.args
. In that case,request.args[-2:]
represents the table name and record ID, which you can use to do any checks.