Supporting multiple users

At some point, it’s likely that you’ll want to extend your apps to allow multiple users to log in and manage their own data. You want to show them their data, and nobody else’s. In this tutorial, we show how straightforward this is with Anvil.

We start with the app from the News Aggregator tutorial, and turn it into a multi-user app, complete with login form:

You don’t need to have completed the News Aggregator tutorial to do this tutorial.

However, you may want to complete one of the ‘Start Here’ tutorials if you haven’t yet.

In this tutorial, you’ll learn how to:

  1. Add the users service
  2. Link news articles to users
  3. Add user permissions

Click the following link to clone the news aggregator app to use as a starting point.

Or, click the following link to clone the finished multi-user app and explore it yourself.


Chapter 1

Users Service

We’ll use Anvil’s built-in Users Service to manage signup and login with a single line of Python.

The Users Service presents a signup/login form, and automatically stores usernames and password hashes in a Data Table.

There are various sign-in options in the Users Service, and you can enable any or all of them:

  • Username and password
  • Google
  • Facebook
  • Microsoft Azure AD and Office 365

And if you have your own Anvil instance you can also use:

  • Local Active Directory
  • X.509 certificates

We also offer integrations with a number of other on-site authentication systems.

Add the Users Service

Let’s start by adding the Users Service.

In the panel on the left (the App Browser), click the ‘+’ next to ‘Services’, then select the Users service.

You’ll see a screen which allows you to select from the available authentication methods, and provides you with some configuration options. For now, let’s choose the following:

Supported sign-in methods:

  • Email + password

New user registration:

  • Allow visitors to sign up
  • New user accounts can be used right away

Your setup should look something like this:

Display the login form

We want our users to log in or sign up when the app loads.

Go to ‘Code’ view of your ‘Homepage’ Form, and add this line to the Form’s __init__ method, underneath the self.init_components(**properties) line:

anvil.users.login_with_form()

Run your app, and you’ll see the login form pop up. Click ‘Sign up for a new account’, and fill in the boxes. (Remember, we aren’t requiring users to confirm their email addresses, so you can use dummy email addresses for testing).

You’ll see some print statements in the Output panel after you sign up - the Users Service has automatically created a ‘Users’ Data Table for you, and added a new row to the table!

Stop your app, click on ‘Data Tables’ in the App Browser, and select the ‘Users’ Table from the tabs at the top. You’ll see your sign in information has been stored in the table:

That’s the Users Service set up and ready to go. Nice work! On to Chapter 2.


Chapter 2

Linking news articles to users

We’re going to change the list of news articles displayed on the Homepage, depending on who is logged into our app. We want to show each user the articles they added, and nobody else’s.

To do this, we create a link between our ‘articles’ and ‘Users’ Data Tables. Each time a user adds a new article, we’ll store their user information in the ‘articles’ Data Table.

To link your tables together, click on ‘Data Tables’ in the App Browser, and select the ‘articles’ Table in the tabs at the top. Click the ‘+’ to add a new column, then follow ‘Link to table…’, ‘Users’, and select ‘Single Row’. Call this ‘user’.

Creating a link to the users table

Next, we want to store the logged in user in this ‘user’ column, whenever we add a news article to the database.

To get the currently logged-in user, we can call anvil.users.get_user(). This returns the currently logged-in user, or None if no user is logged in.

Click on ‘ServerModule1’ in the App Browser.

Let’s modify the add_article function to store the currently logged-in user in our ‘articles’ Data Table. Update your add_article function to look like this:

@anvil.server.callable
def add_article(article_dict):
  # Get the logged in user
  current_user = anvil.users.get_user()

  # Check that someone is logged in
  if current_user is not None:
    app_tables.articles.add_row(
	  created=datetime.now(),
	  # Store the logged in user in the 'user' column
	  user=current_user,
	  **article_dict
	)

Run your app again, log in and add a new article. Then stop your app, and go back to your Data Tables. You’ll see your new article row now contains a link to a record in the ‘Users’ table.

Nice work – we’re now linking new articles to the user that’s logged in! Time for Chapter 3.


Chapter 3

Add user permissions

Next, let’s change the list of news articles displayed on the Homepage. We’ll display only those articles that belong to the logged in user.

Go back to ‘ServerModule1’. Our get_articles function returns a list of news articles to our client-side code. So, we’ll modify this function to return the articles that belong to the logged-in user.

First, we’ll check that someone is logged in. Then, we’ll only return those articles that belong to the logged in user, using Anvil’s search query operator.

Update your get_articles function to look like this:

@anvil.server.callable
def get_articles():
  # Get the logged in user
  current_user = anvil.users.get_user()

  # Check that someone is logged in
  if current_user is not None:
    # Get a list of articles that belong to the logged-in user,
    # sorted by 'created' column, in descending order
    return app_tables.articles.search(
      tables.order_by("created", ascending=False),
      user=current_user
    )

Run your app again, and you’ll notice that you can only see the news articles that belong to you.

Next, we’ll amend the server functions that allow users to update and delete news articles. We’ll make sure that users can only update or delete articles that belong to them.

In your Server Module, create a new server function called verify_user_permissions(). This will check that someone is logged in and that the article they’re trying to update or delete belongs to them:

def verify_user_permission(article):
  current_user = anvil.users.get_user()
  # Check that someone is logged in
  if current_user is not None:
    # Check if the article to be updated does exist in the Data Table
    # Check that the article belongs to the logged in user
    if app_tables.articles.has_row(article) and article['user'] == current_user:
      return True

Next, update the update_article function to look like this:

@anvil.server.callable
def update_article(article, article_dict):
  if verify_user_permission(article):
    # Set the 'updated' property to datetime.now()
    article_dict['updated'] = datetime.now()
    article.update(**article_dict)
  else:
    # Raise an exception if the article doesn't exist in the Data Table
    # or the user doesn't own the article being updated
    raise Exception("Article does not exist or does not belong to this user")

Let’s add the same logic to our delete_article function:

@anvil.server.callable
def delete_article(article):
  if verify_user_permission(article):
    # Delete the article
    article.delete()
  else:
    # Raise an exception if the article doesn't exist in the Data Table
    # or the user doesn't own the article being deleted
    raise Exception("Article does not exist or does not belong to this user")

We’re now making sure that users can see, update and delete articles that belong to them. All our articles are stored in the same Data Table, but each user can only access their own articles.


That’s it!

And that’s it. You’ve just upgraded a database-backed web app so it can be used by many different users!

Your app is already live on the internet. Go to Publish App in the Gear Menu gear icon for details.

You can also use the following link to clone the finished app in the Anvil editor, and explore it yourself:


What next?

Head to the Anvil Learning Centre for more tutorials, or head to our examples page to learn how to build more complex apps in Anvil.