Create a URL Shortener with Python Flask

FlaskFlaskBeginner
Practice Now

Introduction

In this project, we will learn how to create a URL shortener using Python and Flask. A URL shortener is a tool that takes a long URL and converts it into a shorter, more manageable URL. This can be useful for sharing links on social media or in emails, as well as making long URLs more user-friendly.

👀 Preview

Alt text

🎯 Tasks

In this project, you will learn:

  • How to set up a project folder and create the necessary files for the URL shortener.
  • How to set up a SQLite database to store the original URLs and their corresponding shortened URLs.
  • How to generate short URLs using random characters.
  • How to create an index page where users can submit a URL to be shortened and display the shortened URL.
  • How to redirect users to the original URL when they enter a shortened URL.
  • How to create a history page to display all the URLs that have been shortened.
  • How to run the project and test it in a web browser.

🏆 Achievements

After completing this project, you will be able to:

  • Understand how to set up a Flask project and create the necessary files.
  • Utilize SQLite to create a database and execute queries.
  • Generate random characters for the short URLs.
  • Develop HTML templates and extend base templates.
  • Handle form submissions and display dynamic content using Flask.
  • Implement URL redirection based on user input.

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL flask(("`Flask`")) -.-> flask/CoreConceptsGroup(["`Core Concepts`"]) flask(("`Flask`")) -.-> flask/DevelopmentToolsGroup(["`Development Tools`"]) flask(("`Flask`")) -.-> flask/DataHandlingGroup(["`Data Handling`"]) flask/CoreConceptsGroup -.-> flask/application_object("`Application Object`") flask/DevelopmentToolsGroup -.-> flask/blueprint_objects("`Blueprint Objects`") flask/DataHandlingGroup -.-> flask/incoming_request_data("`Incoming Request Data`") flask/DataHandlingGroup -.-> flask/response_objects("`Response Objects`") flask/CoreConceptsGroup -.-> flask/useful_internals("`Useful Internals`") flask/CoreConceptsGroup -.-> flask/class_based_views("`Class-Based Views`") flask/DevelopmentToolsGroup -.-> flask/command_line_interface("`Command Line Interface`") subgraph Lab Skills flask/application_object -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/blueprint_objects -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/incoming_request_data -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/response_objects -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/useful_internals -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/class_based_views -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} flask/command_line_interface -.-> lab-298843{{"`Create a URL Shortener with Python Flask`"}} end

Creating the Project Files

To get started, we need to create a project folder and add the following files to it:

  • app.py: This file will contain all the code for our URL shortener.
  • templates/index.html: This file will contain the HTML code for the index page of our website.
  • templates/history.html: This file will contain the HTML code for the history page of our website.
  • templates/base.html: This file will contain the base HTML code that will be used in both index.html and history.html.
cd ~/project
touch app.py
mkdir templates
touch templates/index.html templates/history.html templates/base.html

Setting Up the Database

Our URL shortener will need a database to store the original URLs and their corresponding shortened URLs. We will be using SQLite for our database, which is a lightweight and easy-to-use database management system.

To set up the database, we first need to import the necessary modules in app.py:

## app.py
from flask import Flask, render_template, request, redirect
import string
import random
import sqlite3

Next, we need to connect to the SQLite database and create a cursor object:

## Connect to the SQLite database
conn = sqlite3.connect("urls.db", check_same_thread=False)
db = conn.cursor()

Then, we can create a table in our database to store the URLs:

## Create the URLs table if it doesn't exist
db.execute(
    """CREATE TABLE IF NOT EXISTS urls
              (id INTEGER PRIMARY KEY AUTOINCREMENT,
               original_url TEXT NOT NULL,
               short_url TEXT NOT NULL,
               created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"""
)
conn.commit()

Generating Short URLs

To generate short URLs, we will use a combination of random letters and numbers. This can be achieved using the string and random modules in Python. We will create a function called generate_short_url() that will return a random string of 6 characters:

def generate_short_url():
    characters = string.ascii_letters + string.digits
    short_url = "".join(random.choice(characters) for _ in range(6))
    return short_url

Creating the Index Page

The index page of our website will allow users to submit a URL to be shortened. It will also display the shortened URL once it has been generated. The HTML code for this page can be found in templates/index.html.

First, we need to extend the base.html template and create a form for users to submit their URLs:

{% extends "base.html" %} {% block content %}
<div class="text-center">
  <h1 class="text-3xl font-bold text-gray-900 mb-8">Shorten Your URL</h1>
  <form action="/" method="POST" class="w-full max-w-sm mx-auto">
    <div
      class="flex items-center border-2 border-blue-500 rounded overflow-hidden"
    >
      <input
        type="text"
        name="original_url"
        placeholder="Enter URL"
        class="appearance-none bg-transparent border-none w-full text-gray-700 py-2 px-4 leading-tight focus:outline-none"
      />
      <button
        type="submit"
        class="bg-blue-500 hover:bg-blue-700 text-white py-2 px-4 rounded-r focus:outline-none"
      >
        Shorten
      </button>
    </div>
  </form>
  {% if short_url %}
  <div class="mt-4">
    <p class="text-lg text-gray-700">
      Short URL:
      <a href="{{ request.host_url }}{{ short_url }}" class="text-blue-500"
        >{{ request.host_url }}{{ short_url }}</a
      >
    </p>
  </div>
  {% endif %}
</div>
{% endblock %}

In index.html, we have a form with a single input field for the user to enter their URL. The form will be submitted to the same page, so we set the action attribute to /. We also set the method attribute to POST so that the form data will be sent in the request body.

Now, we need add templates/base.html:

<!doctype html>
<html>
  <head>
    <title>URL Shortener</title>
    <link
      href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
      rel="stylesheet"
    />
  </head>
  <body>
    <nav class="p-6 bg-white flex justify-between items-center">
      <a href="/" class="text-2xl font-bold text-gray-900">URL Shortener</a>
      <div>
        <a href="/" class="text-gray-800 mr-6">Home</a>
        <a href="/history" class="text-gray-800">History</a>
      </div>
    </nav>
    <main class="container mx-auto max-w-xl pt-8 min-h-screen">
      {% block content %} {% endblock %}
    </main>
  </body>
</html>

In base.html, we have a navigation bar at the top of the page with links to the home page and the history page. We also have a main element that will contain the content of each page. The content block is where the content of each page will be inserted.

We also need to add the necessary code in app.py to handle form submissions and generate the shortened URL:

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        original_url = request.form["original_url"]
        short_url = generate_short_url()

        ## Insert the original and short URLs into the database
        db.execute(
            "INSERT INTO urls (original_url, short_url) VALUES (?, ?)",
            (original_url, short_url),
        )
        conn.commit()

        return render_template("index.html", short_url=short_url)

    return render_template("index.html")

In index(), we check if the request method is POST. If it is, we get the original URL from the form data and generate a short URL. We then insert the original and short URLs into the database and render the index.html template with the short URL.

Redirecting to the Original URL

When a user enters a shortened URL, we want to redirect them to the corresponding original URL. To do this, we need to create a function that will retrieve the original URL from the database based on the shortened URL:

@app.route("/<short_url>")
def redirect_to_url(short_url):
    ## Retrieve the original URL from the database based on the short URL
    db.execute("SELECT original_url FROM urls WHERE short_url=?", (short_url,))
    result = db.fetchone()

    if result:
        original_url = result[0]
        return redirect(original_url)

    return render_template("index.html")

Creating the History Page

The history page will display all the URLs that have been shortened, along with their original URLs and the date they were added. The HTML code for this page can be found in templates/history.html.

First, we need to extend the base.html template and add a table to display the URLs:

{% extends "base.html" %} {% block content %}
<div class="w-full overflow-x-auto">
  <table class="w-full bg-white rounded shadow overflow-hidden">
    <thead class="bg-gray-200">
      <tr>
        <th
          class="text-left py-3 px-4 uppercase font-semibold text-sm text-gray-700"
        >
          Original URL
        </th>
        <th
          class="text-left py-3 px-4 uppercase font-semibold text-sm text-gray-700"
        >
          Short URL
        </th>
        <th
          class="text-left py-3 px-4 uppercase font-semibold text-sm text-gray-700"
        >
          Date
        </th>
      </tr>
    </thead>
    <tbody>
      {% for result in results %}
      <tr class="hover:bg-gray-100">
        <td class="border-t">
          <p class="py-3 px-4">{{ result[0] }}</p>
        </td>
        <td class="border-t">
          <a
            href="{{ request.host_url }}{{ result[1] }}"
            class="py-3 px-4 text-blue-500 hover:text-blue-700"
            >{{ request.host_url }}{{ result[1] }}</a
          >
        </td>
        <td class="border-t">
          <p class="py-3 px-4">{{ result[2] }}</p>
        </td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
</div>
{% endblock %}

We also need to add the necessary code in app.py to retrieve the URLs from the database and pass them to the template:

@app.route("/history")
def history():
    ## Retrieve all URLs from the database, ordered by the most recent ones
    db.execute(
        "SELECT original_url, short_url, created_at FROM urls ORDER BY created_at DESC"
    )
    results = db.fetchall()
    return render_template("history.html", results=results)

Running the Project

Add the following code to the bottom of app.py:

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=8080)

Once the modules are installed, we can run our project by executing python app.py in our terminal. This will start the Flask development server and our website will be accessible at http://localhost:8080.

Swich to 8080 Tab and refresh the page. You should see the following:

Alt text

Summary

In this project, we learned how to create a URL shortener using Python and Flask. We first set up the necessary project files and database, then created functions to generate short URLs and handle form submissions. We also learned how to retrieve and display the URLs from the database. With this project, you can now create your own URL shortener and customize it to your liking.

Other Flask Tutorials you may like