Checks and Validation
When defining the data structure for your application, it is common to want to define rules about your data and what happens when it changes. These include:
-
Validation, e.g. “This column must be a number between 1 and 10”
-
Permissions, e.g. “Only administrators may change the value of this column”
-
Side effects, e.g. “Every time we update a row from this table, we set its
last_updated
column to the current time”
Model classes allow you to express these constraints in code.
Low-level API
A model class may override the methods _do_update()
, _do_delete()
and/or _do_create()
to intercept updates, deletion and draft creation. To complete the action, call the superclass method.
_do_update()
The _do_update()
method is called when column values are updated by assigning to a column, calling the update()
method, or saving buffered changes to a row.
It takes two positional arguments:
-
updates
: A dictionary of column names to new values, representing the requested changes -
from_client
: A boolean value, true if the request was initiated by the client. (This is useful when creating client-writable models, in which case_do_update()
will run on the server even if the change was initiated by client code.)
_do_delete()
The _do_delete()
method is called when row deletion is requested with the delete()
method. It takes one positional argument:
from_client
: A boolean value, true if the request was initiated from the client.
_do_create()
The _do_create()
class method is called when saving a draft row. It takes two positional arguments:
-
values
: A dictionary of column names to values representing the requested changes -
from_client
: A boolean value, true if the request was initiated from the client.
_do_create()
should be a @classmethod
, as it does not refer to an existing instance.Example
This code extends the previous example to add validation, add permission checks and automatically update the last_created
column with the current time whenever the row is changed:
class TodoItem(app_tables.todos.Row):
def _do_delete(self, from_client):
me = anvil.users.get_user()
if not me:
raise Exception("Must be logged in to delete records")
if self["assigned_user"] != me:
raise Exception("Only the assigned user can delete a record")
super()._do_delete(from_client)
def _do_update(self, updates, from_client):
me = anvil.users.get_user()
if me is None:
raise Exception("Must be logged in to edit records")
if self["assigned_user"] != me:
raise Exception("Must be the owner of this item to update it")
if "title" in updates and not update["titles"]:
raise Exception("Must specify a title")
updates['last_updated'] = datetime.now()
super()._do_update(updates, from_client)
@classmethod
def _do_create(cls, values, from_client):
me = anvil.users.get_user()
if me is None:
raise Exception("Must be logged in to do this")
if not values.get("title", ""):
raise Exception("Must specify a title")
values["assigned_user"] = me
values['last_updated'] = datetime.now()
return super()._do_create(values, from_client)
Do you still have questions?
Our Community Forum is full of helpful information and Anvil experts.