What I’m trying to do:
I am building administration functionality into an app and I wanted to check whether there was a best practice in Anvil for writing this in a succinct and Pythonic way which uses datatable searches in the “correct” manner. I assume that many people will have this requirement so hopefully this can serve as a template for this type of setup.
Data Tables are: user_roles: admin or viewer orgs: list of org names accounts: list of accounts and their association with one or more orgs users: standard user table data plus user_role and orgs to which the user belongs
What I’ve tried and what’s not working:
I have followed all of the tutorials for working with data grids (which highlight different ways of interacting with them e.g. search results vs. data bindings and UI vs. code), but I’m still having trouble working with the search iterators and rows returned by the app_tables.users.search function - the code below does not seem to update the Data Grid as expected:
I also know there’s an issue with my query as it is currently returning accounts which are in both orgs instead of either. One extra complexity is I actually only want to display the orgs which the current user is a member of, not all orgs the account is associated with… but one step at a time!
Code Sample:
# server code
@anvil.server.callable
def get_user_orgs():
user = anvil.users.get_user()
user_accounts = app_tables.accounts.search(account_orgs=q.any_of(user['user_orgs']))
return user_accounts
# form code
anvil.users.login_with_form()
user_accounts = anvil.server.call('get_user_orgs')
self.user_accounts_repeating_panel.items = user_accounts
Workaround
I have managed to achieve what I need to display by going back to Python primitives and constructing a dict for the table contents, but it is verbose and kind of ugly.
I also want to be able to write back to the data tables and I think that this workaround might make things unnecessarily complex down the track. Plus I also feel that understanding how to best work with LiveObjects (SearchIterators and Rows) would be really beneficial to getting the best out of Anvil.
I have constructed a minimal app to ringfence just this part of my functionality, any guidance would be hugely appreciated - thanks!
The account_orgs column of your accounts table contains orgs data table rows. The data grid is accordingly displaying a string representation of those orgs rows (“If you use a non-string value in the dictionary, the DataRowPanel calls str() on it before displaying it.” Anvil Docs | Data Grids) See the docs linked there for how to instead custom format the display of account_orgs in this data grid. For instance, you can drag a Label into that column in user_accounts_row_template and then add code (to the template) to set this Label’s text attribute to self.item['account_orgs']['org_name'].
(Alternatively, you could specify this in the server function, as it sounds like you have already tried. To accomplish the “one extra complexity,” I think you’ll have to do something similar–either client- or server-side. But someone else may have a better pattern to suggest. For instance, perhaps you could use something like an ORM, defining a portable class for orgs objects so that they would “know” that ‘org_name’ is their proper string representation. But I think you’d have to code that yourself–or else work from the anvilistas ORM developed by @owen.campbell.)
On the query issue, your code is currently only returning rows for which that column matches the list of orgs stored for that user. To get your desired result, “unpacking” the list using a preceding * should do the trick: q.any_of(*user['user_orgs']).
Thanks @hugetim, this is really helpful. I think I’ve now got my head around the structure returned by datatable queries, and unpacking the list was exactly what I needed here.
I guess one of my struggles with working with SearchIterators/Rows instead of Lists/Dicts is that I am finding it fairly opaque as to what they contain and where they come from. Do you know if there’s a quick way of e.g. printing the structure and/or contents without having to refer back to the search query, then the original datatable to understand the fields? Also (related), when e.g. an anvil.tables.Row LiveObject is returned, is there a quick mechanism for understanding where the Object came from?
I don’t think there is, generally, though one option is to convert to List/Dict and then inspect that. Searching around the forum, here’s one helpful post:
And here are a couple relevant Feature Requests:
You might want to add a Feature Request (which I would support) for making SearchIterators more transparent, though there might be some cost to doing that beyond my understanding. (Here’s one aspect of SearchIterators I was not clear on, for instance.)