Client-Writable Models
It’s convenient to express all your UI logic in Forms, without having to create a server function corresponding to every action. However, this needs to be done securely: Forms are untrusted client-side code, so we must perform server-side validation.
With client-writable models, client-code can update rows directly, create new rows, and delete existing rows. All of these operations go via the server, where validation and permission checks can run in a trusted environment before the changes are applied to the database.
Making a model client-writable
When creating a model class, you can add options to the superclass that allow instances of your model to be created, updated and/or deleted directly from Form code.
There are four different options:
client_updatable=True
: client code can update rows directlyclient_creatable=True
: client code can create new rowsclient_deletable=True
: client code can delete existing rowsclient_writable=True
: equivalent to setting the three above options toTrue
If any of the above options are set to True
, you’ll need to implement permission checks to ensure that only permitted operations can occur. For example, if client_updatable
is set to True
, you’ll want to override do_update()
to check that the logged-in user has permission to edit this row before doing the update.
For example:
class TodoItem(app_tables.todo_items.Row, client_updatable=True):
def _do_update(self, updates, from_client):
if 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")
updates['last_updated'] = datetime.now()
super()._do_update(updates, from_client)
client_writable()
views when you have defined a client-writable model. This setting will allow untrusted client code to save changes directly to the database and will override any permission checks and may cause errors in the future.How it works
When the client_[X]able
option is set for a particular operation, it will cause that operation to be redirected to your server-side Python code. This will execute the corresponding _do_[X]()
method on the server. If the default _do_[X]()
function is called (eg via calling super()._do_update()
), then the operation will be sent to the Anvil database and executed.
For example, if we have a row with a client-updatable model, and we update the 'name'
column to the value 'foo'
, the following operations might execute:
-
Client-side code runs
row['name'] = 'foo'
to update the value of the'name'
column. -
The update is sent to the app’s server code.
-
In the app’s server side environment, the model’s
_do_update()
function on the model is called.- The
updates
argument is the dictionary{'name': 'foo'}
. - The
from_client
argument isTrue
.
- The
-
The model’s
_do_update()
function decides that this update is permitted, and callssuper()._do_update({'name': 'foo'}, True)
. -
The superclass
_do_update()
sends the update to the Anvil database.
Do you still have questions?
Our Community Forum is full of helpful information and Anvil experts.