Chapter 3:
Add, Edit and Delete your items from your app
You’ve successfully connected to your external database from a web app! That’s the core mission of this tutorial accomplished.
We’ll go a little further by building a UI to edit the contents of the inventory table. We’ll also create some input components to add and delete rows.
Step 1: Create a 'refresh' method
We want to be able to refresh the table at any time, so we can show the user what happens when they create or update rows.
Here’s the line we’ve been using to fetch the inventory items from the database:
self.repeating_panel_1.items = anvil.server.call('get_items')
Remove this line from __init__
and put it in a method of its own:
def refresh(self, **event_args):
self.repeating_panel_inventory.items = anvil.server.call('get_items')
Now call your new method from __init__
:
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
# Any code you write here will run before the form opens.
self.refresh()
Let’s also create an event to trigger a refresh. That makes it easier to trigger a refresh from outside this Form.
At the end of __init__
, add the following line to bind an event that triggers a refresh:
# At the end of __init__:
self.set_event_handler('x-refresh', self.refresh)
Re-run your app: you should still see your database rows displayed in the table.
Step 2: Write a function to add items
Let’s make our app add items to the database. We’ll start with the back-end.
In inventory.py
, add this simple function to run an INSERT
statement against the external database:
@anvil.server.callable
def insert_item(name, quantity):
cur.execute('''
INSERT INTO inventory (item_name, quantity)
VALUES (%s, %s);
''',
[name, quantity])
Why don’t we just use Python format strings to insert the values?
Psycopg2’s cursor.execute
method allows you to construct queries from Python objects in a way
that protects against SQL injection. That prevents users from taking over our database by using SQL statements as variables.
Step 3: Build the UI to add items
Now we need to run that insert_item
function from the web app. Let’s build an ‘add’ widget into the Data Grid.
Add a column to your Data Grid. Clear its Title and Key. Set its width to 80. This will hold our ‘add’ button later.
Add a Data Row Panel to the bottom of your Data Grid.
Drop a TextBox into each of the Name and Quantity columns. Rename them text_box_name
and text_box_quantity
. Set the Quantity TextBox’s type
to number
.
Drop a Button into the end column. Rename it button_add
. Clear the Button’s text
and set its icon
to fa:plus
.
Create a click
handler by clicking the blue arrows to the right of ‘click’ in the Events section:
def button_add_click(self, **event_args):
anvil.server.call(
'insert_item',
self.text_box_name.text,
self.text_box_quantity.text
)
# Refresh the open Form to load the new item into the UI
get_open_form().raise_event('x-refresh')
# Clear the input boxes
self.text_box_name.text = ''
self.text_box_quantity.text = ''
Now you can add items to your database from your web app!
Step 4: Write a function to update items
Next, we’ll update rows in the external database from the web app.
We need to write another function in inventory.py
- this time we’ll do an UPDATE
:
@anvil.server.callable
def update_item(item_id, name, quantity):
cur.execute('''
UPDATE inventory
SET item_name = %s, quantity = %s
WHERE id = %s;
''',
[name, quantity, item_id])
Step 5: Build the UI to update items
Now back to the Anvil Editor. We’ll modify each line of the table to contain input components so the user can change the items that already exist.
Double-click on the Repeating Panel in your Data Grid and drop TextBoxes into the Quantity and Name columns.
Call them text_box_name
and text_box_quantity
respectively.
Set the Quantity TextBox to be type number
.
These TextBoxes are going to display the values we read from the database when we refresh
.
Since we are inside a Repeating Panel, we can access the values for each row as self.item
(see the docs on Repeating Panels for more information).
Set up a Data Binding for each TextBox to get the relevant value into its text
property. Be sure to
uncheck ‘Write back’.
Now go to the code for RowTemplate1.
Write a method to call when the values are changed:
def update_item(self, **event_args):
anvil.server.call(
'update_item',
self.item['id'],
self.text_box_name.text,
self.text_box_quantity.text,
)
Bind this method to the pressed_enter
and lost_focus
events of each TextBox:
Now run your app. You’ll find your table is full of TextBoxes that update the database every time they are modified.
Step 6: Write a function to delete items
To delete items in the external database, we do exactly the same thing we’ve done for inserting, selecting
and updating - write a one-line function in inventory.py
with a simple SQL statement:
@anvil.server.callable
def delete_item(item_id):
cur.execute('''
DELETE FROM inventory WHERE id = %s;
''',
[item_id])
Step 7: Build the UI to delete items
Double-click on the RepeatingPanel inside the Data Grid. Drag-and-drop a Button into the end column.
This will be the delete button for each item. Rename it button_delete
. Clear the Button’s text, set its icon to fa:times
and the foreground colour to theme:Secondary 500
.
Now create a click
event handler for your new delete button by clicking the blue arrows to the right of ‘click’ in the Events section:
def button_delete_click(self, **event_args):
"""This method is called when the button is clicked"""
anvil.server.call('delete_item', self.item['id'])
get_open_form().raise_event('x-refresh')
Run your app - you can now delete items from your external database using your web app.
Nice work! You’ve not only connected your web app to an external database, you’ve also given the UI functionality to add, edit and delete items in that database.
You can do a lot more using the Uplink
Using an Uplink script to connect to systems outside of Anvil is very powerful. You can generalise this approach to solve a whole host of problems.
For example, you could use the Uplink to run machine learning models on your own cluster and return the results to Anvil (and use a Background Task to do this asynchronously). Or you could use an Uplink script as a gateway to communicate with IoT devices inside your network.
Anything you can do with Python, you can control with an Anvil app via the Uplink.
For a comprehensive view of what the Uplink can do, check out the Uplink section of the reference docs: