How I Used the Flask Framework to Create an URL Shortener Application

5/5 - (1 vote)

A URL shortener service generates a shorter, more readable version of the URL it was given. Flask, a Python web framework can be used to create a URL shortener app.

So, we will create an application allowing users to enter a URL and shorten it. We will use the SQLite database engine to store application data.
If you prefer to learn how this is done using the Django framework, you are free to read this article.

Set up

Create a new folder for this project. Then, create and activate a virtual environment by running the following commands in your terminal.

python3 -m venv venv
source venv/bin/activate

Install Flask and the hashids library.

pip install flask hashids

The hashids library will be used to generate a unique ID. You will understand this as we proceed.

Creating a Database Engine

Since we will store application data, we must create a database file. Do it and call the file schema.sql. Then, write the following SQL commands.

DROP TABLE IF EXISTS urls;
CREATE TABLE urls(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  original_url TEXT NOT NULL,
  clicks INTEGER NOT NULL DEFAULT 0
);

If the above code seems strange, you may want to familiarize yourself with SQL commands.

We want to create a table named urls. As we donโ€™t want to face issues caused by several tables with the same name, we must first delete it. Thatโ€™s what is meant by โ€˜DROP TABLEโ€ฆโ€™

The table is then created with four columns. The id column will contain the unique integer value for each entry. Next is the date the shortened URL was generated. The third column is the original URL. Finally, the number of times the URL was clicked.

The schema.sql file can only be executed with the help of a Python script. So, we create another file called init_db.py

import Sqlite3

connection = Sqlite3.connect('database.db')

with open('schema.sql') as sql:
    connection.executescript(sql.read())

connection.commit()
connection.close()

Once you run the script (with python3 init_db.py), a new file called database.db will be created. This is where all application data will be stored.

The connect() method creates the file. As soon as the file is created, it is then populated with the urls table. This is done by first opening and reading the content from the schema.sql.

It then calls the executescript() method to execute all the SQL commands in the SQL file. After which, we commit and close the file. By now, your folder should contain the following files:

database.db init_db.py schema.sql

Creating the Database Connection

Let us open a connection to the database file. Create a file and name it db_connection.py.

import sqlite3

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

Notice that we set the row-factory attribute to sqlite3.Row. This makes it possible to access values by column name. We then return the connection object, which will be used to access the database.

The Main File

Next, create another file and name it main.py. This will be our main file. In this file, we will import the database connection file.

from db_connection import get_db_connection
from hashids import Hashids
from flask import Flask, flash, render_template, request, url_for, redirect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'Your secret key'

hashids = Hashids(min_length=4, salt=app.config['SECRET_KEY'])

@app.route('/', methods=('GET', 'POST'))
def index():
    conn = get_db_connection()
    if request.method == 'POST':
        url = request.form['url']

        if not url:
            flash('The URL is required!')
            return redirect(url_for('index'))

        url_data = conn.execute('INSERT INTO urls (original_url) VALUES (?)', (url,))
        conn.commit()
        conn.close()

        url_id = url_data.lastrowid
        hashid = hashids.encode(url_id)
        short_url = request.host_url + hashid
        return render_template('index.html', short_run=short_url)
    return render_template('index.html')

We create an instance of the Flask class. The __name__ variable allows Flask to locate other resources, including templates in the current directory. We then create hashids object that will have four characters. (You can choose to have more characters). We use a secret key to specify the salt for the Hashids library.

The index() function is decorated with the @app.route decorator that assigns the URL ('/') to the function, thus turning it into a Flask view function.

In the index() function, we open a database connection. Then, we check if the request method is POST. If so, the code block under it will be executed. If not, we only return an empty web page using the render_template() method.

If the request method is POST, we use request.form['url'] to collect input from the template file (index.html). The output is the URL to shorten. However, if the user gives no URL, we simply flash a message and redirect the user back to the same index.html web page.

If a URL is given, it will be added to the database by executing the command, INSERT INTO โ€ฆ

After closing the database, we select the last row id of the database, which is the current URL added. Remember the AUTOINCREMENT keyword in the id column of the database file. This ensures that the id is incremented with each new entry.

With the last row id selected, we use the hashids.encode() method to generate a unique hash and concatenate it to the URL of the applicationโ€™s host (indicated with the request.host_url attribute). This becomes the shortened URL that would be displayed to the user.

Please check my GitHub page for the template files. Make sure you create a templates folder to keep the HTML files.

The local server is opened when you run python3 main.py in your terminal. This is possible because of the special name variable and the app.run() method.

Adding Extra Features

Wonโ€™t it be nice to know how many times each URL has been clicked and have them displayed on a web page? We are going to add that feature. Update your app.py by adding the following:

@app.route('/stats')
def stats():
    conn = get_db_connection()
    db_urls = conn.execute('SELECT * FROM urls').fetchall()
    conn.close()

    urls = []
    for url in db_urls:
        url = dict(url)
        url['short_url'] = request.host_url + hasids.encode(url['id'])
        urls.append(url)
    return render_template('stats.html', urls=urls)

We again open a database connection, and fetch all the columns in the urls table (indicated by *) and a list of all the rows using the fetchall() method.

After closing the database, we loop through the result. In each iteration, we convert the sqlite3.Row object to a dictionary and repeat the same thing we did previously to encode the id number. This is then concatenated to form a new URL. Finally, we append the result to an empty list and render it to the browser.

๐Ÿ’ก Notice we didnโ€™t commit the database as we did previously. This is because we didnโ€™t make changes to the database. We close it after fetching the data we needed.

Your folder should now have the following files:

  • database.db,
  • db_connection.py,
  • init_db.py,
  • main.py,
  • schema.sql, templates.

Templates Files

As earlier stated, you should check my GitHub page for the templates files. We created a base.html file inside the templates folder that other files will inherit.

The other two files have certain things that make rendering dynamic content to our Flask web page possible. It is the {% ... %} and { ... } code blocks.

These are Jinja2 templating language that comes together with the Flask library.

The render_templates() method in the stats() function has another argument, urls. This is from the stats.html web page, while the other urls is the variable that will be displayed on the web page.

Conclusion

This is one of the ways to create a URL shortener app using the Flask framework. This project has expose us to how Flask works as well as how it interacts with database. If you struggle to understand some of what we did, that should be expected as a beginner. However, as you keep working on projects, it will become second nature to you.

๐Ÿ’ก Recommended: How I Created an URL Shortener App Using Django