This tutorial is out of date and no longer maintained.
Note: This is Part Two of a three-part series. Build a CRUD Web App With Python and Flask - Part One Build a CRUD Web App With Python and Flask - Part Two Build a CRUD Web App With Python and Flask - Part Three
This is Part Two of a three-part tutorial to build an employee management web app, named Project Dream Team. In Part One of the tutorial, we set up a MySQL database using MySQL-Python and Flask-SQLAlchemy. We created models, migrated the database, and worked on the home
and auth
blueprints and templates. By the end of Part One, we had a working app that had a homepage, registration page, login page, and dashboard. We could register a new user, log in, and log out.
In Part Two, we will work on:
We’ll start by creating an admin user through the command line. Flask provides a handy command, flask shell
, that allows us to use an interactive Python shell for use with Flask apps.
Output>>> from app.models import Employee
>>> from app import db
>>> admin = Employee(email="admin@admin.com",username="admin",password="admin2016",is_admin=True)
>>> db.session.add(admin)
>>> db.session.commit()
We’ve just created a user with a username, admin
, and a password, admin2016
. Recall that we set the is_admin
field to default to False
in the Employee
model. To create the admin user above, we override the default value of is_admin
and set it to True
.
Now that we have an admin user, we need to add a view for an admin dashboard. We also need to ensure that once the admin user logs in, they are redirected to the admin dashboard and not the one for non-admin users. We will do this in the home
blueprint.
Next, we’ll create the admin dashboard template. Create an admin_dashboard.html
file in the templates/home
directory, and then add the following code to it:
Now we need to edit the base template to show a different menu for the admin user.
In the menu above, we make use of the current_user
proxy from Flask-Login to check whether the current user is an admin. If they are, we display the admin menu which will allow them to navigate to the Departments, Roles, and Employees pages. Notice that we use #
for the links in the admin menu. We will update this after we have created the respective views.
Now run the app and log in as the admin user that we just created. You should see the admin dashboard:
Let’s test the error we set in the home/views.py
file to prevent non-admin users from accessing the admin dashboard. Log out and then log in as a regular user. In your browser’s address bar, manually enter the following URL: http://127.0.0.1:5000/admin/dashboard
. You should get a 403 Forbidden
error. It looks pretty boring now, but don’t worry, we’ll create custom error pages in Part Three!
Now we’ll start working on the admin
blueprint, which has the bulk of the functionality in the application. We’ll begin by building out CRUD functionality for the departments.
We’ll start with the admin/forms.py
file, where we’ll create a form to add and edit departments.
The form is pretty simple and has only two fields, name
and department
, both of which are required. We enforce this using the DataRequired()
validator from WTForms. Note that we will use the same form for adding and editing departments.
Now, let’s work on the views:
We begin by creating a function, check_admin
, which throws a 403 Forbidden
error if a non-admin user attempts to access these views. We will call this function in every admin view.
The list_departments
view queries the database for all departments and assigns them to the variable departments
, which we will use to list them in the template.
The add_department
view creates a new department object using the form data and adds it to the database. If the department name already exists, an error message is displayed. This view redirects to the list_departments
. This means that once the admin user creates a new department, they will be redirected to the Departments page.
The edit_department
view takes one parameter: id
. This is the department ID and will be passed to the view in the template. The view queries the database for a department with the ID specified. If the department doesn’t exist, a 404 Not Found
error is thrown. If it does, it is updated with the form data.
The delete_department
view is similar to the edit_department
one, in that it takes a department ID as a parameter and throws an error if the specified department doesn’t exist. If it does, it is deleted from the database.
Note that we render the same template for adding and editing individual departments: department.html
. This is why we have the add_department
variable in the add_department
view (where it is set to True
), as well as in the edit_department
view (where it is set to False
). We’ll use this variable in the department.html
template to determine what wording to use for the title and heading.
Create a templates/admin
directory, and in it, add a departments
directory. Inside it, add the departments.html
and department.html
files:
We’ve created a table in the template above, where we will display all the departments with their name, description, and the number of employees. Take note of the count()
function, which we use in this case to get the number of employees. Each department listed will have an edit and delete link. Notice how we pass the department.id
value to the edit_department
and delete_department
views in the respective links.
If there are no departments, the page will display “No departments have been added”. There is also a button that can be clicked to add a new department.
Now let’s work on the template for adding and editing departments:
Notice that we use the add_department
variable which we initialized in the admin/views.py
file, to determine whether the page title will be “Add Department” or “Edit Department”.
Add the following lines to your style.css
file:
The .middle
, .inner
, and .outer
classes are to center the content in the middle of the page.
Lastly, let’s put the correct link to the Departments page in the admin menu:
Re-start the flask server, and then log back in as the admin user and click on the Departments link. Because we have not added any departments, loading the page will display:
Let’s try adding a department:
It worked! We get the success message we configured in the add_department
view, and can now see the department displayed.
Now let’s edit it:
Notice that the current department name and description are already pre-loaded in the form. Also, take note of the URL, which has the ID of the department we are editing.
Editing the department is successful as well. Clicking the Delete link deletes the department and redirects to the Departments page, where a confirmation message is displayed:
Now to work on the roles. This will be very similar to the departments code because the functionality for roles and departments is exactly the same.
We’ll start by creating the form to add and edit roles. Add the following code to the admin/forms.py
file:
Next, we’ll write the views to add, list, edit, and delete roles. Add the following code to the admin/views.py file:
These list, add, edit, and delete views are similar to the ones for departments that we created earlier.
Create a roles
directory in the templates/admin
directory. In it, create the roles.html
and role.html
files:
Just like we did for the departments, we have created a table where we will display all the roles with their name, description, and the number of employees. Each role listed will also have an edit and delete link. If there are no roles, a message of the same will be displayed. There is also a button that can be clicked to add a new role.
We use the add_role
variable above the same way we used the add_department
variable for the department.html
template.
Once again, let’s update the admin menu with the correct link:
Re-start the server. You should now be able to access the Roles page, and add, edit and delete roles.
Now to work on listing employees, as well as assigning them departments and roles.
We’ll need a form to assign each employee a department and role. Add the following to the admin/forms.py
file:
We have imported a new field type, QuerySelectField
, which we use for both the department and role fields. This will query the database for all departments and roles. The admin user will select one department and one role using the form on the front-end.
Add the following code to the admin/views.py
file:
The list_employees
view queries the database for all employees and assigns them to the variable employees
, which we will use to list them in the template.
The assign_employee
view takes an employee ID. First, it checks whether the employee is an admin user; if it is, a 403 Forbidden
error is thrown. If not, it updates the employee.department
and employee.role
with the selected data from the form, essentially assigning the employee a new department and role.
Create a employees
directory in the templates/admin
directory. In it, create the employees.html
and employee.html
files:
The employees.html
template shows a table of all employees. The table shows their full name, department, and role, or displays a -
in case no department and role has been assigned. Each employee has an assigned link, which the admin user can click to assign them a department and role.
Because the admin user is an employee as well, they will be displayed in the table. However, we have formatted the table such that admin users stand out with a green background and white text.
We need to update the admin menu once more:
Navigate to the Employees page now. If there are no users other than the admin, this is what you should see:
When there is an employee registered, this is displayed:
Feel free to add a variety of departments and roles so that you can start assigning them to employees.
You can re-assign departments and roles as well.
We now have a completely functional CRUD web app! In Part Two of the tutorial, we’ve been able to create an admin user and an admin dashboard, as well as customize the menu for different types of users. We’ve also built out the core functionality of the app, and can now add, list, edit, and delete departments and roles, as well as assign them to employees. We have also taken security into consideration by protecting certain views from unauthorized access.
In Part Three, we will create custom error pages, write tests, and deploy the app to PythonAnywhere.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!