Adding Users to the Database
Before we create our users we will need a way to hash our passwords. It’s not advisable to use plain text passwords in your database because of security reasons so we need to hash them. One good hashing algorithm we can use is called bcrypt and there is a flask extension that helps us do that. The flask extension is called flask-bcrypt. Install it with pip using “pip install flask-bcrypt”
Bcrypt has a method called bcrypt.generate_password_hash('testing') that is used to hash
passwords, and the argument it takes is the password string you want to hash. Everytime you run
this method it returns a different hashed version of the password. In order to check if the
hashed password is actually the original password text you can use the method
bcrypt.check_password_hash() which takes two arguments, the first argument is the variable that
contains the hashed password and the second one is the plain password text. It returns true or
false based on whether the two arguments are the same or not. see below
hashed_pw = bcrypt.generate_password_hash('testing').decode('utf-8') # the decode method just hashes the password into an utf-8 string instead of the default bytesbcrypt.check_password_hash(hashed_pw, 'helloworld') --> returns falsebcrypt.check_password_hash(hashed_pw, 'testing') --> returns trueyou need to import the Bcrypt class from the extension with “from flask-bcrypt import Bcrypt” in your app initialization. Then you should create an instance of the class with “bcrypt = Bcrypt(app)”
Please note that I have changed the application structure where i have created a module folder
that contains my templates, static folders. Also I have created different files for the routes
and models inside this new module folder. And the main app configuration is done in a file called
__init__.py which is also in this new folder. Outside this new module folder is a file called
run.py which contains the code for running the flask app. “if name == main, app.run(debug=True)”.
In this case you should import the bcrypt extension in the __init__.py file.
Check below for how we used what we’ve learnt so far to allow a new user to register to our database.
@app.route('/register', methods=['GET', 'POST'])def register(): """Route for user registration.""" form = RegistrationForm() if form.validate_on_submit(): hashed_password = bcrypt.generate_password_hash('form.password.data').decode('utf-8') user = User(username=form.username.data, email=form.email.data, password=hashed_password) db.session.add(user) db.session.commit() flash('Your account has been created. You can Login In', 'success') return redirect(url_for('login')) return render_template('register.html', title='Register', form=form)However, note that even though we have added a user to our database. There is still one problem that exists in the sense that we are not checking if the username or email already exists. Remember that we want our username and email to be unique to one person which means we dont want another user trying to register with the same email or username. To put this validation check in place we can write a custom validation function in our forms.py file under the “RegistrationForm” class, where we basically check if the username or email exists before. If the username or email already exists we can raise a validation error to the user with an error message of choice. See below:
class RegistrationForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)]) email = StringField('Email', validators=[DataRequired(), Email()]) password = PasswordField('Password', validators=[DataRequired()]) confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')]) submit = SubmitField('Sign Up')
def validate_username(self, username): user = User.query.filter_by(username=username.data).first() if user: raise ValidationError('User Already Exists, Try Again')
def validate_email(self, email): user = User.query.filter_by(email=email.data).first() if user: raise ValidationError('Email Already Exists, Try Again')Note that you will have to import the ValidationError from the WTForm.
Logging In
Now that we have a pretty good registration system, we need to create a login system so users
with accounts can login and logout. This can be done with an extension called “flask-login”
install it with pip. Then you add it to your app initialization that is the __init__.py file
you need to import the LoginManager class from the extension with
“from flask_login import LoginManager” in your app initialization. Then you should create an
instance of the class with “login_manager = LoginManager(app)”
The way this works is that we will add some functionality to our database models, and then it will handle all of the sessions in the background for us.
Ps: this process was too long for me to document. To check the login functionality, open the routes.py and the models.py file to see how i set it up. Also you can use chatgpt to generate a list of steps you can follow to make that happen.
Logging Out
This is fairly simple. Create a logout route, call the logout_user() method inside the logout
function and redirect to the homepage. Inside the blog application though, i used a condition in
my layout template to check if user is logged in, if yes we displayed the logout link at the top
right. Else we just display the regular login/register links
Restrictions
This is basically how to put a restriction on certain routes, so you can only visit those routes when you are logged in. for instance, you have to be logged in to twitter in order to edit your profile, so they’ve put a restriction on the edit profile route where it only allowes logged in users.
First lets create a route for the users’ account that they can access after they’ve logged in
@app.route('/account')def account(): return render_template('account.html', title="Account")Then we’ll follow standard procedure to create an “account.html” file. But now inside our layout, we’ll now display a link to the account route when the user is logged in. We can do that by using an if statement to check if the user is is_authenticated.
Also, we want to put a check in place that makes sure that a user is logged in before they can access the account page. To do that we can use the “login_required” decorator from the flask-login extension. After importing the login_required, you can add it to the account route. See below
@app.route('/account')@login_requireddef account(): return render_template('account.html', title="Account")After doing that, we also need to tell our extension where our login route is located. We can do
that by opening our __init__.py file, and right under where you created the instance of the
login_manager, you can set the login route by typing the
“login_manager.login_view = ‘login’”. The view that we pass in here is the function name that you
pass to your route, so its the same thing that you pass to the url_for function.