Python Corner: Anvil scenarios with pythonic solutions, inspired by the Python Cookbook

Problem:

You have two table rows from different tables and you want to know which keys, values are common to both.

Solution:

Consider two tables with the following fields:

  • table_1
    • id: number
    • name: string
    • age: number
    • address: string
  • table_2
    • id: number
    • name: string
    • age: number
    • height: number
    • weight: number

the row with id 1 represents the same person and has the following details:

// table_1
{
    "id": 1,
    "name": "John Doe",
    "age": 32,
    "address": "1 Willow Road, Cambridge, UK, CB3 9ET"
}

// table_2
{
    "id: 1,
    "name: 'John Doe'
    "age: 34
    "height: 182
    weight: 73.5
}
row_1 = app_tables.table_1.get(id='1')
row_2 = app_tables.table_2.get(id='1')

common_keys = dict(row_1).keys() & dict(row_2).keys()
# {'id', 'name', 'age'}

keys_in_table_1_not_in_table_2 = dict(row_1).keys() - dict(row_2).keys()
# {'address'}

common_items = dict(row_1).items() & dict(row_2).items()
# { ('id', 1), ('name', 'John Doe')}

Discussion

This feature is little known in python. dict.keys() and dict.items() are set like and you can perform set operations on them. You can even perform set operations with dict.keys() objects and actual set objects.

The above problem/solution may same like a trivial example at first but there is a very specific Anvil use case.

Let’s say you want to update a table_row from a form’s item property. This is a very common implementation in Anvil.

News Aggregator: Update existing articles

@anvil.server.callable
def update_article(article, article_dict):
    # check that the article given is really a row in the ‘articles’ table
    if app_tables.articles.has_row(article):
        article_dict['updated'] = datetime.now()
        article.update(**article_dict)
    else:
        raise Exception("Article does not exist")

The code above assumes that article, the row, and article_dict, have the same keys. But I’ve often run into situtations when it’s convenient to have extra keys in article_dict and the above code would result in an attribute error for the extra keys.

Knowing dict.keys() is set like we can safeguard the above solution against extra keys.

@anvil.server.callable
def update_article(article, article_dict):
    # check that the article given is really a row in the ‘articles’ table
    if app_tables.articles.has_row(article):
        article_dict['updated'] = datetime.now()
        article_row_keys = dict(article).keys()
        article.update(**{key: article_dict[key] for key in article_dict.keys() if key in article_row_keys})
    else:
        raise Exception("Article does not exist")

Another similar operation would be creating a new table row rather than updating an existing row.

@anvil.server.callable
def add_article(article_dict):
    columns = app_tables.articles.list_columns() # columns is a list of dicts
    col_names = {col['name'] for col in columns} # set of column names
    row = app_tables.articles.add_row(**{ key: aritcle_dict[key] for key in article_dict if key in col_names })
    return row