Flask by example 7 (Spin up an Admin dashboard quickly and easily with Flask-Admin)
Welcome to part 7 of this series, in this part we’re going to build an admin dashboard for our application which would provide basic CRUD
functionality for the models in our database.
Go ahead and install flask admin from pip
It’s very easy to use flask-admin, just import the Admin
class, create a new object with it, and pass in our flask application object as the first parameter, the name argument is the name that’s displayed on the admin homepage, it defaults to the application name.
After that you can add different models by by creating ModelView
objects.
Our Model objects would show up as different menus in the admin dashboard.
Add the following to votr.py
reload the page and open localhost:5000/admin
You should see a a blank page that tells you Your “Login was successful” and a plain navbar that looks like this
You can add your models and play around the the admin. create, delete and modify records as you please
Adding authentication to flask admin
After playing around with flask admin, you’d have noticed that something very important is missing…Authentication
Now, there are several ways of adding authentication to flask admin, but I’m going to show you how to integrate our current Authentication system with flask admin.
First of all, it would make more sense to keep all our “admin stuff” in a separate file in order to keep the main application file clean. Let’s call that admin file admin.py
Flask admin provides a way of “hiding” models from users based on any condition we like, this is very useful in building role-based admin interfaces, For example a cashier might have access to the post transactions but he should not be able to see the total transactions posted for the day (Which contains other entries posted by other cashiers), Only the manager should be able to see that
To add this feature, we have to inherit the ModelView
class and override two methods
place the following in the admin.py file
The is_accessible method determines if a model is accessible or not, based on a particular condition, in our case we only want to show the model to the current user, if they’re logged in and their username equals “Administrator”
In the inaccessible_callback method, we’re simply saying if the model is not accessible redirect the user back to the homepage. The next
argument is used to take the admin user back to the last page they tried to access.
To make use of this parameter, just change the return statement in your login route to:
and the action url of the login form on the homepage:
To use our AdminView
class, we have to tell Flask-Admin about it in votr.py.
The important line here is:
with the index_view
argument, we’re telling flask admin that we want the Topics model to serve as the default view for our application instead of the blank homepage you saw earlier, the url parameter changes the url for the topic model to /admin
instead of the default which was /admin/topics
Finally we’re setting the endpoint
to admin
That’s all!, with this information flask admin is able to build the blueprints with our models (Internally flask admin uses introspection to get the details about the models passed to it and creates blueprints on the fly)
The admin page should also be protected from users whose username doesn’t equal Administrator. Our username field is actually unique, so this means that we can only have one admin user.
To create other admin users, you can add more usernames to check for in the is_accessible
method or better still add a boolean field to your user model to determine if the user is an admin user or not. I’ll leave you to your imagination here.
Protecting the polls from multiple votes
In the last part, we talked about adding a new feature that prevents users from voting multiple times on a poll. To do this we’ll have to add a new model to track a user and the polls they’ve has voted on, and then on every request to /api/vote
we can simply check if they voted on that poll before and then abort the request with a custom message or allow the request if they haven’t.
Add a new model called UserPolls in votr.py
after doing that run the migrations and upgrade the database
IMPORTANT: before running the migrations, make sure you comment out the db.create_all
in votr.py
if you don’t SQLAlchemy, would create the new table from you and Alembic won’t be able to detect any change in the database schema when you try to run the database migration
Modify the /api/vote endpoint to incorporate the new changes
That’s all, a user shouldn’t be able to vote on a poll more than once, if they try that they should see a popup in their browser telling them that “multiple votes are not allowed”.
Customizing Flask-Admin
Out of the box, flask admin gives us a pretty usable interface and reasonable defaults, but that doesn’t mean it’s not flexible. flask admin allows us make different enhancements ranging from ui enhancements (display order, theming) to behavioural enhancements. (search boxes, filters).
We’re going to add some new features to the admin page:
- Change the order in which the columns are displayed for the Topics model
- A search box to search for poll by their title
- A filter on the status field so we can see all open or closed polls at any point in time
- Make the date field for Topics sortable
- Change the default date format to something more readable
- Display the total vote count for each topic and make the topics sortable with it
The first four options added easily
column_list: This is used to list the various columns in the order you want them.
column_searchable_list: Columns that you want to be searchable in the model.
column_default_sort: The column that the view should be sorted with by default (when the view is loaded for the first time). The second parameter True
tells flask-admin to sort it in descending order.
column_filters: List of columns that can be used to filter.
Notice that we created a new class TopicView
that inherits from AdminView
, so let’s change the index_view
argument for the Admin
object in votr.py to use the new class.
That’s it, reload the admin page and you should see the new changes in effect
With some simple variables, we’ve been able to add some customization to our admin interface.
The last two customizations are not as straight-forward, but they aren’t too complex either
The following code is used to change the date format
We put it in the constructor of AdminView
because we want to re-use this date format in all our models including Topics
which already inherits from AdminView
You should also note that we can define flask admin variables as class variables like we did for the first four customizations or as instance variables like we’re doing with self.column_type_formatters
. so if you want other models to inherit a customization you can create the property as an instance variable.
column_formatters
expects a dictionary of Object types as the key and the corresponding display value we want as the value
typefmt.BASE_FORMATTERS
provides sane default formats for the various types of fields our model has. This list includes formatters for lists
, dicts
, bools
and other python data types.
We simply updated the dictionary with our own custom format for datetime objects and left the rest to flask admin to handle.
To accomplish the last improvement, We have to make use of a feature in SQLAlchemy called hybrid_property
A hybrid_property
can be thought of as a computable column. At the database layer, the column doesn’t exist but we can use it almost like any other field in our model.
Let’s modify the Topics
model in the models.py file
The hybrid_property
decorator tells SQLAlchemy that we want to use the return value of total_vote_count
as a column of the model not just a plain property of the class
The key part of the hybrid_property is the the decorator @total_vote_count.expression
. Depending on how complex the hybrid_property
is and how we compute the values, we might need a hybrid_property
expression (For simple computed values based on fields of the model it’s not necessary, flask-admin can figure out how to sort the column)
The expression must return SQL
that would be used by flask-admin to sort the column properly, if you don’t need the sorting you can just leave out the expression part.
So in this case we’re returning SQL that’s similar to this:
So with that information, flask-admin would be able to sort the Topics by the total number of votes they have.
Don’t forget to add this to the TopicView
class in admin.py
and modify colum_list
to show the new column
Note: The to_json
method of the model has been modified to use the new hybrid property
We’ve come to the end of this part, this post was a gentle introduction to flask-admin and what you can do with it. you also saw how you can restrict each user to a single vote on a poll.
Everything about flask admin is customizable, to keep this tutorial brief i didn’t talk about customizing the templates but if you’re wondering how “customizable” flask admin templates are here are two screenshots of an admin dashboard built with flask admin.
There’s a so much to explore. Redis CLI is even integrated with flask admin!.
Now that you know about flask admin, try to resist the urge to use it for your general/public user’s admin dashboard. If you know 101% of what you’re doing, there’s actually nothing wrong with using it, but have it at the back of your head that you’ll probably spend more time customizing flask admin compared to the time you’ll spend rolling your own admin dashboard from scratch.
You also run the risk of users gaining access or seeing things they aren’t supposed to see.
Flask admin is more suitable for admins or users that you can trust
In the next part, we’re going to talk about flask blueprints, when and why you should use them in your flask application. see you there!