One place for hosting & domains

      SQLite

      How to Use an SQLite Database in a Flask Application


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      In web applications, you usually need a database, which is an organized collection of data. You use a database to store and maintain persistent data that can be retrieved and manipulated efficiently. For example, in a social media application, you have a database where user data (personal information, posts, comments, followers) is stored in a way that can be efficiently manipulated. You can add data to a database, retrieve it, modify it, or delete it, depending on different requirements and conditions. In a web application, these requirements might be a user adding a new post, deleting a post, or deleting their account, which might or might not delete their posts. The actions you perform to manipulate data will depend on specific features in your application. For example, you might not want users to add posts with no titles.

      Flask is a lightweight Python web framework that provides useful tools and features for creating web applications in the Python Language. SQLite is a simple and fast open source SQL engine that can be used with Python to store and manipulate application data. SQLite works well with Python because the Python standard library provides the sqlite3 module, which you can use to interact with any SQLite database without having to install anything. Using SQLite with Python also requires minimal setup compared to other database engines.

      In this tutorial, you’ll build a small web application that demonstrates how to use SQLite with Flask to perform basic data manipulation covering CRUD: Create, Read, Update, and Delete. The web application will be a basic blog that displays posts on the index page. Users can create, edit, and delete individual posts.

      Prerequisites

      Step 1 — Setting up the Database

      In this step, you’ll set up the SQLite database you’ll use to store your data (the blog posts for your application). You’ll then populate the database with a few example entries.

      You will use the sqlite3 module to interact with the database, which is readily available in the standard Python library.

      Data in SQLite is stored in tables and columns, so you first need to create a table called posts with the necessary columns. You’ll create a .sql file that contains SQL commands to create the posts table with a few columns. You’ll then use this schema file to create the database.

      Open a database schema file called schema.sql inside your flask_app directory:

      Type the following SQL commands inside this file:

      flask_app/schema.sql

      DROP TABLE IF EXISTS posts;
      
      CREATE TABLE posts (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          title TEXT NOT NULL,
          content TEXT NOT NULL
      );
      

      Save and close the file.

      In this schema file, you first delete the posts table if it already exists. This avoids the possibility of another table named posts existing, which might result in confusing behavior (for example, if it has different columns). This isn’t the case here, because you haven’t created the table yet, so the SQL command won’t be executed. Note that this will delete all of the existing data whenever you execute this schema file. For our purposes, you will only execute this schema once, but you might want to execute it again to delete whatever data you inserted and start with an empty database again.

      Next, you use CREATE TABLE posts to create the posts table with the following columns:

      • id: An integer that represents a primary key. This key will get assigned a unique value by the database for each entry (that is, each blog post). AUTOINCREMENT automatically increments the post IDs, so that the first post will have an ID of 1, and the post added after it will have an ID of 2, and so on. Each post will always have the same ID, even if other posts are deleted.
      • created: The time the blog post was created. NOT NULL signifies that this column should not be empty, and the DEFAULT value is the CURRENT_TIMESTAMP value, which is the time at which the post was added to the database. Just like id, you don’t need to specify a value for this column, as it will be automatically filled in.
      • title: The post title. NOT NULL signifies that this column can’t be empty.
      • content: The post content. NOT NULL signifies that this column can’t be empty.

      Now, you’ll use the schema.sql file to create the database. To do so, you’ll create a Python file that will generate an SQLite .db database file based on this schema.sql file. Open a file named init_db.py inside your flask_app directory:

      Add the following code to it:

      flask_app/init_db.py

      import sqlite3
      
      connection = sqlite3.connect('database.db')
      
      
      with open('schema.sql') as f:
          connection.executescript(f.read())
      
      cur = connection.cursor()
      
      cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                  ('First Post', 'Content for the first post')
                  )
      
      cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                  ('Second Post', 'Content for the second post')
                  )
      
      connection.commit()
      connection.close()
      

      You first import the sqlite3 module. You open a connection to a database file named database.db, which will be created once you run the Python file. Then you use the open() function to open the schema.sql file. Next you execute its contents using the executescript() method that executes multiple SQL statements at once, which will create the posts table. You create a Cursor object that allows you to process rows in a database. In this case, you’ll use the cursor’s execute() method to execute two INSERT SQL statements to add two blog posts to your posts table. Finally, you commit the changes and close the connection.

      Save and close the file and then run it in the terminal using the python command:

      Once the file finishes execution, a new file called database.db will appear in your flask_app directory. This means you’ve successfully set up your database.

      Next, you’ll create a small Flask application, retrieve the two posts you inserted into the database, and display them on the index page.

      Step 2 — Displaying Posts

      In this step, you will create a Flask application with an index page where the blog posts you have in your database are displayed.

      With your programming environment activated and Flask installed, open a file called app.py for editing inside your flask_app directory:

      This file will set up your database connection and create a single Flask route to use that connection. Add the following code to the file:

      flask_app/app.py

      import sqlite3
      from flask import Flask, render_template
      
      app = Flask(__name__)
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          posts = conn.execute('SELECT * FROM posts').fetchall()
          conn.close()
          return render_template('index.html', posts=posts)
      

      Save and close the file.

      In the code above, you first import the sqlite3 module to use it to connect to your database. Then you import the Flask class and the render_template() function from the flask package. You make a Flask application instance called app. You define a function called get_db_connection(), which opens a connection to the database.db database file you created earlier, and sets the row_factory attribute to sqlite3.Row so you can have name-based access to columns. This means that the database connection will return rows that behave like regular Python dictionaries. Lastly, the function returns the conn connection object you’ll be using to access the database.

      You then use the app.route() decorator to create a Flask view function called index(). You use the get_db_connection() function to open a database connection. Then you execute an SQL query to select all entries from the posts table. You use the fetchall() method to fetch all the rows of the query result, this will return a list of the posts you inserted into the database in the previous step.

      You close the database connection using the close() method and return the result of rendering the index.html template. You also pass the posts object as an argument, which contains the results you got from the database. This will allow you to access the blog posts in the index.html template.

      To display the posts you have in your database on the index page, you will first create a base template, which will have all the basic HTML code other templates will also use to avoid code repetition. Then you’ll create the index.html template file you rendered in your index() function. To learn more about templates, see How to Use Templates in a Flask Application.

      Create a templates directory, then open a new template called base.html:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside the base.html file:

      flask_app/templates/base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %}- FlaskApp</title>
          <style>
              .post {
                  padding: 10px;
                  margin: 5px;
                  background-color: #f3f3f3
              }
      
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for('index') }}">FlaskApp</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      Save and close the file.

      This base template has all the HTML boilerplate you’ll need to reuse in your other templates. The title block will be replaced to set a title for each page, and the content block will be replaced with the content of each page. The navigation bar has two links, one for the index page where you use the url_for() helper function to link to the index() view function, and the other for an About page if you choose to include one in your application.

      Next, open a template called index.html. This is the template you referenced in the app.py file:

      • nano templates/index.html

      Add the following code to it:

      flask_app/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Posts {% endblock %}</h1>
          {% for post in posts %}
              <div class="post">
                  <p>{{ post['created'] }}</p>
                  <h2>{{ post['title'] }}</h2>
                  <p>{{ post['content'] }}</p>
              </div>
          {% endfor %}
      {% endblock %}
      

      Save and close the file.

      In the code above, you extend the base.html template and replace the contents of the content block. You use an <h1> heading that also serves as a title.

      You use a Jinja for loop in the line {% for post in posts %} to go through each post in the posts list. You access the creation date via {{ post['created'] }}, the title via {{ post['title'] }}, and the post content via {{ post['content'] }}.

      While in your flask_app directory with your virtual environment activated, tell Flask about the application (app.py in this case) using the FLASK_APP environment variable:

      Then set the FLASK_ENV environment variable to development to run the application in development mode and get access to the debugger. For more information about the Flask debugger, see How To Handle Errors in a Flask Application. Use the following commands to do this (on Windows, use set instead of export):

      • export FLASK_ENV=development

      Next, run the application:

      With the development server running, visit the following URL using your browser:

      http://127.0.0.1:5000/
      

      You’ll see the posts you added to the database on the first initiation.

      Index Page

      You’ve displayed the posts in your database on the index page. You now need to allow users to add new posts. You’ll add a new route for adding posts in the next step.

      Step 3 — Creating Posts

      In this step, you will add a new route to your Flask application that allows users to add new blog posts to the database, which will then appear on the index page.

      You’ll add a page with a web form where users enter the post title and post content. This form will be validated to make sure users don’t submit an empty form. To inform users the form is invalid, you’ll use a flash message which will only be shown once and will disappear on the next request (if you navigate to another page for example).

      Leave the development server running and open a new terminal window.

      First, open your app.py file:

      For handling the web form, you’ll need to import a few things from the flask package:

      • The global request object to access submitted data.
      • The url_for() function to generate URLs.
      • The flash() function to flash a message if a request is invalid.
      • The redirect() function to redirect users to the index page after adding the posts to the database.

      Add these imports to the first line in the file:

      flask_app/app.py

      from flask import Flask, render_template, request, url_for, flash, redirect
      
      # ...
      

      The flash() function stores flashed messages in the client’s browser session, which requires setting a secret key to secure sessions that remember information from one request to another. You must never allow anyone to access your secret key. See the Flask documentation for sessions for more information.

      Set a secret key by adding a SECRET_KEY configuration to your application via the app.config object. Add it next to the app instance definition.

      flask_app/app.py

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

      Remember that the secret key should be a long random string. For more on web forms and the secret key configuration, see How To Use Web Forms in a Flask Application.

      Next, add the following route at the end of the app.py file:

      flask_app/app.py

      # ...
      
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          return render_template('create.html')
      

      Save and close the file.

      In this route, you pass the tuple ('GET', 'POST') to the methods parameter to allow both GET and POST requests. GET requests are used to retrieve data from the server. POST requests are used to post data to a specific route. By default, only GET requests are allowed. When the user first requests the /create route using a GET request, a template file called create.html will be rendered. You will later edit this route to handle POST requests for when users fill and submit the web form for creating new posts.

      Open the new create.html template:

      • nano templates/create.html

      Add the following code to it:

      flask_app/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Add a New Post {% endblock %}</h1>
          <form method="post">
              <label for="title">Title</label>
              <br>
              <input type="text" name="title"
                     placeholder="Post title"
                     value="https://www.digitalocean.com/community/tutorials/{{ request.form['title'] }}"></input>
              <br>
      
              <label for="content">Post Content</label>
              <br>
              <textarea name="content"
                        placeholder="Post content"
                        rows="15"
                        cols="60"
                        >{{ request.form['content'] }}</textarea>
              <br>
              <button type="submit">Submit</button>
          </form>
      {% endblock %}
      

      Save and close the file.

      You extend the base template, set a heading as a title, and use a <form> tag with the attribute method set to post to indicate that the form will submit a POST request. You have a text field with the name title, which you’ll use to access the title data in your /create route. You set the value of the text field to request.form['title'] which is either empty or a saved version of the title if the form is invalid, so that the title does not get lost when things go wrong.

      After the title input field, you add a text area named content with the value {{ request.form['content'] }} to restore post content if the form is invalid.

      Last, you have a Submit button at the end of the form.

      Now, with the development server running, use your browser to navigate to the /create route:

      http://127.0.0.1:5000/create
      

      You will see an Add a New Post page with an input field for a post title, a text area for the post’s content, and a Submit button.

      Add New Post

      If you fill in the form and submit it, sending a POST request to the server, nothing happens because you did not handle POST requests on the /create route.

      Open app.py to handle the POST request the user submits:

      Edit the /create route to look as follows:

      flask_app/app.py

      # ...
      
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          if request.method == 'POST':
              title = request.form['title']
              content = request.form['content']
      
              if not title:
                  flash('Title is required!')
              elif not content:
                  flash('Content is required!')
              else:
                  conn = get_db_connection()
                  conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                               (title, content))
                  conn.commit()
                  conn.close()
                  return redirect(url_for('index'))
      
          return render_template('create.html')
      

      Save and close the file.

      You handle POST requests inside the if request.method == 'POST' condition. You extract the title and content the user submits from the request.form object. If the title is empty, you use the flash() function to flash the message Title is required!. You do the same in case of empty content.

      If both the title and the content are supplied, you open a database connection using the get_db_connection() function. You use the execute() method to execute an INSERT INTO SQL statement to add a new post to the posts table with the title and content the user submits as values. You use the ? placeholder to insert data into the table safely. You commit the transaction and close the connection. Lastly, you redirect the user to the index page where they can see their new post below existing posts.

      Warning: Never use Python string operations to dynamically create an SQL statement string. Always use the ? placeholder in your SQL statements to dynamically substitute values. Pass a tuple of values as the second argument to the execute() method to bind your values to the SQL statement. This prevents SQL injection attacks.

      With the development server running, use your browser to navigate to the /create route:

      http://127.0.0.1:5000/create
      

      Fill the form and submit it.

      You’ll be redirected to the index page where you’ll see your new post.

      If you submit a form without a title or one without any content, your post won’t be added to the database, you won’t be redirected to the index page, and you won’t receive any feedback for why that is. This is because you haven’t set up flashed messages to be displayed anywhere yet.

      Open base.html to add a link to the Create page in the navigation bar, and to display flashed messages below it.

      Edit the file to look as follows:

      flask_app/templates/base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %} - FlaskApp</title>
          <style>
              .post {
                  padding: 10px;
                  margin: 5px;
                  background-color: #f3f3f3
              }
      
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
      
              .alert {
                  padding: 20px;
                  margin: 5px;
                  color: #970020;
                  background-color: #ffd5de;
              }
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("index') }}">FlaskApp</a>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("create') }}">Create</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% for message in get_flashed_messages() %}
                  <div class="alert">{{ message }}</div>
              {% endfor %}
      
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      Save and close the file.

      Here, you add a new <a> link to the navigation bar that points to the Create page.

      You use a Jinja for loop to go through the flashed messages. These are available in the get_flashed_messages() special function. Each message is displayed in a <div> tag with a CSS class called alert. You style this <div> tag inside the <style> tag in the <head> section.

      Refresh your index page and you’ll see the new link in the navigation bar.

      Click the Create link, and submit an empty form. You’ll receive the flashed message “Title is required!”

      Fill in the title field and leave the content text area empty. Submit the form again, and you’ll receive a “Content is required!” message. Notice how the “Title is required!” message is gone. This is because it’s a flashed message and not a permanent one.

      You now have a way of adding new posts. Next, you’ll add a new route for allowing users to edit existing posts.

      Step 4 — Editing Posts

      In this step, you will add a new route to the application to allow users to edit existing posts.

      First, to avoid code repetition and to isolate code and make it easier to maintain, you’ll add a new function that takes an ID and retrieves a post associated with it from the database. You’ll use this function to get the post data you want to edit, and you’ll use it to get the post for when you want to delete it in the next step.

      Open app.py:

      The function you’ll use to retrieve a post will respond with a 404 Not Found error if the ID of the requested post does not correspond with any of the existing posts. To do this, you’ll use the abort() function, which aborts a request and responds with an error message. For more, see How To Handle Errors in a Flask Application.

      Add the abort() function to the imports:

      flask_app/app.py

      from flask import Flask, render_template, request, url_for, flash, redirect, abort
      

      Add a new function called get_post() below your get_db_connection() function:

      flask_app/app.py

      
      # ...
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      def get_post(post_id):
          conn = get_db_connection()
          post = conn.execute('SELECT * FROM posts WHERE id = ?',
                              (post_id,)).fetchone()
          conn.close()
          if post is None:
              abort(404)
          return post
      
      # ...
      

      This new function has a post_id argument that determines what post to retrieve and return. You open a database connection with get_db_connection() and execute an SQL query to get the post associated with the given post_id value. You get the post with the fetchone() method, store it in the post variable, and close the connection.

      If the post variable has the value None, meaning no result was found in the database, you use the abort() function you imported earlier to respond with a 404 error code and the function will finish execution. If, however, a post was found, you return the value of the post variable.

      Next, add a new route for editing posts at the end of the file:

      flask_app/app.py

      # ...
      
      @app.route('/<int:id>/edit/', methods=('GET', 'POST'))
      def edit(id):
          post = get_post(id)
      
          if request.method == 'POST':
              title = request.form['title']
              content = request.form['content']
      
              if not title:
                  flash('Title is required!')
      
              elif not content:
                  flash('Content is required!')
      
              else:
                  conn = get_db_connection()
                  conn.execute('UPDATE posts SET title = ?, content = ?'
                               ' WHERE id = ?',
                               (title, content, id))
                  conn.commit()
                  conn.close()
                  return redirect(url_for('index'))
      
          return render_template('edit.html', post=post)
      

      Save and close the file.

      You use the route /<int:id>/edit/, with int: being a converter that accepts positive integers. And id is the URL variable that will determine the post you want to edit. For example, /2/edit/ will allow you to edit the post with the ID of 2. The ID is passed from the URL to the edit() view function. You pass the value of the id argument to the get_post() function to fetch the post associated with the provided ID from the database. Remember that this will respond with a 404 Not Found error if no post with the given ID exists.

      The last line renders a template file called edit.html, and passes in the post variable that has the post data. You’ll use this to display the existing title and content on the Edit page.

      The if request.method == 'POST' block handles the new data the user submits. Similar to adding a new post, you extract the title and content. You flash a message if the title or the content is not provided.

      If the form is valid, you open a database connection and use the UPDATE SQL statement to update the posts table by setting the new title and new content, where the ID of the post in the database is equal to the ID that was in the URL. You commit the transaction, close the connection, and redirect to the index page.

      Next you need to create a page where users can do the editing. Open a new edit.html template:

      Add the following code to it:

      flask_app/templates/edit.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
          <form method="post">
              <label for="title">Title</label>
              <br>
              <input type="text" name="title"
                     placeholder="Post title"
                     value="{{ request.form['title'] or post['title'] }}"></input>
              <br>
      
              <label for="content">Post Content</label>
              <br>
              <textarea name="content"
                        placeholder="Post content"
                        rows="15"
                        cols="60"
                        >{{ request.form['content'] or post['content'] }}</textarea>
              <br>
              <button type="submit">Submit</button>
          </form>
      {% endblock %}
      

      Save and close the file.

      This is similar to the code in the create.html template, except for displaying the post title inside the page’s title in the line {% block title %} Edit "{{ post['title'] }}" {% endblock %}, the value of the input in {{ request.form['title'] or post['title'] }}, and the value of the text area in {{ request.form['content'] or post['content'] }}. This displays the data stored in the request if it exists; otherwise it displays the data from the post variable that was passed to the template containing current database data.

      With the development server running, use your browser to navigate to the following URL to edit the first post:

      http://127.0.0.1:5000/1/edit
      

      You’ll see a page that looks as follows:

      Edit Post

      Edit the post and submit the form. You’ll see your changes applied on the index page. If you submit a form without a title or without any content, you’ll receive a flashed message.

      You now need to add a link that points to the Edit page for each post on the index page. Open the index.html template file:

      • nano templates/index.html

      Edit the file to look exactly like the following:

      flask_app/templates/index.html

      
      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Posts {% endblock %}</h1>
          {% for post in posts %}
              <div class="post">
                  <p>{{ post['created'] }}</p>
                  <h2>{{ post['title'] }}</h2>
                  <p>{{ post['content'] }}</p>
                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("edit', id=post['id']) }}">Edit</a>
              </div>
          {% endfor %}
      {% endblock %}
      

      Save and close the file.

      You added an <a> tag that links to the edit() view function. You pass the post ID you have in post['id']) to the url_for() function to generate the post’s edit link. This adds a link to the Edit page of each post below it.

      Refresh the index page and click the Edit link to edit a post.

      You can now add new posts and edit existing ones. Next, you’ll add a button to allow users to delete existing posts.

      Step 5 — Deleting Posts

      In this step, you will add a Delete button to the Edit page to allow users to delete a post.

      First, you’ll add a new /id/delete route that accepts POST requests, similar to the edit() view function. Your new delete() view function will receive the ID of the post to be deleted from the URL, retrieve it using the get_post() function, and then delete it from the database if it exists.

      Open the app.py file:

      Add the following route at the end:

      flask_app/app.py

      # ...
      
      @app.route('/<int:id>/delete/', methods=('POST',))
      def delete(id):
          post = get_post(id)
          conn = get_db_connection()
          conn.execute('DELETE FROM posts WHERE id = ?', (id,))
          conn.commit()
          conn.close()
          flash('"{}" was successfully deleted!'.format(post['title']))
          return redirect(url_for('index'))
      

      Save and close the file.

      This view function only accepts POST requests in the methods parameter. This means that navigating to the /ID/delete route on your browser will return a 405 Method Not Allowed error, because web browsers default to GET requests. To delete a post, the user clicks on a button that sends a POST request to this route.

      The function receives the ID of the post to be deleted. You use this ID to retrieve the post using the get_post() function. This responds with a 404 Not Found error if no post with the given ID exists. You open a database connection and execute a DELETE FROM SQL command to delete the post. You use WHERE id = ? to specify the post you want to delete.

      You commit the change to the database and close the connection. You flash a message to inform the user that the post was successfully deleted and redirect them to the index page.

      Note that you don’t render a template file. This is because you’ll just add a Delete button to the Edit page.

      Open the edit.html template file:

      Then add the following <hr> and <form> tags directly before the {% endblock %} line:

      flask_app/templates/edit.html

              <button type="submit">Submit</button>
          </form>
      
      
          <hr>
          <form action="https://www.digitalocean.com/community/tutorials/{{ url_for("delete', id=post['id']) }}" method="POST">
              <input type="submit" value="Delete Post"
                      onclick="return confirm('Are you sure you want to delete this post?')">
          </form>
      {% endblock %}
      

      Save and close the file.

      Here, you have a web form that submits a POST request to the delete() view function. You pass post['id'] to specify the post that will be deleted. You use the confirm() method available in web browsers to display a confirmation message before submitting the request.

      Now navigate again to the Edit page of a post and try deleting it:

      http://127.0.0.1:5000/1/edit
      

      After you confirm the deletion, you’ll be redirected to the index page, and the post will no longer be there. A flash message will appear below the navigation bar informing you that the post was successfully deleted.

      You now have a way of deleting unwanted posts from the database in your Flask application.

      Conclusion

      You built a small web blog that communicates with an SQLite database. You have basic functionalities in your Flask application, such as adding new data to the database, retrieving data and displaying it on a page, and editing and deleting existing data.

      For more on how to use SQLite with Python and Flask, see the following tutorials:

      If you would like to read more about Flask, check out the other tutorials in the Flask series.



      Source link

      How To Use Python-Markdown with Flask and SQLite


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Flask is a framework for building web applications using the Python language. With Flask, you can use SQLite as your database engine to store application data.

      Markdown is a markup language commonly used for the process of writing content in an easy-to-read text format. Using Markdown, you can format plain text with features such as headings, links, and images, you can then convert the text into HTML, which includes these formatting features. To learn how to use Markdown, check out the Markdown syntax standard.

      Python-Markdown is a Python library that allows you to convert Markdown text to HTML; it mostly follows the Markdown standard, with a few minor differences to the standard Markdown syntax.

      In this tutorial, you’ll use Flask, SQLite, and Python-Markdown to build a small note-taking web application that supports formatting text using Markdown. The app will allow users to display, create, and format notes with headings, links, lists, images, and other features. You’ll use the Bootstrap toolkit to style your application.

      Prerequisites

      Step 1 — Setting Up Dependencies

      In this step, you will activate your Python environment and install Flask and Python-Markdown using the pip package installer. Then you’ll create the database you will use to store notes and add some sample data to it.

      First, activate your programming environment if you haven’t already:

      Once you have activated your programming environment, install Flask and the Python-Markdown library using the following command:

      • pip install flask markdown

      Then create a database schema file called schema.sql, which will contain SQL commands to create a notes table. Open the file inside your flask_notes directory:

      Type the following SQL commands inside this file:

      flask_notes/schema.sql

      DROP TABLE IF EXISTS notes;
      
      CREATE TABLE notes (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          content TEXT NOT NULL
      );
      

      In this SQL file, you first delete any already existing table named notes, which could cause problems. This ensures that your notes table is exactly as described in this SQL. Note that this will delete all of the content you have in the database whenever you use these SQL commands, so ensure you don’t write any important content in the web application until you finish this tutorial and experiment with the final result.

      You use the CREATE TABLE notes statement to create the notes table with the following columns:

      • id: An integer that represents a primary key; the database will assign this a unique value for each entry (a note).
      • created: The note’s creation date; it will be automatically filled in with the time at which the note was added to the database.
      • content: The note’s content.

      Save and close the file.

      To create the database using the schema.sql file, open a file named init_db.py inside the flask_notes directory:

      Then add the following code:

      flask_notes/init_db.py

      
      import sqlite3
      
      connection = sqlite3.connect('database.db')
      
      
      with open('schema.sql') as f:
          connection.executescript(f.read())
      
      cur = connection.cursor()
      
      cur.execute("INSERT INTO notes (content) VALUES (?)", ('# The First Note',))
      cur.execute("INSERT INTO notes (content) VALUES (?)", ('_Another note_',))
      cur.execute("INSERT INTO notes (content) VALUES (?)", ('Visit [this page](https://www.digitalocean.com/community/tutorials) for more tutorials.',))
      
      connection.commit()
      connection.close()
      

      Here, you first import the sqlite3 module. Next, you connect to a file called database.db that will be created once you execute this program. database.db is the database that will hold all your application’s data. You then open the schema.sql file and run it using the executescript() method that executes multiple SQL statements at once. This will create the notes table.

      Using a Cursor object, you execute a few INSERT SQL statements to create three notes. You’re using Markdown syntax here: the first note is an <h1> heading, the second note is in italic, and the third note contains a link. You use the ? placeholder in the execute() method and pass a tuple containing the note’s content to safely insert data into the database.

      Finally, you commit the changes and close the connection.

      Save and close the file.

      Run the program:

      After execution, a new file called database.db will appear in your flask_notes directory.

      You’ve activated your environment, installed Flask and Python-Markdown, and created the SQLite database. Next, you’ll retrieve the Markdown notes from the database, convert them to HTML, and display them on the application’s homepage.

      Step 2 — Displaying the Notes on the Index Page

      In this step, you will create a Flask application that connects to the database and displays the sample data you have in the database. You will convert the Markdown text in the database to HTML and then render it on the index page.

      First, create the app.py application file inside your flask_notes directory:

      Add the following code to it:

      flask_notes/app.py

      import sqlite3
      import markdown
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      

      You first import the sqlite3 module, the markdown package, and Flask helpers.

      The get_db_connection() function opens a connection to the database.db database file and then sets the row_factory attribute to sqlite3.Row. This gives you name-based access to columns, which means that the database connection will return rows that behave like regular Python dictionaries. Lastly, the function returns the conn connection object you’ll be using to access the database.

      Following that, add the next snippet of code:

      flask_notes/app.py

      #. . .
      
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      

      Here, you create the Flask application object and set a secret key to secure sessions.

      Then add the following code:

      flask_notes/app.py

      #. . .
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          db_notes = conn.execute('SELECT id, created, content FROM notes;').fetchall()
          conn.close()
      
          notes = []
          for note in db_notes:
             note = dict(note)
             note['content'] = markdown.markdown(note['content'])
             notes.append(note)
      
          return render_template('index.html', notes=notes)
      

      The index() function is a Flask view function, which is a function decorated using the special @app.route decorator. Flask converts this function’s return value into an HTTP response that an HTTP client, such as a web browser, will display.

      In the index() view function, you open a database connection and you execute a SELECT SQL statement to fetch the ID, the creation date, and the content for all rows of the notes table. You use the fetchall() method to get a list of all the rows and save this data in the db_notes variable. Then, you close the connection.

      To convert the content of the notes from Markdown to HTML, you create a new empty list called notes. You loop through the db_notes list and convert each note from an sqlite3.Row to a regular Python dictionary using the dict() Python function to allow assignment. Next, you use the markdown.markdown() function to convert the value of note['content'] to HTML. For example, calling markdown.markdown('#Hi') would return the string '<h1>Hi</h1>' because in Markdown # represents the <h1> heading. After modifying note['content'], you append the note to the notes list.

      Finally, you render a template file called index.html, passing it the notes list.

      After all the additions, your file will be as follows:

      flask_notes/app.py

      import sqlite3
      import markdown
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          db_notes = conn.execute('SELECT id, created, content FROM notes;').fetchall()
          conn.close()
      
          notes = []
          for note in db_notes:
             note = dict(note)
             note['content'] = markdown.markdown(note['content'])
             notes.append(note)
      
          return render_template('index.html', notes=notes)
      

      Save and close the file.

      Next, you’ll create a base template and the index.html template file.

      In your flask_notes directory, create a templates directory and open a file called base.html inside it:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside base.html; note that you’re using Bootstrap here. If you are not familiar with HTML templates in Flask, read Step 3 of How To Make a Web Application Using Flask in Python 3:

      flask_notes/templates/base.html

      <!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskNotes</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% 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://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Most of the code in the preceding block is standard HTML and 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.

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

      Save and close the file.

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

      • nano templates/index.html

      Add the following code to it:

      flask_notes/templates/index.html

      
      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskNotes {% endblock %}</h1>
          {% for note in notes %}
          <div class="card">
              <div class="card-body">
                  <span class="badge badge-primary">#{{ note['id'] }}</span>
                  <span class="badge badge-default">{{ note['created'] }}</span>
                  <hr>
                  <p>{{ note['content'] |safe }}</p>
              </div>
          </div>
          <hr>
          {% endfor %}
      {% endblock %}
      

      Here you extend base.html, you define a title, and you use a for loop to go through notes displaying each note in a Bootstrap card. You display the note’s ID, the note’s creation date, and the note’s content, which you converted into HTML in the index() view function.

      You use the |safe Jinja filter, which you apply to the content using |; this is similar to calling a function in Python (in a similar way to safe(note['content'])). The |safe filter allows the browser to render HTML code—if it was not applied, it will display the HTML as plain text. This is usually referred to as “escaping HTML,” which is a security feature that prevents malicious HTML from being interpreted in the browser that would result in a dangerous security vulnerability called cross-site scripting (XSS). Since the Python-Markdown library returns safe HTML, you can allow the browser to render it using the |safe filter. Remember not to use this filter until you are certain that the HTML code you allow is safe and trusted.

      For more, read Flask’s documentation on controlling autoescaping.

      Save and close the file.

      Set the environment variables Flask needs and run the application using the following commands:

      • export FLASK_APP=app
      • export FLASK_ENV=development
      • flask run

      The FLASK_APP environment variable specifies the application you want to run (the app.py file). The FLASK_ENV environment variable specifies the mode; development means that the application will run in development mode with the debugger running (remember to avoid using this mode in production). You run the application using the flask run command.

      Open a browser and type in the URL http://127.0.0.1:5000/.

      Flask Notes Index

      Here, you’ll find that each note is formatted and rendered as HTML in the browser instead of plain text.

      You created a Flask application that connects to the database, fetches notes, converts their content from Markdown text to HTML, and then renders them on the index page. Next, you’ll add a route to allow users to add new notes, which they can write using Markdown syntax.

      Step 3 — Adding New Notes

      In this step, you’ll add a new route that will allow users to take new notes. Users can use Markdown syntax to write their notes—your application will save the notes to the database, which it will then display on the index page with the proper formatting. You will also add a button to the navigation bar that takes users to this new page.

      You will use web forms to allow users to submit data in your Flask application and you’ll store user-submitted data in your database.

      First open the app.py file to add a new route:

      Add the following code to the end of the file:

      flask_notes/app.py

      #. . .
      
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          conn = get_db_connection()
      
          if request.method == 'POST':
              content = request.form['content']
              if not content:
                  flash('Content is required!')
                  return redirect(url_for('index'))
              conn.execute('INSERT INTO notes (content) VALUES (?)', (content,))
              conn.commit()
              conn.close()
              return redirect(url_for('index'))
      
          return render_template('create.html')
      

      Since you will use this route to insert new data to the database via a web form, you allow both GET and POST requests using methods=('GET', 'POST') in the app.route() decorator. In the create() view function, you open a database connection.

      If the user has submitted a form that means the condition request.method == 'POST' is true, so you extract the note’s content that the user has submitted using request.form['content'] and save it to a variable called content. If the content is empty, you flash a 'Content is required!' message and redirect the user to the index page. If the content was not empty, you use an INSERT SQL statement to add the note’s content to the notes table. You commit the changes and close the connection, then you redirect the user to the index page.

      If the request is a GET request, meaning the user has just visited the page, you render a template file called create.html.

      Save and close the file.

      Next, open the create.html template file:

      • nano templates/create.html

      Add the following code to it:

      flask_notes/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Add a New Note {% endblock %}</h1>
          <form method="post">
          <div class="form-group">
              <label for="content">Note Content</label>
              <textarea type="text" name="content"
                     placeholder="Note content, you can use Markdown syntax" class="form-control"
                     value="{{ request.form['content'] }}" autofocus></textarea>
          </div>
      
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
          </form>
      
      {% endblock %}
      

      Here, you use a form with a text area for the note’s content. You use request.form to access the form data that is stored in case something goes wrong with your form submission (for example, if no content was provided). You add a submit button under the text area for the user to press to send data to the application in a POST request.

      Save and close the file.

      Next open the base.html file to add a New Note button to the navigation bar:

      Edit the file with the highlighted code as follows:

      flask_notes/templates/base.html

      <!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskNotes</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
      
                  <li class="nav-item active">
                      <a class="nav-link" href="https://www.digitalocean.com/community/tutorials/{{ url_for("create') }}">New Note</a>
                 </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% 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://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      You add a new <li> item to the navigation bar with the url_for() function to link to the create() view function. With this, you can access the page for creating a new note from the navigation bar.

      Run the development server if you haven’t already:

      Use your browser to go to http://127.0.0.1:5000/create and add the following markdown note:

      ### Flask
      Flask is a **web** framework for _Python_.
      
      Here is the Flask logo:
      
      ![Flask Logo](https://flask.palletsprojects.com/en/1.1.x/_images/flask-logo.png)
      

      This markdown contains an h3 heading, the word web in bold, the word Python in italic, and an image.

      Submit the note, you will find that your application formats it into HTML.

      Markdown Formatted Note

      You now have a new route that allows users to add new notes to the database. Users can take notes with Markdown formatting and the application will display the notes in HTML on the index page.

      You can access the full code for the application from this repository.

      Conclusion

      You created a Flask application for taking notes in Markdown format to allow users to use text formatting such as headings, bold and italic text, adding images and links, among other things. You connected your application to a SQLite database to store and retrieve your data. You’ve incorporated Markdown text into HTML conversion so that notes will render on the page. To learn more about using Markdown in Python, read How To Use Python-Markdown to Convert Markdown Text to HTML.

      For further information about Flask, check out the following tutorials:



      Source link

      How To Make a URL Shortener with Flask and SQLite


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Flask is a framework for building web applications using Python and SQLite is a database engine that you can use with Python to store application data.

      In this tutorial, you will build a URL shortener, a service that takes any URL and generates a shorter, more readable version like bit.ly.

      Hashids is a library that generates a short unique ID from integers. For example, you can use it to convert a number like 12 to a unique string like 1XcId. You will use Hashids to generate unique strings for URL IDs.

      You can use unique strings to generate IDs for videos on a video-sharing site or IDs for images on a service to upload images. This unique string gives you unpredictable IDs; therefore, if a user can access an image at your_domain/image/J32Fr, they can’t predict the location of other images. This is not possible if you use integer IDs in a URL shortener—for example, your_domain/image/33 would allow users to predict the location of other images. Unpredictable URLs add a form of privacy to your service because they prevent users from working out different URLs shortened by other users.

      You will use Flask, SQLite, and the Hashids library to build your URL shortener. Your application will allow users to enter a URL and generate a shorter version, in addition to a statistics page where users can view the number of times a URL has been clicked. You’ll use the Bootstrap toolkit to style your application.

      Prerequisites

      Step 1 — Setting Up Dependencies

      In this step, you will activate your Python environment and install Flask and the Hashids library using the pip package installer. Then you’ll create the database you will use to store URLs.

      First, activate your programming environment if you haven’t already:

      Once you have activated your programming environment, install Flask and the Hashids library using the following command:

      • pip install flask hashids

      Then create a database schema file called schema.sql, containing SQL commands to create a urls table. Open a file called schema.sql inside your flask_shortener directory:

      Type the following SQL commands inside this file:

      flask_shortener/schema.sql

      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
      );
      

      In the schema file, you first delete the urls table if it already exists. This avoids the possibility of another table named urls existing, which might result in confusing behavior; for example, if it has different columns. Note that this will delete all of the existing data whenever the schema file executes.

      You then create the table with the following columns:

      • id: The ID of the URL, this will be a unique integer value for each URL entry. You will use it to get the original URL from a hash string.
      • created: The date the URL was shortened.
      • original_url: The original long URL to which you will redirect users.
      • clicks: The number of times a URL has been clicked. The initial value will be 0, which will increment with each redirect.

      Save and close the file.

      To execute the schema.sql file to create the urls table, open a file named init_db.py inside your flask_shortener directory:

      Then add the following code:

      flask_shortener/init_db.py

      import sqlite3
      
      connection = sqlite3.connect('database.db')
      
      with open('schema.sql') as f:
          connection.executescript(f.read())
      
      connection.commit()
      connection.close()
      

      Here you connect to a file called database.db that your program will create once you execute this program. This file is the database that will hold all of your application’s data. You then open the schema.sql file and run it using the executescript() method that executes multiple SQL statements at once. This will create the urls table. Finally, you commit the changes and close the connection.

      Save and close the file.

      Run the program:

      After execution, a new file called database.db will appear in your flask_shortener directory.

      With this, you’ve installed Flask and the Hashids library, created the database schema, and created the SQLite database with a table called urls to store the URL shortener’s original URLs. Next, you’ll use Flask to create the index page where your users can enter a URL to generate a short URL.

      Step 2 — Creating the Index Page for Shortening URLs

      In this step, you will create a Flask route for the index page, which will allow users to enter a URL that you then save into the database. Your route will use the ID of the URL to generate a short string hash with the Hashids library, construct the short URL, and then render it as a result.

      First, open a file named app.py inside your flask_shortener directory. This is the main Flask application file:

      Add the following code to the file:

      flask_shortener/app.py

      import sqlite3
      from hashids import Hashids
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      

      In this code, you first import the sqlite3 module, the Hashids class from the hashids library, and Flask helpers.

      The get_db_connection() function opens a connection to the database.db database file and then sets the row_factory attribute to sqlite3.Row. As a result, you can have name-based access to columns; the database connection will return rows that behave like regular Python dictionaries. Lastly, the function returns the conn connection object you’ll be using to access the database.

      Next, add the following:

      flask_shortener/app.py

      . . .
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      
      hashids = Hashids(min_length=4, salt=app.config['SECRET_KEY'])
      
      

      You create the Flask application object and set a secret key to secure sessions. Since the secret key is a secret random string, you’ll also use it to specify a salt for the Hashids library; this will ensure the hashes are unpredictable since every time the salt changes, the hashes also change.

      Note: A salt is a random string that is provided to the hashing function (that is, hashids.encode()) so that the resulting hash is shuffled based on the salt. This process ensures the hash you get is specific to your salt so that the hash is unique and unpredictable, like a secret password that only you can use to encode and decode hashes. Remember to keep it secret for security purposes (which is why you use the application’s secret key).

      You create a hashids object specifying that a hash should be at least 4 characters long by passing a value to the min_length parameter. You use the application’s secret key as a salt.

      Next, add the following code to the end of your file:

      flask_shortener/app.py

      . . .
      @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_url=short_url)
      
          return render_template('index.html')
      

      The index() functions 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, you accept both GET and POST requests by passing methods=('GET', 'POST') to the app.route() decorator. You open a database connection.

      Then if the request is a GET request, it skips the if request.method == 'POST' condition until the last line. This is where you 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. You 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 submitted a URL, you use the INSERT INTO SQL statement to store the submitted URL in the urls table. You include the ? placeholder in the execute() method and pass a tuple containing the submitted URL to insert data safely into the database. Then you commit the transaction and close the connection.

      In a variable called url_id, you store the ID of the URL you inserted into the database. You can access the ID of the URL using the lastrowid attribute, which provides the row ID of the last inserted row.

      You construct a hash using the hashids.encode() method, passing it the URL ID; you save the result in a variable called hashid. As an example, the call hashids.encode(1) might result in a unique hash like KJ34 depending on the salt you use.

      You 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 your_domain if you deploy your application. For example, the short_url variable will have a value like http://127.0.0.1:5000/KJ34, which is the short URL that will redirect your users to the original URL stored in the database with the ID that matches the hash KJ34.

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

      After all the additions, the file will be as follows:

      flask_shortener/app.py

      import sqlite3
      from hashids import Hashids
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      
      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_url=short_url)
      
          return render_template('index.html')
      

      Save and close the file.

      Next, you’ll create a base template and the index.html template file.

      In your flask_shortener directory, create a templates directory and open a file called base.html inside it:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside base.html. Note that, for styling, you’re using Bootstrap here too. If you are not familiar with HTML templates in Flask, see Step 3 of How To Make a Web Application Using Flask in Python 3:

      flask_shortener/templates/base.html

      <!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskShortener</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% 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://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Most of the code in the preceding block is standard HTML and 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. You 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.

      Save and close the file.

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

      • nano templates/index.html

      Add the following code to it:

      flask_shortener/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskShortener {% endblock %}</h1>
          <form method="post">
          <div class="form-group">
              <label for="url">URL</label>
              <input type="text" name="url"
                     placeholder="URL to shorten" class="form-control"
                     value="{{ request.form['url'] }}" autofocus></input>
          </div>
      
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
          </form>
      
          {% if short_url %}
          <hr>
          <span>{{ short_url }}</span>
          {% endif %}
      {% endblock %}
      

      Here you extend base.html, define a title, and create a form with an input named url. 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. You also add a submit button.

      Then you 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, you display the short URL under the form.

      Set the environment variables Flask needs and run the application using the following commands:

      • export FLASK_APP=app
      • export FLASK_ENV=development
      • flask run

      The FLASK_APP environment variable specifies the application you want to run (the app.py file). The FLASK_ENV environment variable specifies the mode. development means that the application will run in development mode with the debugger running. Remember to avoid using this mode in production. You run the application using the flask run command.

      Open a browser and type in the URL http://127.0.0.1:5000/. You will find a Welcome to FlaskShortener page.

      Flask Shortener Index page

      Submit a URL, and you will receive a short URL.

      Flask Shortened URL displayed beneath the URL input box

      You 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, you’ll add a route that extracts the hash from the short URL, finds the original URL, and redirects users to it.

      Step 3 — Adding the Redirect Route

      In this step, you will add a new route that takes the short hash the application generates and decodes the hash into its integer value, which is the original URL’s ID. Your new route will also use the integer ID to fetch the original URL and increment the clicks value. Finally, you will redirect users to the original URL.

      First, open the app.py to add a new route:

      Add the following code to the end of the file:

      flask_shortener/app.py

      . . .
      
      @app.route('/<id>')
      def url_redirect(id):
          conn = get_db_connection()
      
          original_id = hashids.decode(id)
          if original_id:
              original_id = original_id[0]
              url_data = conn.execute('SELECT original_url, clicks FROM urls'
                                      ' WHERE id = (?)', (original_id,)
                                      ).fetchone()
              original_url = url_data['original_url']
              clicks = url_data['clicks']
      
              conn.execute('UPDATE urls SET clicks = ? WHERE id = ?',
                           (clicks+1, original_id))
      
              conn.commit()
              conn.close()
              return redirect(original_url)
          else:
              flash('Invalid URL')
              return redirect(url_for('index'))
      

      This new route accepts a value id through the URL and passes it to the url_redirect() view function. For example, visiting http://127.0.0.1:5000/KJ34 would pass the string 'KJ34' to the id parameter.

      Inside the view function, you first open a database connection. Then you use the decode() method of the hashids object to convert the hash to its original integer value and store it in the original_id variable. You check that the original_id has a value—meaning decoding the hash was successful. If it has a value, you extract the ID from it. As the decode() method returns a tuple, you fetch the first value in the tuple with original_id[0], which is the original ID.

      You then use the SELECT SQL statement to fetch the original URL and its number of clicks from the urls table, where the ID of the URL matches the original ID you extracted from the hash. You fetch the URL data with the fetchone() method. Next, you extract the data into the two original_url and clicks variables.

      You then increment the number of clicks of the URL with the UPDATE SQL statement.

      You commit the transaction and close the connection, and redirect to the original URL using the redirect() Flask helper function.

      If decoding the hash fails, you flash a message to inform the user that the URL is invalid, and redirect them to the index page.

      Save and close the file.

      Run your development server:

      Use your browser to go to http://127.0.0.1:5000/. Enter a URL and visit the resulting short URL; your application will redirect you to the original URL.

      You created a new route that redirects users from the short URL to the original URL. Next, you’ll add a page to show how many times each URL has been visited.

      Step 4 — Adding a Statistics Page

      In this step, you’ll add a new route for a statistics page that displays how many times each URL has been clicked. You’ll also add a button that links to the page on the navigation bar.

      Allowing users to see the number of visits each shortened link has received will provide visibility into each URL’s popularity, which is useful for projects, like marketing ad campaigns. You can also use this workflow as an example of adding a feature to an existing Flask application.

      Open app.py to add a new route for a statistics page:

      Add the following code to the end of the file:

      flask_shortener/app.py

      . . .
      
      @app.route('/stats')
      def stats():
          conn = get_db_connection()
          db_urls = conn.execute('SELECT id, created, original_url, clicks FROM urls'
                                 ).fetchall()
          conn.close()
      
          urls = []
          for url in db_urls:
              url = dict(url)
              url['short_url'] = request.host_url + hashids.encode(url['id'])
              urls.append(url)
      
          return render_template('stats.html', urls=urls)
      

      In this view function, you open a database connection. Then you fetch the ID, the creation date, the original URL, and the number of clicks for all of the entries in the urls table. You use the fetchall() method to get a list of all the rows. You then save this data in the db_urls variable and close the connection.

      To display the short URL for each entry, you will need to construct it and add it to each item in the list of the URLs you fetched from the database (db_urls). You create an empty list called urls and loop through the db_urls list with for url in db_urls.

      You use the dict() Python function to convert the sqlite3.Row object to a dictionary to allow assignment. You add a new key called short_url to the dictionary with the value request.host_url + hashids.encode(url['id']), which is what you used before to construct short URLs in the index view function. You append this dictionary to the urls list.

      Finally, you render a template file called stats.html, passing the urls list to it.

      Save and close the file.

      Next, create the new stats.html template file:

      • nano templates/stats.html

      Type the following code into it:

      flask_shortener/templates/stats.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} FlaskShortener Statistics {% endblock %}</h1>
          <table class="table">
              <thead>
                  <tr>
                  <th scope="col">#</th>
                  <th scope="col">Short</th>
                  <th scope="col">Original</th>
                  <th scope="col">Clicks</th>
                  <th scope="col">Creation Date</th>
                  </tr>
              </thead>
              <tbody>
                  {% for url in urls %}
                      <tr>
                          <th scope="row">{{ url['id'] }}</th>
                          <td>{{ url['short_url'] }}</td>
                          <td>{{ url['original_url'] }}</td>
                          <td>{{ url['clicks'] }}</td>
                          <td>{{ url['created'] }}</td>
                      </tr>
                  {% endfor %}
              </tbody>
          </table>
      
      {% endblock %}
      

      Here you extend the base.html base template by specifying a title and defining a table with the following columns:

      • #: The ID of the URL.
      • Short: The short URL.
      • Original: The original URL.
      • Clicks: The number of times a short URL has been visited.
      • Creation Date: The creation date of the short URL.

      Each row is filled using a for loop that goes through the urls list and displays the value of each column for each URL.

      Run the development server with the following:

      Use your browser to go to http://127.0.0.1:5000/stats. You will find all the URLs in a table.

      Statistics page with list of URLs and number of clicks

      Next, add a Stats button to the navigation bar. Open the base.html file:

      Edit the file as per the following highlighted lines:

      flask_shortener/templates/base.html

      <!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskTodo</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
      
                  <li class="nav-item active">
                      <a class="nav-link" href="https://www.digitalocean.com/community/tutorials/{{ url_for("stats')}}">Stats</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% 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://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Here you incorporate a new <li> item to the navigation bar. You use the url_for() function to link to the stats() view function. You can now access the statistics page from the navigation bar.

      Your statistics page shows information about each URL, including its shorter equivalent and how many times it has been visited.

      You can reuse this code for monitoring number of clicks in other contexts, such as keeping track of how many times a post has been liked or updated on a social media site or how many times a photo/video has been viewed.

      You can access the full code for the application from this repository.

      Conclusion

      You have created a Flask application that allows users to enter a long URL and generate a shorter version. You have transformed integers into short string hashes, redirected users from one link to another, and set up a page for statistics so you can monitor shortened URLs. For further projects and tutorials on working with Flask, check out the following tutorials:



      Source link