Database Row Portable Class with Extra Information

I wanted to return a database Row to the client, but also manipulate some column values temporarily just for that client session, so I made a portable class that allows me to do that and still get the row’s id normally:

import anvil.server
import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables

@anvil.server.portable_class
class DbRow():
  def __init__(self, _db_row: tables.Row, extra_info: dict=None):
    self._db_row = _db_row
    self.extra_info = extra_info
  
  def __getitem__(self, column_key):
    try:
      return self._db_row[column_key]
    except tables.NoSuchColumnError:
      try:
        return self.extra_info[column_key]
      except (KeyError, TypeError):
        raise tables.NoSuchColumnError(f"No such column '{column_key}'")

  def get_id(self):
    return self._db_row.get_id()
1 Like

I like it!

A similar approach could be used to create a mutable object for use with write back data bindings that you could then pass back to the server to finalize the updates to the data table.

1 Like

Good thought!

import anvil.server
import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables

@anvil.server.portable_class
class DbRow():
  def __init__(self, db_row: tables.Row, extra_info: dict=None):
    self._db_row = db_row
    self._extra_info = extra_info or {}
    self._update_info = {}
  
  def __getitem__(self, column_key):
    try:
      return self._db_row[column_key]
    except tables.NoSuchColumnError:
      try:
        return self._extra_info[column_key]
      except KeyError:
        raise tables.NoSuchColumnError(f"No such column '{column_key}'")

  def __setitem__(self, column_key, value):
    if column_key in self._db_row:
      self._update_info[column_key] = value
    else:
      self._extra_info[column_key] = value

  def get_id(self):
    return self._db_row.get_id()

  def update(self, update_data: dict=None, update_db_now=True):
    if update_data:
      self._update_info = {**self._update_info, **update_data}
    if update_db_now and self._update_info:
      self._db_row.update(self._update_info)
      self._update_info = {}

1 Like

getitem would also need to check to see if the data was in update_info before checking the db_row, right?

I considered it, and I don’t think its necessarily wrong, but for my implementation at the current moment I want to just get the current database value until I have actually sent the update.

Aaaaand I almost immediately found a use for that functionality, though only conditionally:

import anvil.server
import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables


@anvil.server.portable_class
class DbRow:
    def __init__(
        self, db_row: tables.Row, extra_info: dict = None, use_updated_values=False
    ):
        self._db_row = db_row
        self._extra_info = extra_info or {}
        self.use_updated_values = use_updated_values
        self.update_info = {}

    def __getitem__(self, column_key):
        if self.use_updated_values:
            try:
                return self.update_info[column_key]
            except KeyError:
                # Getting values from row or extra info already handled
                pass
        try:
            return self._db_row[column_key]
        except (tables.NoSuchColumnError, TypeError):
            try:
                return self._extra_info[column_key]
            except KeyError:
                raise tables.NoSuchColumnError(f"No such column '{column_key}'")

    def __setitem__(self, column_key, value):
        if column_key in self._db_row:
            self.update_info[column_key] = value
        else:
            self._extra_info[column_key] = value

    def get_id(self):
        return self._db_row.get_id()

    def update(self, update_data: dict = None, update_db_now=True):
        if update_data:
            self.update_info = {**self.update_info, **update_data}
        if update_db_now and self.update_info:
            self._db_row.update(self.update_info)
            self.update_info = {}