Chapter 4:
Add news articles to the database

So far, we’ve put together an add/edit form with inputs relating to news articles.

The Add/Edit Form we created in the previous chapters.

It’s time to connect it up to our database and save articles that the user enters. This is the ‘Create’ part of our CRUD app.

The process will look like this:

  1. The user clicks the ‘Add an article’ button
  2. A popup appears, displaying the content of the ‘ArticleEdit’ Form
  3. The user fills in the inputs
  4. The user clicks either ‘Save’ or ‘Cancel’
  5. If the user clicks save, store the article. Otherwise, discard the article.

We’ll walk through these steps below.

Step 1: Display an alert when the user clicks 'Add an Article'

We want to display our ‘ArticleEdit’ Form when a user clicks the ‘Add an article’ button.

We’ll use Events for this.

Click on ‘Homepage’ in the App Browser to go back to your UI. Click on your ‘Add an article’ Button, and scroll to the bottom of the Properties Panel. You’ll see a list of events for the Button.

Click the blue arrows next to ‘click’.

Adding a click handler to the 'Add an article' Button from the Properties pane.

Configuring a click event handler from the Properties pane.

You will be taken to the Form Editor’s ‘Code’ view, where you’ll see an add_article_button_click method on your Form that looks like this:

def add_article_button_click(self, **event_args):
  """This method is called when the button is clicked"""
  pass

This is the Python method that runs when the ‘Add an article’ Button is clicked.

When someone clicks this button, we want to display a popup Form that users can fill out to save a new article. We do this using Alerts.

To display a simple alert, change your click handler to trigger an alert as follows:

def add_article_button_click(self, **event_args):
  # Display a popup that says 'You clicked the button'
  alert("You clicked the button")

To see it in action, click the ‘Run’ button at the top of the screen:

Location of the Run button

You’ll see your app running. Click the ‘Add an article’ button and your popup should appear!

An alert saying 'You clicked the button'.

Step 2: Put the ArticleEdit Form in the alert

We want our alert to display the ‘ArticleEdit’ Form. To do this, we first need to import the ArticleEdit Form inside our Homepage Form.

Add this line to the top of your ‘Homepage’ Form:

from ..ArticleEdit import ArticleEdit

We can now display the ‘ArticleEdit’ Form in our popup by customising the alert using Anvil’s custom popup styles. Set the content property of the alert to an instance of the ‘ArticleEdit’ Form, set the title property to “Add an article”, and set the large property to True:

  def add_article_button_click(self, **event_args):
    # Open an alert displaying the 'ArticleEdit' Form
    alert(
      content=ArticleEdit(),
      title="Add Article",
      large=True,
    )

When you run your app and click the button, you should get the ArticleEdit form in a popup box:

The ArticleEdit Form in an alert box.

Step 3: Add Save and Cancel buttons

We want to display two buttons on our popup: ‘Save’ and ‘Cancel’. We’ll Create new article if the user clicks ‘Save’, and discard the user inputs if they click ‘Cancel’.

We do this by setting the alert’s buttons property, which expects a list of buttons. Each button should be set to a 2-tuple. The first element of each tuple is is the text to display on the button, and the second element is the value returned if the user clicks the button.

Add a buttons property to the alert in your add_article_button_click function:

  def add_article_button_click(self, **event_args):
    # Open an alert displaying the 'ArticleEdit' Form
    alert(
      content=ArticleEdit(),
      title="Add Article",
      large=True,
      buttons=[("Save", True), ("Cancel", False)],
    )

Run your app, and click your ‘Add an Article button’. You’ll see the ArticleEdit Form in a popup, and notice that you have ‘Save’ and ‘Cancel’ buttons.

The alert from before, now with Save and Cancel buttons.

Step 4: Store the data entered in the form

Let’s make the form store the title, content, image and category into our ‘Articles’ table when the user hits ‘Save’.

When the information is first entered, we’ll store it in a Python dictionary. Then if the user hits ‘Cancel’, we can just discard the dictionary - nothing is saved in the Data Table.

We’ll store this dictionary in the item property of the ArticleEdit Form.

All Forms in Anvil have a property called self.item that can be accessed in the Form’s code. We’ll initialise our ArticleEdit Form with an empty dictionary as its self.item.

Edit your add_article_button_click function to look like this:

  def add_article_button_click(self, **event_args):
    # Initialise an empty dictionary to store the user inputs
    new_article = {}
    # Open an alert displaying the 'ArticleEdit' Form
    save_clicked = alert(
      content=ArticleEdit(item=new_article),
      title="Add Article",
      large=True,
      buttons=[("Save", True), ("Cancel", False)],
    )
    # If the alert returned 'True', the save button was clicked.
    if save_clicked:
      print(new_article)

It’s important to remember to change

ArticleEdit() to ArticleEdit(item=new_article)

This means self.item will start as an empty dictionary and we’ll update it as the user fills in the form.

We can update self.item using Data Bindings. You can find Data Bindings in the Properties panel for each component, near the top:

An empty Data Bindings list near the top of the Properties Panel.

Select the ArticleEdit Form. For each of the ‘name’, ‘content’, and ‘category’ inputs, add a Data Binding to self.item:

  • title_box: Bind the text property to self.item['title']
  • content_box: Bind the text property to self.item['content']
  • category_box: Bind the selected_value property to self.item['category']
adding data bindings to the ArticleEdit Form

For the FileLoader component, when the user uploads an image, we want to add it to self.item. To do this, we set an event handler on the FileLoader component which will run when the user uploads an image.

Double click the FileLoader component to create a method for the change event - the change event is raised when a user uploads a file. Its file argument is a Media object containing the chosen file:

adding a 'file_loader_change' event handler to the FileLoader component

Edit the image_uploader_change method to look like this:

  def image_uploader_change(self, file, **event_args):
    """This method is called when a new file is loaded into this FileLoader"""
    # Add the image to self.item
    self.item['image'] = file

Now run your app, click on ‘Add new article’, fill in all the fields, upload an image and click the ‘Save’ button.

You should now see your new_article dictionary printed in the Output Panel

Step 5: Save the data to the 'Articles' table

We’ve got the data in a Python dictionary - now we want to save it to the database.

We want to Create a new row in the ‘Articles’ table if the user clicks ‘Save’. We’ll write to the Data Table from inside a Server Module.

Anvil’s Server Modules are a full server-side Python environment. Server Modules cannot be edited or seen by the user, so we can trust them to do what we tell them. This is why we’ll use a Server Module to write to the Data Table. (Read more about our security recommendations for CRUD apps.)

Create a Server Module by clicking the ‘+’ next to Server Code in the App Browser. You’ll see some code with a yellow background:

Adding a Server Module

We’ll write a server function to add a new row to the ‘articles’ Data Table. We’ll use the add_row method to do this.

Add this function to your Server Module:

@anvil.server.callable
def add_article(article_dict):
  app_tables.articles.add_row(
    created=datetime.now(),
    **article_dict
  )

The @anvil.server.callable decorator allows us to call this function from client code.

We’re using the datetime library to set the ‘created’ column to the current date and time, so you’ll also need to import the datetime class at the top of your Server Module:

from datetime import datetime

The final step is to call this method from the client when the user chooses to save a news article.

We want to call our add_article server function when the button is clicked, and pass in the user inputs. We made our add_article server function available to our client-side code by decorating it as @anvil.server.callable.

This means we can call it from our client-side code using anvil.server.call('add_article', <arguments>)

Let’s call our add_article server function from our add_article_button_click function:

  def add_article_button_click(self, **event_args):
    # Initialise an empty dictionary to store the user inputs
    new_article = {}
    # Open an alert displaying the 'ArticleEdit' Form
    save_clicked = alert(
      content=ArticleEdit(item=new_article),
      title="Add Article",
      large=True,
      buttons=[("Save", True), ("Cancel", False)],
    )
    # If the alert returned 'True', the save button was clicked.
    if save_clicked:
      anvil.server.call('add_article', new_article)

If the user clicks ‘Cancel’, save_clicked will be False, so the data in our new_article dictionary will be discarded.

Run your app again, click the ‘Add an article’ button, fill in the inputs, and click Save. Now, if you go to the ‘articles’ table in your Data Table, you’ll see the article in the Data Table.

Filling in the ArticleEdit form and checking that the data I entered is in the Data Table.

Great! We can now add news articles to our Data Table.

That’s the ‘C’ in our CRUD app, and the bulk of the work is out of the way - the Read, Update and Delete will be plain sailing now.

In Chapter 5, we’ll read the list of stored items and display it to the user. That’s the ‘R’ in our CRUD app.

Chapter complete

Congratulations, you've completed this chapter!