One place for hosting & domains

      Database

      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 Update Database Records in Laravel Eloquent



      Part of the Series:
      A Practical Introduction to Laravel Eloquent ORM

      Eloquent is an object relational mapper (ORM) that is included by default within the Laravel framework. In this project-based series, you’ll learn how to make database queries and how to work with relationships in Laravel Eloquent. To follow along with the examples demonstrated throughout the series, you’ll improve a demo application with new models and relationships. Visit the series introduction page for detailed instructions on how to download and set up the project.

      In a previous section of this series, you updated an existing Artisan command in order to support the new lists feature. Although there are commands to insert and delete links, the demo application currently doesn’t have a command to edit existing links. This can be useful to move links between lists, for instance, or update a link description.

      In this guide, you’ll create a new Artisan command to update existing links in the database.

      From your terminal, first make sure you’re in your project’s root directory, then run the following to bootstrap a new Artisan command:

      • docker-compose exec app php artisan make:command LinkUpdate

      This will create a new LinkUpdate.php file located at app/Console/Commands. Open the file in your code editor of choice:

      app/Console/Commands/LinkUpdate.php
      

      This file contains boilerplate code for a new Artisan command. You’ll update it to handle editing a link provided its unique id. This is what your handle() method needs to do:

      • Obtain an id provided by the user and check for the existence of a link with a matching id in the database.
      • If a valid link cannot be found, show an error message and exit.
      • If a valid link is found, prompt the user to provide updated values for the link description and link list.
      • Ask the user to confirm changes.
      • When confirmed, update the item in the database.

      Start by including a couple use definitions at the top of the file, to facilitate referencing to the Link and LinkList classes later on:

      app/Console/Commands/LinkUpdate.php

      <?php
      
      namespace AppConsoleCommands;
      
      use AppModelsLink;
      use AppModelsLinkList;
      use IlluminateConsoleCommand;
      
      ...
      

      To obtain the link id, you should set up a mandatory argument in the new link:update command, so that users are required to provide that parameter at run time. Locate the command signature definition at the top of the file and replace it with the highlighted line:

      app/Console/Commands/LinkUpdate.php

      ...
      
      class LinkUpdate extends Command
      {
          /**
           * The name and signature of the console command.
           *
           * @var string
           */
          protected $signature="link:update {link_id}";
      ...
      

      If you save the file and try to run the command now without an additional argument, you’ll get an error:

      • docker-compose exec app php artisan link:update

      Output

      Not enough arguments (missing: "link_id").

      In the handle() method, you need to obtain the link id provided by the user and locate it in the database. This can be done with the argument() method that is provided through the parent Command class. Then, you can use the find() Eloquent method to query the database for a link with that id. If the find() method returns null, it means no link with that id was found, so the program should exit in error.

      app/Console/Commands/LinkUpdate.php

      ...
         /**
           * Execute the console command.
           *
           * @return int
           */
          public function handle()
          {
              $link_id = $this->argument('link_id');
              $link = Link::find($link_id);
      
              if ($link === null) {
                  $this->error("Invalid or non-existent link ID.");
                  return 1;
              }
      
              // obtain updated information from user
          }
      ...
      

      When a valid link is found, you need to prompt the user for the updated link information.You can do so using the ask method, highlighted in the next example:

      app/Console/Commands/LinkUpdate.php: function handle()

      ...
              if ($link === null) {
                  $this->error("Invalid or non-existent link ID.");
                  return 1;
              }
      
              $link->description = $this->ask('Link Description (ENTER to keep current)') ?? $link->description;
              $list_name = $this->ask('Link List (ENTER to keep current)') ?? $link->link_list->title;
      ...
      

      This code will prompt the user for an updated description and list, while keeping the current values as default in case a user doesn’t provide new ones, pressing ENTER to skip the prompt.

      Once you have all this information, you can proceed to the update. It’s a good idea to use the confirm() method to have the user confirm the changes before you run the database update. This is how such code would look:

      app/Console/Commands/LinkUpdate.php: function handle()

      ...
              $link->description = $this->ask('Link Description (ENTER to keep current)') ?? $link->description;
              $list_name = $this->ask('Link List (ENTER to keep current)') ?? $link->link_list->title;
      
              $this->info("Description: $link->description");
              $this->info("Listed in: " . $list_name);
      
              if ($this->confirm('Is this information correct?')) {
                  //code that updates the link
              }
      ...
      

      Inside the if block, you have to start by checking if the requested list exists, otherwise create a new list with the provided name. Then, you’ll use the associate() method to update the relationship between this link and its “parent” list. The save() method, finally, will persist the changes to the database:

      app/Console/Commands/LinkUpdate.php: function handle()

      ...
              if ($this->confirm('Is this information correct?')) {
                  $list = LinkList::firstWhere('slug', $list_name);
                  if (!$list) {
                      $list = new LinkList();
                      $list->title = $list_name;
                      $list->slug = $list_name;
                      $list->save();
                  }
                  $link->link_list()->associate($list)->save();
                  $this->info("Updated.");
              }
      ...
      

      This is the complete LinkUpdate.php file for your reference:

      app/Console/Commands/LinkUpdate.php

      <?php
      
      namespace AppConsoleCommands;
      
      use AppModelsLink;
      use AppModelsLinkList;
      use IlluminateConsoleCommand;
      
      class LinkUpdate extends Command
      {
          /**
           * The name and signature of the console command.
           *
           * @var string
           */
          protected $signature="link:update {link_id}";
      
          /**
           * The console command description.
           *
           * @var string
           */
          protected $description = 'Update a link in the database';
      
          /**
           * Create a new command instance.
           *
           * @return void
           */
          public function __construct()
          {
              parent::__construct();
          }
      
          /**
           * Execute the console command.
           *
           * @return int
           */
          public function handle()
          {
              $link_id = $this->argument('link_id');
              $link = Link::find($link_id);
      
              if ($link === null) {
                  $this->error("Invalid or non-existent link ID.");
                  return 1;
              }
      
              $link->description = $this->ask('Link Description (ENTER to keep current)') ?? $link->description;
              $list_name = $this->ask('Link List (ENTER to keep current)') ?? $link->link_list->title;
      
              $this->info("Description: $link->description");
              $this->info("Listed in: " . $list_name);
      
              if ($this->confirm('Is this information correct?')) {
                  $list = LinkList::firstWhere('slug', $list_name);
                  if (!$list) {
                      $list = new LinkList();
                      $list->title = $list_name;
                      $list->slug = $list_name;
                      $list->save();
                  }
                  $link->link_list()->associate($list)->save();
                  $this->info("Updated.");
              }
      
              return 0;
          }
      }
      

      Note: For more detailed information on Artisan commands, check our guide on How To Create Artisan Commands to Manage Database Records in Laravel, which is part of our introductory Laravel series.

      Save the file when you’re finished. Then, use the link:show command to obtain all links and its respective IDs:

      • docker-compose exec app php artisan link:show

      Output

      +----+-------------------------------------------------+--------------+----------------------------------+ | id | url | list | description | +----+-------------------------------------------------+--------------+----------------------------------+ | 1 | https://digitalocean.com/community | default | DO Community | | 2 | https://digitalocean.com/community/tags/laravel | default | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | default | PHP Tutorials at DigitalOcean | | 4 | https://twitter.com/digitalocean | social | Twitter | | 5 | https://dev.to/digitalocean | social | DEV.to | | 6 | https://laravel.com/docs/8.x/eloquent | default | Laravel Eloquent Docs | +----+-------------------------------------------------+--------------+----------------------------------+

      Then, choose an item to edit. For instance, you may want to create a digitalocean list for the links that point to the DigitalOcean website (that would correspond to items with IDs 1, 2, and 3 in the previous example output).

      To update the link with ID 1, run:

      • docker-compose exec app php artisan link:update 1

      Output

      Link Description (ENTER to keep current): > DO Community Link List (ENTER to keep current): > digitalocean Description: DO Community Listed in: digitalocean Is this information correct? (yes/no) [no]: > y Updated.

      Then, run the link:show command again to see the updated information:

      Output

      +----+-------------------------------------------------+--------------+----------------------------------+ | id | url | list | description | +----+-------------------------------------------------+--------------+----------------------------------+ | 1 | https://digitalocean.com/community | digitalocean | DO Community | | 2 | https://digitalocean.com/community/tags/laravel | digitalocean | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | digitalocean | PHP Tutorials at DigitalOcean | | 4 | https://twitter.com/digitalocean | social | Twitter | | 5 | https://dev.to/digitalocean | social | DEV.to | | 6 | https://laravel.com/docs/8.x/eloquent | default | Laravel Eloquent Docs | +----+-------------------------------------------------+--------------+----------------------------------+

      In this guide, you learned how to update database records with Laravel Eloquent. You have upgraded the demo application to include a new command that allows users to edit existing links in the database.

      In the next and final part of this series, you’ll create a new command to delete a list of links.





      Source link

      How To Delete Database Records in Laravel Eloquent



      Part of the Series:
      A Practical Introduction to Laravel Eloquent ORM

      Eloquent is an object relational mapper (ORM) that is included by default within the Laravel framework. In this project-based series, you’ll learn how to make database queries and how to work with relationships in Laravel Eloquent. To follow along with the examples demonstrated throughout the series, you’ll improve a demo application with new models and relationships. Visit the series introduction page for detailed instructions on how to download and set up the project.

      In Eloquent, you can delete database records conveniently with the delete method from the parent Model class. The link:delete command, already implemented within the base version of the demo application, deletes links based on a valid link id. The application is still missing a command to delete lists.

      In the last part of this series, you’ll create a new command to delete lists. For simplicity, any links associated with the list to be deleted will be reassigned to the default link list.

      From your terminal, run the following to bootstrap a new Artisan command:

      • docker-compose exec app php artisan make:command ListDelete

      This will create a new ListDelete.php file located at app/Console/Commands. Open the file in your code editor of choice:

      app/Console/Commands/ListDelete.php
      

      You’ll update this code to handle deleting a link list provided its unique slug, which is a URL-friendly name used to identify each list.

      This is what your handle() method needs to do:

      • Obtain a slug provided by the user and check for the existence of a list with a matching slug in the database.
      • If a valid list cannot be found, show an error message and exit.
      • If a valid list is found, prompt the user to confirm.
      • Reassign to the default list any links associated with the list that will be deleted.
      • Delete the list from the database.

      If you’ve been following along with all parts of the series so far, you have implemented similar code before when creating the LinkUpdate command. The main difference now is that you won’t need to prompt the user for additional info, and before running the delete() method you’ll need to run a mass update to change associated links to a different list.

      Replace the boilerplate code in your ListDelete.php file with the following:

      app/Console/Commands/ListDelete.php

      <?php
      
      namespace AppConsoleCommands;
      
      use AppModelsLink;
      use AppModelsLinkList;
      use IlluminateConsoleCommand;
      
      class ListDelete extends Command
      {
          /**
           * The name and signature of the console command.
           *
           * @var string
           */
          protected $signature="list:delete {list_slug}";
      
          /**
           * The console command description.
           *
           * @var string
           */
          protected $description = 'Delete Lists';
      
          /**
           * Create a new command instance.
           *
           * @return void
           */
          public function __construct()
          {
              parent::__construct();
          }
      
          /**
           * Execute the console command.
           *
           * @return int
           */
          public function handle()
          {
              $list_slug = $this->argument('list_slug');
              $list = LinkList::firstWhere('slug', $list_slug);
      
              if ($list === null) {
                  $this->error("Invalid or non-existent List.");
                  return 1;
              }
      
              if ($this->confirm("Confirm deleting the list '$list->title'? Links will be reassigned to the default list.")) {
                  $default_list = LinkList::firstWhere('slug', 'default');
                  if (!$default_list) {
                      $default_list = new LinkList();
                      $default_list->title="default";
                      $default_list->slug = 'default';
                      $default_list->save();
                  }
      
                  $this->info("Reassigning links to default list...");
      
                  Link::where('link_list_id', $list->id)->update(['link_list_id' => $default_list->id]);
      
                  $list->delete();
                  $this->info("List Deleted.");
              }
      
              return 0;
          }
      }
      

      Save the file.

      In the previous code, the handle() method starts by trying to locate a link list based on the provided slug. If a valid list can’t be found, the application exits in error. When a valid list is found, the confirm() method is called to ask the user for confirmation.

      When confirmed, the application will locate the default list or create a new one if necessary, assigning it to the $default_list variable.

      Next, it will locate and update all links that are associated with the list that is about to be deleted. The chained call to update() will update the referenced list ID on all links that match the query, using the condition defined within the previous where() call. This line is highlighted for your reference.

      Finally, the list is deleted with the delete() method, also highlighted. This method is available to all Eloquent models through the parent Model class.

      To delete a list, first run link:show to obtain all links currently in the database:

      • docker-compose exec app php artisan link:show

      Output

      +----+-------------------------------------------------+--------------+----------------------------------+ | id | url | list | description | +----+-------------------------------------------------+--------------+----------------------------------+ | 1 | https://digitalocean.com/community | digitalocean | DO Community | | 2 | https://digitalocean.com/community/tags/laravel | digitalocean | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | digitalocean | PHP Tutorials at DigitalOcean | | 4 | https://twitter.com/digitalocean | social | Twitter | | 5 | https://dev.to/digitalocean | social | DEV.to | | 6 | https://laravel.com/docs/8.x/eloquent | default | Laravel Eloquent Docs | +----+-------------------------------------------------+--------------+----------------------------------+

      To delete the digitalocean list and revert those links back to the default list, run:

      • docker-compose exec app php artisan list:delete digitalocean

      Confirm the deletion by typing y and hitting ENTER.

      Output

      Confirm deleting the list 'digitalocean'? Links will be reassigned to the default list. (yes/no) [no]: > y Reassigning links to default list... List Deleted.

      If you run the link:show() command again, you’ll see the updated information:

      Output

      +----+-------------------------------------------------+---------+----------------------------------+ | id | url | list | description | +----+-------------------------------------------------+---------+----------------------------------+ | 1 | https://digitalocean.com/community | default | DO Community | | 2 | https://digitalocean.com/community/tags/laravel | default | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | default | PHP Tutorials at DigitalOcean | | 4 | https://twitter.com/erikaheidi | social | Twitter | | 5 | https://dev.to/erikaheidi | social | DEV.to | | 6 | https://laravel.com/docs/8.x/eloquent | default | Laravel Eloquent Docs | +----+-------------------------------------------------+---------+----------------------------------+

      The application now has a dedicated command to delete lists of links.





      Source link