Creating the Index Page for Shortening URLs

In this step, we will create a Flask route for the index page, which will allow users to enter a URL that we then save into the database. This route will use the custom short id provided by the user or generate one on its own, construct the short URL, and then render it as a result.

First, let’s move to routes.py file and create a Python function to generate short id.

In order to generate a short id, we have used the choice method from Python’s random module. Also, we have used Python’s in-built string module for lower case alphabets, upper case alphabets, and digits.

Now, we need to create a template for the index page that will be served by the index route. This template will have a simple form where user can input the original URL and custom short id(optional) and submit it. But we’ll not create index.html directly. We can make use of the Template Inheritance concept in Jinja2. So, let us create a templates directory within the app package and create a base.html file inside that. You can paste the HTML code into that file.

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">

    <title>{% block title %} {% endblock %}</title>
  </head>
  <body>
    <div class="container mt-3">
        {% for message in get_flashed_messages() %}
            <div class="alert alert-danger">{{ message }}</div>
        {% endfor %}
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js" integrity="sha384-eMNCOe7tC1doHpGoWe/6oMVemdAVTMs2xqW4mwXrXsW0L84Iytr2wi5v2QjrP/xp" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.min.js" integrity="sha384-cn7l7gDp0eyniUwwAZgrzD06kc/tftFf19TOAs2zVinnD/C7E91j9yyk5//jjpt/" crossorigin="anonymous"></script>
  </body>
</html>
Note that, for styling, we’re using Bootstrap here.

Most of the code in the preceding block is standard HTML code required for Bootstrap. The <meta> tags provide information for the web browser, the <link> tag links the Bootstrap CSS files, and the <script> tags are links to JavaScript code that allows some additional Bootstrap features. Check out the Bootstrap documentation for more information.

The <title>{% block title %} {% endblock %}</title> tag allows the inheriting templates to define a custom title. We use the for message in get_flashed_messages() loop to display the flashed messages (warnings, alerts, and so on). The {% block content %} {% endblock %} placeholder is where inheriting templates place the content so that all templates have access to this base template, which avoids repetition.

Next, create the index.html file that will extend this base.html file:

{% extends 'base.html' %}

{% block content %}
    <h1 class="text-center mb-3">{% block title %} Welcome to Shorty {% endblock %}</h1>
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-8">
            <form method="post" action="{{url_for('index')}}">
            <div class="form-floating mb-3">
                <input type="text" name="url" id="url"
                    placeholder="Enter looooooooooooong URL" class="form-control"
                    value="{{ request.form['url'] }}" autofocus></input>
                <label for="url">URL</label>
            </div>
            <div class="form-floating mb-3">
                <input type="text" name="custom_id" id="custom_id"
                    placeholder="Want to customise? (optional)" class="form-control"
                    value="{{ request.form['custom_id'] }}"></input>
                <label for="custom_id">Custom Short ID</label>
            </div>

            <div class="form-group text-center">
                <button type="submit" class="btn btn-lg btn-primary">Shorten</button>
            </div>
            </form>

            {% if short_url %}
            <hr>
            <span><a href="{{ short_url }}">{{ short_url }}</a></span>
            {% endif %}
        </div>
        <div class="col-md-2"></div>
    </div>
{% endblock %}
 

Here we extend base.html, define a title, and create a form with two inputs named url and custom_id. The url input will allow users to enter URLs to shorten. It has a value of request.form['url'], which stores data in cases of submission failure; that is if the user provides no URL. Similarly, custom_id input will allow users to enter custom short id. We then have a submit button.

Then we check if the short_url variable has any value—this is true if the form submits and the short URL generates successfully. If the condition is true, we display the short URL under the form.

Now we can re-write our index view function in routes.py as :

from datetime import datetime
from app.models import ShortUrls
from app import app, db
from flask import render_template, request, flash, redirect, url_for

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

        if short_id and ShortUrls.query.filter_by(short_id=short_id).first() is not None:
            flash('Please enter different custom id!')
            return redirect(url_for('index'))

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

        if not short_id:
            short_id = generate_short_id(8)

        new_link = ShortUrls(
            original_url=url, short_id=short_id, created_at=datetime.now())
        db.session.add(new_link)
        db.session.commit()
        short_url = request.host_url + short_id

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

    return render_template('index.html')
 

The index() function is a Flask view function, which is a function decorated using the special @app.route decorator. Its return value gets converted into an HTTP response that an HTTP client, such as a web browser, displays.

Inside the index() view function, we accept both GET and POST requests by passing methods=['GET', 'POST'] to the app.route() decorator. 

Then if the request is a GET request, it skips the if request.method == 'POST' condition until the last line. This is where we render a template called index.html, which will contain a form for users to enter a URL to shorten.

If the request is a POST request, the if request.method == 'POST' condition is true, which means a user has submitted a URL. We store the URL in the url variable; if the user has submitted an empty form, you flash the message The URL is required! and redirect to the index page. If the user has entered custom_id, we store it in short_id, else we generate random short id using the function that we had created before.

If the user has submitted a URL, we create a new_link with all the data such as original_url short_id and created_at. Then we commit the transaction.

We then construct the short URL using request.host_url, which is an attribute that Flask’s request object provides to access the URL of the application’s host. This will be http://127.0.0.1:5000/ in a development environment and our_domain if we deploy our application. For example, the short_url variable will have a value like http://127.0.0.1:5000/asdf1gHJ, which is the short URL that will redirect users to the original URL stored in the database with the ID that matches the asdf1gHJ.

Lastly, we render the index.html template passing the short_url variable to it.

We can now run the server and test our view function.

We have created a Flask application with a page that accepts URLs and generates shorter ones, but the URLs don’t do anything yet. In the next step, we’ll add a route that extracts the short_id from the short URL, finds the original URL, and redirects users to it.

Discussion

1

0