Question on data binding with Data Grid

@stefano.menci @p.colbert thanks to both of you for clarification about restricting the view of a row object. I read it in the docs so it’s not a complete discovery to me, but i also never used it so i’m also not familiar with this feature.
However, i don’t think i will ever use this feature or even use rows directly in the client (at least for apps that require a certain level of security and with multiple users or group of users -businesses for instance-).
Because i never used the view feature of data tables, my current approach was simply to :

  • explicitly name the columns i don’t want to share to users as _column_name (for instance, an object id, a user id, or a login date, …)
  • build a Global variable containing data from tables and from columns which name doesn’t start with “_”
  • only send this Global variable (a dict) to the client

i’m sure the view approach would be more efficient, but at least here i know what goes in the client and what stays in the server. Maybe i will add it later in my app (or in future ones).


@stefano.menci :

A Row object is more than a smart database updater. It also caches / lazily loads some values. This can be managed by the fetch_only query arguments and by the accelerated table settings.

Well… will have to look deeper into this if i decide to use em in client someday.


I never send Row objects to the client. My server callables return data structures, usually lists and dictionaries, that the client uses to build some class instances. At this point the client has an object that knows what it needs to know to manage the UI and to call other server callables to save any edited data. I only save the whole object when I decide to save, for example when a user clicks on the Save button, and all the data related to that object, which often is spread across multiple tables, is saved in the same transaction.

Yup, that’s also something i was trying to be aware of. Most of what you describe is what i was trying to figure out a few weeks ago and what i finally came with. As someone with zero background as a dev, I wasn’t sure it was “like that” that devs do it, but it seems to be the most convenient and logical approach.

Didn’t want users to have direct access to rows, so i used global variables (dict) containing the data from tables.
Same for fetching or saving data in tables, i made a first test app with multiple round trips to server functions and understood it was not the way to go.
So yeah, i try to minimize as much as possible the server function calls.

For instance, i do all this in one function during my custom login:

  • check user permissions and read-access to data tables
  • based on user access, create a global variable containing all accessible data
  • store also the UI access of the user (what components should not be displayed), even though someone qualified enough could manipulate it, this would not cause major issues. a user that is not supposed to see something may see it, for instance a “remove item” button, but in the server i double check the user permission so…
  • return global variable to the client, which is used to set the UI.

My global variable being a simple dict, it can be tiring sometimes to update the global variable after a server function call (edit, remove, or add).

This is the right thing to do, but it is not enough. You need to do the same permission check on the server side. Any respectable hacker can see and modify your client side code, and bypass any check and make any server call. But no one will be able to fool the checks made on the server side.

Fortunately i was aware of that, and i always check the user permissions in the server functions. Here is an example:

def check_user_db_access(db_name, writeable=False):
    role = anvil.server.session['role']
    offer = anvil.server.session['offer']
    db_access_by_role = app_tables._db_readable_by_role.get(role=role)[db_name]
    if writeable:
        db_access_by_role = app_tables._db_writeable_by_role.get(role=role)[db_name]
    db_access_by_offer = app_tables._db_access_by_offer.get(offer=offer)[db_name]
    db_access = db_access_by_offer and db_access_by_role
    return db_access

@anvil.server.callable
@anvil.tables.in_transaction
def _delete_item_from_database(db_name, id):
    # check user access:
    if check_user_db_access(db_name, writeable=True):
        # user access granted
        row = getattr(app_tables, db_name, None).get(
            _business_id=anvil.server.session['business_id'],
            _id=id)
        if row:
            row.delete()
            return True, "L'objet a bien été supprimé."
        else:
            return False, "L'objet n'a pas pu être supprimé: objet introuvable dans la base de données."
    else:
        # no access, return cancel message
        return False, "Vous n'avez pas la permission requise pour cette action."

so basically i have databases to define user permissions based on their role and based on the plan (offer) they have subscribed to (which have been stored to the server.session during the custom login process).
Then, when a call to server function from client is made (such as _delete_item_from_database here) user access is always checked.
Then i return True or False if the call is successfull or not, with a success or error message that i display as a notification.


see the AttributeToKey paragraph here

Wow Okay ! now that’s some cool stuff ! Pretty cool to see someone’s compilation of best practices and dos and don’ts.
I’m glad to see my approach is on the good path, even though there’s lot of things i need to rethink.
Will definitely take a closer look at this in the next days !

2 Likes