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:
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.
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
- 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.
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
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.
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’.
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.
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.
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
@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.
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 for details.
You can also use the following link to clone the finished app in the Anvil editor, and explore it yourself: