One place for hosting & domains

      OnetoMany

      How To Create a One-To-Many Relationship 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 practice the examples explained throughout the series, you’ll improve a demo application with new models and relationships.

      The demo Landing Laravel application that you set up as a prerequisite for this series contains a single database table to store links. In this tutorial you’ll modify this initial database structure to include a second table, which you will use to organize links into lists.

      For the links and lists example we’re going to use in this series, each link is part of only one list, but each list can have multiple links. This kind of relationship is also known as a one-to-many relationship.

      A one-to-many relationship happens when one item, which we’ll call type A, can be linked to several items of type B, but the opposite doesn’t hold true: an item of type B can only be linked to one item of type A. Transposing this scenario to the current demo application models, A is the list type, and B is the link type.

      To get started, you’ll need to create a model and a database table to represent a List of links. Then, you’ll update the existing Link model and table to include the relationship between both models. Because the term List is reserved for PHP internals, you won’t be able to name your new model with that term. You can call this new model LinkList instead.

      First, make sure you’re in the application directory:

      Create a new model using artisan:

      • docker-compose exec app php artisan make:model LinkList

      This will generate a new model class in the app/Model directory:

      app/Model/LinkList.php
      

      If you look at your app/Console/Commands directory, you’ll notice that there’s already a class file named LinkList.php. This is not to be confused with the Eloquent model you just created. This class contains a CLI command that lists all the links in the database via artisan.

      To avoid confusion in the future, now is a good moment to rename that class and its command signature to a different name. In this case use the class name LinkShow since that name also describes what the class does. To rename the app/Console/Commands/LinkList.php file to another valid name, run this command in your terminal:

      • mv app/Console/Commands/LinkList.php app/Console/Commands/LinkShow.php

      Then, open the file app/Console/Commands/LinkShow.php in your code editor to change the class name from LinkList to LinkShow, and the command signature from link:list to link:show, like the highlighted lines in the following code listing. This is how the file should look like once you’re finished:

      app/Console/Commands/LinkShow.php

      <?php
      
      namespace AppConsoleCommands;
      
      use AppModelsLink;
      use IlluminateConsoleCommand;
      
      class LinkShow extends Command
      {
          /**
           * The name and signature of the console command.
           *
           * @var string
           */
          protected $signature = 'link:show';
      
          /**
           * The console command description.
           *
           * @var string
           */
          protected $description = 'List links saved in the database';
      
          /**
           * Create a new command instance.
           *
           * @return void
           */
          public function __construct()
          {
              parent::__construct();
          }
      
          /**
           * Execute the console command.
           *
           * @return int
           */
          public function handle()
          {
              $headers = [ 'id', 'url', 'description' ];
              $links = Link::all(['id', 'url', 'description'])->toArray();
              $this->table($headers, $links);
      
              return 0;
          }
      }
      

      Save and close the file when you’re done. To check that everything worked as expected, run your newly renamed link:show artisan command:

      • docker-compose exec app php artisan link:show

      You’ll receive output like this:

      Output

      +----+-------------------------------------------------+----------------------------------+ | id | url | description | +----+-------------------------------------------------+----------------------------------+ | 1 | https://digitalocean.com/community | DigitalOcean Community | | 2 | https://digitalocean.com/community/tags/laravel | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | PHP Tutorials at DigitalOcean | +----+-------------------------------------------------+----------------------------------+

      The new app/Model/LinkList.php class that you generated with the previous artisan make:model command contains generic code for a new Eloquent class. Unlike other ORMs such as Doctrine, Eloquent doesn’t alter database structures, handling only data itself. Eloquent models are usually lean, with class properties automatically inferred from the model’s table structure.

      This approach to handling data-only with Eloquent means that you don’t need to set up any properties for the LinkList class because they will be inferred from the database table structure for that model.

      Structural database operations are typically handled in Laravel via database migrations. Migrations allow developers to programmatically define structural changes to the database, such as creating, modifying, and deleting tables.

      You’ll now create a new migration to set up the lists table in the database.

      The artisan command line tool included by default with Laravel contains several helper methods to bootstrap new components such as controllers, models, migrations, among others. To create a new migration using artisan, run:

      • docker-compose exec app php artisan make:migration create_link_lists_table

      Output

      Created Migration: 2021_07_07_152554_create_link_lists_table

      This command will generate a new file under the database/migrations directory in your Laravel application, using an auto-generated name based on the current date and time, and the migration name. That file contains generic code that you’ll modify to set up the lists table.

      Using your code editor, open the generated migration file. The file currently looks like this:

      database/migrations/2021_07_07_152554_create_link_lists_table.php

      <?php
      
      use IlluminateDatabaseMigrationsMigration;
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateSupportFacadesSchema;
      
      class CreateLinkListsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('link_lists', function (Blueprint $table) {
                  $table->id();
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('link_lists');
          }
      }
      
      

      The migration runs the up() method when executed with the artisan migrate command. This is where your table definition goes, and by default it creates an id primary key field and two timestamp fields (created_at and updated_at), defined with the timestamps() Schema method. Those fields are automatically filled by Eloquent when a model is created and updated, respectively. The down() method is called when the migration is rolled back with the artisan rollback command, and typically executes code to drop the table or revert structural changes.

      You’ll change the up method to include the following fields:

      • title: a string representing the title of this List
      • description: a string representing the description of a List
      • slug: a unique, short string based on the title, typically used to create user-friendly URLs

      In a one-to-many relationship, the many side (which in this scenario corresponds to the links table) is the one to hold the column reference (or foreign key) to the other element (corresponding to the lists table). That means you’ll have to modify the links table later on, in order to include a reference field that will link that table to the lists table.

      The lists table, on the other hand, doesn’t need any special field to reference its links.

      Replace the current content in your migration file with the following code:

      database/migrations/2021_07_07_152554_create_link_lists_table.php

      <?php
      
      use IlluminateDatabaseMigrationsMigration;
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateSupportFacadesSchema;
      
      class CreateLinkListsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('link_lists', function (Blueprint $table) {
                  $table->id();
                  $table->timestamps();
                  $table->string('title', 60);
                  $table->string('slug', 60)->unique();
                  $table->text('description')->nullable();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('link_lists');
          }
      }
      
      

      Save the file when you’re done.

      Next, open the existing links migration file in your code editor. In the demo project, you’ll find the migration at the following path:

      2020_11_18_165241_create_links_table.php
      

      First, include a use directive pointing to the fully qualified class name for the LinkList class, at the beginning of the file and right after the last use line:

      …
      use IlluminateSupportFacadesSchema;
      use AppModelsLinkList;
      ...
      

      Next, include the following line in the table definition, within the up method and right after the line that sets up the description field:

      $table->text('description');
      $table->foreignIdFor(LinkList::class);
      

      The foreignIdFor() method creates a foreign key column to the referenced Eloquent model. It uses default nomenclature to set up a field that is linked to the primary key field of the referenced table.

      This is how the full migration class should look like when you’re done:

      database/migrations/2020_11_18_165241_create_links_table.php

      <?php
      
      use IlluminateDatabaseMigrationsMigration;
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateSupportFacadesSchema;
      use AppModelsLinkList;
      
      class CreateLinksTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('links', function (Blueprint $table) {
                  $table->id();
                  $table->string('url', 200);
                  $table->text('description');
                  $table->foreignIdFor(LinkList::class);
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('links');
          }
      }
      

      Save the file when you’re finished editing it. Next, wipe the database and then run the migration command again to recreate your database structure with the updated migrations files:

      • docker-compose exec app php artisan db:wipe
      • docker-compose exec app php artisan migrate

      Configuring Eloquent Model Relationships

      The database tables are now set up, but you still need to configure the Eloquent models to define the relationship between them.

      On the List model, which is the one side of the relationship, you’ll set up a new method named links. This method will work as a proxy to access the links that are related to each list, using the hasMany method from the parent IlluminateDatabaseEloquentModel class.

      In your code editor, open the file app/Model/LinkList.php. Replace the current generic code with the following content:

      app/Model/LinkList.php

      <?php
      
      namespace AppModels;
      
      use IlluminateDatabaseEloquentFactoriesHasFactory;
      use IlluminateDatabaseEloquentModel;
      
      class LinkList extends Model
      {
          use HasFactory;
      
          public function links()
          {
              return $this->hasMany(Link::class);
          }
      }
      
      

      Save the file when you’re done.

      Next, you’ll edit the many side of the relationship to include a reference back to the List model, so that links can access their respective list. This is done with the belongsTo method from the parent Model class. This method is used to define the inverse side of the one-to-many relationship.

      Open the Link model in your code editor:

      app/Model/Link.php
      

      Replace the current content in your Link.php file with the following code:

      app/Model/Link.php

      <?php
      
      namespace AppModels;
      
      use IlluminateDatabaseEloquentModel;
      
      class Link extends Model
      {
          public function link_list()
          {
              return $this->belongsTo(LinkList::class);
          }
      }
      
      

      Save the file when you’re done.

      With both models updated, your database is now configured completely, but it is currently empty. In the next section of this series, you’ll learn how to insert new records in the database using Eloquent models.

      This tutorial is part of an ongoing weekly series about Laravel Eloquent. You can subscribe to the Laravel tag if you want to be notified when new tutorials are published.



      Source link

      How To Modify Items in a One-to-Many Database Relationships 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, and SQLite is a database engine that can be used with Python to store application data. In this tutorial, you’ll modify items in an application built using Flask and SQLite with a One-to-Many relationship.

      This tutorial is a continuation of How To Use One-to-Many Database Relationships with Flask and SQLite. After having followed it, you’ve successfully created a Flask application to manage to-do items, organize items in lists, and add new items to the database. In this tutorial, you will add the functionality to mark to-do items as complete, to edit and delete items, and to add new lists to the database. By the end of the tutorial, your application will include edit and delete buttons and strikethroughs for completed to-dos.

      Todo Application

      Prerequisites

      Before you start following this guide, you will need:

      Step 1 — Setting Up the Web Application

      In this step, you will set up the to-do application to be ready for modification. If you followed the tutorial in the prerequisites section and still have the code and the virtual environment in your local machine, you can skip this step.

      First use Git to clone the repository of the previous tutorial’s code:

      • git clone https://github.com/do-community/flask-todo

      Navigate to flask-todo:

      Then create a new virtual environment:

      Activate the environment:

      Install Flask:

      Then, initialize the database using the init_db.py program:

      Next, set the following environment variables:

      • export FLASK_APP=app
      • export FLASK_ENV=development

      FLASK_APP indicates the application you are currently developing, which is app.py in this case. FLASK_ENV specifies the mode—set it to development for development mode, this will allow you to debug the application. (Remember not to use this mode in a production environment.)

      Then run the development server:

      If you go to your browser, you’ll have the application running on the following URL at http://127.0.0.1:5000/.

      To close the development server, use the CTRL + C key combination.

      Next, you will modify the application to add the ability to mark items as complete.

      Step 2 — Marking To-Do Items as Complete

      In this step, you’ll add a button to mark each to-do item as complete.

      To be able to mark items as complete, you’ll add a new column to the items table in your database to have a marker for each item so you know whether it is completed or not, then you will create a new route in your app.py file to change the value of this column depending on the user’s action.

      As a reminder the columns in the items table are currently the following:

      • id: The ID of the item.
      • list_id: The ID of the list the item belongs to.
      • created: The item’s creation date.
      • content: The item’s content.

      First, open schema.sql to modify the items table:

      Add a new column named done to the items table:

      flask_todo/schema.sql

      DROP TABLE IF EXISTS lists;
      DROP TABLE IF EXISTS items;
      
      CREATE TABLE lists (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          title TEXT NOT NULL
      );
      
      CREATE TABLE items (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          list_id INTEGER NOT NULL,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          content TEXT NOT NULL,
          done INTEGER NOT NULL DEFAULT 0,
          FOREIGN KEY (list_id) REFERENCES lists (id)
      );
      

      Save and close the file.

      This new column will hold the integer values 0 or 1; the value 0 represents the Boolean value false and 1 represents the value true. The default is 0, which means any new items you add will automatically be unfinished until the user marks the item as complete, in which case the value of the done column will change to 1.

      Then, initialize the database again using the init_db.py program to apply the modifications you have performed on schema.sql:

      Next, open app.py for modification:

      You’ll fetch the id of the item and the value of the done column in the index() function, which fetches the lists and items from the database and sends them to the index.html file for display. The necessary changes to the SQL statement are highlighted in the following file:

      flask_todo/app.py

      @app.route('/')
      def index():
          conn = get_db_connection()
          todos = conn.execute('SELECT i.id, i.done, i.content, l.title 
                                FROM items i JOIN lists l 
                                ON i.list_id = l.id ORDER BY l.title;').fetchall()
      
          lists = {}
      
          for k, g in groupby(todos, key=lambda t: t['title']):
              lists[k] = list(g)
      
          conn.close()
          return render_template('index.html', lists=lists)
      

      Save and close the file.

      With this modification, you get the IDs of the to-do items using i.id and the values of the done column using i.done.

      To understand this change, open list_example.py, which is a small, example program you can use to understand the contents of the database:

      Perform the same modification to the SQL statement as before, then change the last print() function to display the item ID and the value of done:

      flask_todo/list_example.py

      from itertools import groupby
      from app import get_db_connection
      
      conn = get_db_connection()
      
      todos = conn.execute('SELECT i.id, i.done, i.content, l.title 
                            FROM items i JOIN lists l 
                            ON i.list_id = l.id ORDER BY l.title;').fetchall()
      
      lists = {}
      
      for k, g in groupby(todos, key=lambda t: t['title']):
          lists[k] = list(g)
      
      for list_, items in lists.items():
          print(list_)
          for item in items:
              print('    ', item['content'], '| id:',
                    item['id'], '| done:', item['done'])
      

      Save and exit the file.

      Run the example program:

      Here is the output:

      Output

      Home Buy fruit | id: 2 | done: 0 Cook dinner | id: 3 | done: 0 Study Learn Flask | id: 4 | done: 0 Learn SQLite | id: 5 | done: 0 Work Morning meeting | id: 1 | done: 0

      None of the items has been marked as completed so the value of done for each item is 0, which means false. To allow users to change this value and mark items as completed, you will add a new route to the app.py file.

      Open app.py:

      Add a route /do/ at the end of the file:

      flask_todo/app.py

      . . .
      @app.route('/<int:id>/do/', methods=('POST',))
      def do(id):
          conn = get_db_connection()
          conn.execute('UPDATE items SET done = 1 WHERE id = ?', (id,))
          conn.commit()
          conn.close()
          return redirect(url_for('index'))
      

      This new route accepts only POST requests. The do() view function takes an id argument—this is the ID of the item you want to mark as completed. Inside the function, you open a database connection, then you use an UPDATE SQL statement to set the value of the done column to 1 for the item to be marked as completed.

      You use the ? placeholder in the execute() method and pass a tuple containing the ID to safely insert data into the database. Then you commit the transaction and close the connection and redirect to the index page.

      After adding a route to mark items as completed, you need another route to undo this action and return the item to a non-completed status. Add the following route at the end of the file:

      flask_todo/app.py

      . . .
      @app.route('/<int:id>/undo/', methods=('POST',))
      def undo(id):
          conn = get_db_connection()
          conn.execute('UPDATE items SET done = 0 WHERE id = ?', (id,))
          conn.commit()
          conn.close()
          return redirect(url_for('index'))
      

      This route is similar to the /do/ route, and the undo() view function is exactly the same as the do() function except that you set the value of done to 0 instead of 1.

      Save and close the app.py file.

      You now need a button to mark to-do items as completed or uncompleted depending on the state of the item, open the index.html template file:

      • nano templates/index.html

      Change the contents of the inner for loop inside the <ul> element to look as follows:

      flask_todo/templates/index.html

      {% block content %}
          <h1>{% block title %} Welcome to FlaskTodo {% endblock %}</h1>
          {% for list, items in lists.items() %}
              <div class="card" style="width: 18rem; margin-bottom: 50px;">
                  <div class="card-header">
                      <h3>{{ list }}</h3>
                  </div>
                  <ul class="list-group list-group-flush">
                      {% for item in items %}
                          <li class="list-group-item"
                          {% if item['done'] %}
                          style="text-decoration: line-through;"
                          {% endif %}
                          >{{ item['content'] }}
                          {% if not item ['done'] %}
                              {% set URL = 'do' %}
                              {% set BUTTON = 'Do' %}
                          {% else %}
                              {% set URL = 'undo' %}
                              {% set BUTTON = 'Undo' %}
                          {% endif %}
      
      
      
                        <div class="row">
                              <div class="col-12 col-md-3">
                                  <form action="{{ url_for(URL, id=item['id']) }}"
                                      method="POST">
                                      <input type="submit" value="{{ BUTTON }}"
                                          class="btn btn-success btn-sm">
                                  </form>
                              </div>
                          </div>
                          </li>
                      {% endfor %}
                  </ul>
              </div>
          {% endfor %}
      {% endblock %}
      

      In this for loop, you use a line-through CSS value for the text-decoration property if the item is marked as completed, which you know from the value of item['done']. You then use the Jinja syntax set to declare two variables, URL and BUTTON. If the item is not marked as completed the button will have the value Do and the URL will direct to the /do/ route, and if the item was marked as completed, the button will have a value of Undo and will point to /undo/. After, you use both these variables in an input form that submits the proper request depending on the state of the item.

      Run the server:

      You can now mark items as completed on the index page http://127.0.0.1:5000/. Next you will add the ability to edit to-do items.

      Step 3 — Editing To-Do Items

      In this step, you will add a new page for editing items so you can modify the contents of each item and assign items to different lists.

      You will add a new /edit/ route to the app.py file, which will render a new edit.html page in which a user can modify existing items. You will also update the index.html file to add an Edit button to each item.

      First, open the app.py file:

      Then add the following route at the end of the file:

      flask_todo/app.py

      . . .
      @app.route('/<int:id>/edit/', methods=('GET', 'POST'))
      def edit(id):
          conn = get_db_connection()
      
          todo = conn.execute('SELECT i.id, i.list_id, i.done, i.content, l.title 
                               FROM items i JOIN lists l 
                               ON i.list_id = l.id WHERE i.id = ?', (id,)).fetchone()
      
          lists = conn.execute('SELECT title FROM lists;').fetchall()
      
          if request.method == 'POST':
              content = request.form['content']
              list_title = request.form['list']
      
              if not content:
                  flash('Content is required!')
                  return redirect(url_for('edit', id=id))
      
              list_id = conn.execute('SELECT id FROM lists WHERE title = (?);',
                                       (list_title,)).fetchone()['id']
      
              conn.execute('UPDATE items SET content = ?, list_id = ?
                            WHERE id = ?',
                           (content, list_id, id))
              conn.commit()
              conn.close()
              return redirect(url_for('index'))
      
          return render_template('edit.html', todo=todo, lists=lists)
      

      In this new view function, you use the id argument to fetch the ID of the to-do item you want to edit, the ID of the list it belongs to, the value of the done column, the content of the item, and the list title using a SQL JOIN. You save this data in the todo variable. Then you get all of the to-do lists from the database and save them in the lists variable.

      If the request is a normal GET request, the condition if request.method == 'POST' does not run, so the application executes the last render_template() function, passing both todo and lists to an edit.html file.

      If however, a form was submitted, the condition request.method == 'POST' becomes true, in which case you extract the content and the list title the user submitted. If no content was submitted, you flash the message Content is required! and redirect to the same edit page. Otherwise, you fetch the ID of the list the user submitted; this allows the user to move a to-do item from one list to another. Then, you use an UPDATE SQL statement to set the content of the to-do item to the new content the user submitted. You do the same for the list ID. Finally, you commit the changes and close the connection, and redirect the user to the index page.

      Save and close the file.

      To use this new route, you need a new template file called edit.html:

      Add the following contents to this new file:

      flask_todo/templates/edit.html

      {% extends 'base.html' %}
      
      {% block content %}
      
      <h1>{% block title %} Edit an Item {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="content">Content</label>
              <input type="text" name="content"
                     placeholder="Todo content" class="form-control"
                     value="{{ todo['content'] or request.form['content'] }}"></input>
          </div>
      
          <div class="form-group">
              <label for="list">List</label>
              <select class="form-control" name="list">
                  {% for list in lists %}
                      {% if list['title'] == request.form['list'] %}
                          <option value="{{ request.form['list'] }}" selected>
                              {{ request.form['list'] }}
                          </option>
      
                      {% elif list['title'] == todo['title'] %}
                          <option value="{{ todo['title'] }}" selected>
                              {{ todo['title'] }}
                          </option>
      
                      {% else %}
                          <option value="{{ list['title'] }}">
                              {{ list['title'] }}
                          </option>
                      {% endif %}
                  {% endfor %}
              </select>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      {% endblock %}
      

      You use the value {{ todo['content'] or request.form['content'] }} for the content input. This signifies that the value will be either the current content of the to-do item or what the user has submitted in a failed attempt to submit the form.

      For the list selection form, you loop through the lists variable, and if the list title is the same as the one stored in the request.form object (from a failed attempt), then set that list title as the selected value. Otherwise if the list title equals the one stored in the todo variable, then set it as the selected value. This is the current list title of the to-do item before any modification; the rest of the options are then displayed without the selected attribute.

      Save and close the file.

      Then, open index.html to add an Edit button:

      • nano templates/index.html

      Change the contents of the div tag with the "row" class to add another column as follows:

      flask_todo/templates/index.html

      . . .
      <div class="row">
          <div class="col-12 col-md-3">
              <form action="{{ url_for(URL, id=item['id']) }}"
                  method="POST">
                  <input type="submit" value="{{ BUTTON }}"
                      class="btn btn-success btn-sm">
              </form>
          </div>
          <div class="col-12 col-md-3">
              <a class="btn btn-warning btn-sm"
              href="https://www.digitalocean.com/community/tutorials/{{ url_for("edit', id=item['id']) }}">Edit</a>
          </div>
      </div>
      

      Save and close the file.

      This is a standard <a> link tag that points to the relevant /edit/ route for each item.

      Run the server if you haven’t already:

      You can now go to the index page http://127.0.0.1:5000/ and experiment with modifying to-do items. In the next step, you will add a button to delete items.

      Step 4 — Deleting To-Do Items

      In this step, you will add the ability to delete specific to-do items.

      You will first need to add a new /delete/ route, open app.py:

      Then add the following route at the end of the file:

      flask_todo/app.py

      . . .
      @app.route('/<int:id>/delete/', methods=('POST',))
      def delete(id):
          conn = get_db_connection()
          conn.execute('DELETE FROM items WHERE id = ?', (id,))
          conn.commit()
          conn.close()
          return redirect(url_for('index'))
      

      Save and close the file.

      The delete() view function accepts an id argument. When a POST request gets sent, you use the DELETE SQL statement to delete the item with the matching id value, then you commit the transaction and close the database connection, and return to the index page.

      Next, open templates/index.html to add a Delete button:

      • nano templates/index.html

      Add the following highlighted div tag below the Edit button:

      flask_todo/templates/index.html

      <div class="row">
          <div class="col-12 col-md-3">
              <form action="{{ url_for(URL, id=item['id']) }}"
                  method="POST">
                  <input type="submit" value="{{ BUTTON }}"
                      class="btn btn-success btn-sm">
              </form>
          </div>
      
          <div class="col-12 col-md-3">
              <a class="btn btn-warning btn-sm"
              href="https://www.digitalocean.com/community/tutorials/{{ url_for("edit', id=item['id']) }}">Edit</a>
          </div>
      
          <div class="col-12 col-md-3">
              <form action="https://www.digitalocean.com/community/tutorials/{{ url_for("delete', id=item['id']) }}"
                  method="POST">
                  <input type="submit" value="Delete"
                      class="btn btn-danger btn-sm">
              </form>
          </div>
      </div>
      

      This new submit button sends a POST request to the /delete/ route for each item.

      Save and close the file.

      Then run the development server:

      Go to the index page and try out the new Delete button—you can now delete any item you want.

      Now that you have added the ability to delete existing to-do items, you will move on to add the ability to add new lists in the next step.

      Step 5 — Adding New Lists

      So far, lists can only be added directly from the database. In this step, you will add the ability to create new lists when the user adds a new item, instead of only choosing between the existing lists. You will incorporate a new option called New List, which when chosen, the user can input the name of the new list they wish to create.

      First, open app.py:

      Then, modify the create() view function by adding the following highlighted lines to the if request.method == 'POST' condition:

      flask_todo/app.py

      . . .
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          conn = get_db_connection()
      
          if request.method == 'POST':
              content = request.form['content']
              list_title = request.form['list']
      
              new_list = request.form['new_list']
      
              # If a new list title is submitted, add it to the database
              if list_title == 'New List' and new_list:
                  conn.execute('INSERT INTO lists (title) VALUES (?)',
                               (new_list,))
                  conn.commit()
                  # Update list_title to refer to the newly added list
                  list_title = new_list
      
              if not content:
                  flash('Content is required!')
                  return redirect(url_for('index'))
      
              list_id = conn.execute('SELECT id FROM lists WHERE title = (?);',
                                       (list_title,)).fetchone()['id']
              conn.execute('INSERT INTO items (content, list_id) VALUES (?, ?)',
                           (content, list_id))
              conn.commit()
              conn.close()
              return redirect(url_for('index'))
      
          lists = conn.execute('SELECT title FROM lists;').fetchall()
      
          conn.close()
          return render_template('create.html', lists=lists)
      

      Save and close the file.

      Here you save the value of a new form field called new_list in a variable. You will add this field later to the create.html file. Next, in the list_title == 'New List' and new_list condition, you check whether the list_title has the value 'New List', which indicates that the user wishes to create a new list. You also check that the value of the new_list variable is not None, if this condition is met, you use an INSERT INTO SQL statement to add the newly submitted list title to the lists table. You commit the transaction, then you update the value of the list_title variable to match that of the newly added list for later use.

      Next, open create.html to add a new <option> tag to let the user add a new list:

      • nano templates/create.html

      Modify the file by adding the highlighted tags in the following code:

      flask_todo/templates/create.html

          <div class="form-group">
              <label for="list">List</label>
              <select class="form-control" name="list">
                  <option value="New List" selected>New List</option>
                  {% for list in lists %}
                      {% if list['title'] == request.form['list'] %}
                          <option value="{{ request.form['list'] }}" selected>
                              {{ request.form['list'] }}
                          </option>
                      {% else %}
                          <option value="{{ list['title'] }}">
                              {{ list['title'] }}
                          </option>
                      {% endif %}
                  {% endfor %}
              </select>
          </div>
      
          <div class="form-group">
              <label for="new_list">New List</label>
              <input type="text" name="new_list"
                      placeholder="New list name" class="form-control"
                      value="{{ request.form['new_list'] }}"></input>
          </div>
      
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      

      Save and close the file.

      You have added a new <option> tag to refer to the New List option, this will allow the user to specify that they want to create a new list. Then you add another <div> with an input field named new_list, this field is where the user will input the title of the new list they wish to create.

      Finally, run the development server:

      Then visit the index page:

      http://127.0.0.1:5000/
      

      The application will now look as follows:

      Todo Application

      With the new additions to your application, users can now mark to-do items as complete or restore completed items to a non-completion state, edit and delete existing items, and create new lists for different kinds of to-do tasks.

      You can browse the full source code of the application in the DigitalOcean Community Repository.

      Conclusion

      You now have a complete to-do application in which users can create new to-do items, mark an item as complete, and edit or delete existing items, in addition to the ability to create new lists. You have modified a Flask web application, added new features to it, and modified database items specifically in a One-to-Many relationship. You may develop this application further by learning How To Add Authentication to Your App with Flask-Login to add security to your Flask application.



      Source link

      How To Use One-to-Many Database Relationships 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, and SQLite is a database engine that can be used with Python to store application data. In this tutorial, you will use Flask with SQLite to create a to-do application where users can create lists of to-do items. You will learn how to use SQLite with Flask and how one-to-many database relationships work.

      A one-to-many database relationship is a relationship between two database tables where a record in one table can reference several records in another table. For example, in a blogging application, a table for storing posts can have a one-to-many relationship with a table for storing comments. Each post can reference many comments, and each comment references a single post; therefore, one post has a relationship with many comments. The post table is a parent table, while the comments table is a child table—a record in the parent table can reference many records in the child table. This is important to be able to have access to related data in each table.

      We’ll use SQLite because it is portable and does not need any additional set up to work with Python. It is also great for prototyping an application before moving to a larger database such as MySQL or Postgres. For more on how to choose the right database system read our SQLite vs MySQL vs PostgreSQL: A Comparison Of Relational Database Management Systems article.

      Prerequisites

      Before you start following this guide, you will need:

      Step 1 — Creating the Database

      In this step, you will activate your programming environment, install Flask, create the SQLite database, and populate it with sample data. You’ll learn how to use foreign keys to create a one-to-many relationship between lists and items. A foreign key is a key used to associate a database table with another table, it is the link between the child table and its parent table.

      If you haven’t already activated your programming environment, make sure you’re in your project directory (flask_todo) and use this command to activate it:

      Once your programming environment is activated, install Flask using the following command:

      Once the installation is complete, you can now create the database schema file that contains SQL commands to create the tables you need to store your to-do data. You will need two tables: a table called lists to store to-do lists, and an items table to store the items of each list.

      Open a file called schema.sql inside your flask_todo directory:

      Type the following SQL commands inside this file:

      flask_todo/schema.sql

      DROP TABLE IF EXISTS lists;
      DROP TABLE IF EXISTS items;
      
      CREATE TABLE lists (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          title TEXT NOT NULL
      );
      
      CREATE TABLE items (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          list_id INTEGER NOT NULL,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          content TEXT NOT NULL,
          FOREIGN KEY (list_id) REFERENCES lists (id)
      );
      

      Save and close the file.

      The first two SQL command are DROP TABLE IF EXISTS lists; and DROP TABLE IF EXISTS items;, these delete any already existing tables named lists and items so you don’t see confusing behavior. 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.

      Next, you use CREATE TABLE lists to create the lists table that will store the to-do lists (such as a study list, work list, home list, and so on) with the following columns:

      • id: An integer that represents a primary key, this will get assigned a unique value by the database for each entry (i.e. to-do list).
      • created: The time the to-do list was created at. 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 list 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 list title.

      Then, you create a table called items to store to-do items. This table has an ID, a list_id integer column to identify which list an item belongs to, a creation date, and the item’s content. To link an item to a list in the database you use a foreign key constraint with the line FOREIGN KEY (list_id) REFERENCES lists (id). Here the lists table is a parent table, which is the table that is being referenced by the foreign key constraint, this indicates a list can have multiple items. The items table is a child table, which is the table the constraint applies to. This means items belong to a single list. The list_id column references the id column of the lists parent table.

      Since a list can have many items, and an item belongs to only one list, the relationship between the lists and items tables is a one-to-many relationship.

      Next, you will use the schema.sql file to create the database. Open a file named init_db.py inside the flask_todo directory:

      Then add the following code:

      flask_todo/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 lists (title) VALUES (?)", ('Work',))
      cur.execute("INSERT INTO lists (title) VALUES (?)", ('Home',))
      cur.execute("INSERT INTO lists (title) VALUES (?)", ('Study',))
      
      cur.execute("INSERT INTO items (list_id, content) VALUES (?, ?)",
                  (1, 'Morning meeting')
                  )
      
      cur.execute("INSERT INTO items (list_id, content) VALUES (?, ?)",
                  (2, 'Buy fruit')
                  )
      
      cur.execute("INSERT INTO items (list_id, content) VALUES (?, ?)",
                  (2, 'Cook dinner')
                  )
      
      cur.execute("INSERT INTO items (list_id, content) VALUES (?, ?)",
                  (3, 'Learn Flask')
                  )
      
      cur.execute("INSERT INTO items (list_id, content) VALUES (?, ?)",
                  (3, 'Learn SQLite')
                  )
      
      connection.commit()
      connection.close()
      

      Save and close the file.

      Here you connect to a file called database.db that will be created once you execute this program. You then open the schema.sql file and run it using the executescript() method that executes multiple SQL statements at once.

      Running schema.sql will create the lists and items tables. Next, using a Cursor object, you execute a few INSERT SQL statements to create three lists and five to-do items.

      You use the list_id column to link each item to a list via the list’s id value. For example, the Work list was the first insertion into the database, so it will have the ID 1. This is how you can link the Morning meeting to-do item to Work—the same rule applies to the other lists and items.

      Finally, you commit the changes and close the connection.

      Run the program:

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

      You’ve activated your environment, installed Flask, and created the SQLite database. Next, you’ll retrieve the lists and items from the database and display them in the application’s homepage.

      Step 2 — Displaying To-do Items

      In this step, you will connect the database you created in the previous step to a Flask application that displays the to-do lists and the items of each list. You will learn how to use SQLite joins to query data from two tables and how to group to-do items by their lists.

      First, you will create the application file. Open a file named app.py inside the flask_todo directory:

      And then add the following code to the file:

      flask_todo/app.py

      from itertools import groupby
      import sqlite3
      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()
          todos = conn.execute('SELECT i.content, l.title FROM items i JOIN lists l 
                                ON i.list_id = l.id ORDER BY l.title;').fetchall()
      
          lists = {}
      
          for k, g in groupby(todos, key=lambda t: t['title']):
              lists[k] = list(g)
      
          conn.close()
          return render_template('index.html', lists=lists)
      

      Save and close the file.

      The get_db_connection() function opens a connection to the database.db database file and then sets the row_factory attribute to sqlite3.Row. In this way 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.

      In the index() view function, you open a database connection and execute the following SQL query:

      SELECT i.content, l.title FROM items i JOIN lists l ON i.list_id = l.id ORDER BY l.title;
      

      You then retrieve its results by using the fetchall() method and save the data in a variable called todos.

      In this query, you use SELECT to get the content of the item and the title of the list it belongs to by joining both the items and lists tables (with the table aliases i for items and l for lists). With the join condition i.list_id = l.id after the ON keyword, you will get each row from the items table with every row from the lists table where the list_id column of the items table matches the id of the lists table. You then use ORDER BY to order the results by list titles.

      To understand this query better, open the Python REPL in your flask_todo directory:

      To understand the SQL query, examine the contents of the todos variable by running this small program:

      • from app import get_db_connection
      • conn = get_db_connection()
      • todos = conn.execute('SELECT i.content, l.title FROM items i JOIN lists l
      • ON i.list_id = l.id ORDER BY l.title;').fetchall()
      • for todo in todos:
      • print(todo['title'], ':', todo['content'])

      You first import the get_db_connection from the app.py file then open a connection and execute the query (note that this is the same SQL query you have in your app.py file). In the for loop you print the title of the list and the content of each to-do item.

      The output will be as follows:

      Output

      Home : Buy fruit Home : Cook dinner Study : Learn Flask Study : Learn SQLite Work : Morning meeting

      Close the REPL using CTRL + D.

      Now that you understand how SQL joins work and what the query achieves, let’s return back to the index() view function in your app.py file. After declaring the todos variable, you group the results using the following code:

      lists = {}
      
      for k, g in groupby(todos, key=lambda t: t['title']):
          lists[k] = list(g)
      

      You first declare an empty dictionary called lists, then use a for loop to go through a grouping of the results in the todos variable by the list’s title. You use the groupby() function you imported from the itertools standard library. This function will go through each item in the todos variable and generate a group of results for each key in the for loop.

      k represents list titles (that is, Home, Study, Work), which are extracted using the function you pass to the key parameter of the groupby() function. In this case the function is lambda t: t['title'] that takes a to-do item and returns the title of the list (as you have done before with todo['title'] in the previous for loop). g represents the group that contains the to-do items of each list title. For example, in the first iteration, k will be 'Home', while g is an iterable that will contain the items 'Buy fruit' and 'Cook dinner'.

      This gives us a representation of the one-to-many relationship between lists and items, where each list title has several to-do items.

      When running the app.py file, and after the for loop finishes execution, lists will be as follows:

      Output

      {'Home': [<sqlite3.Row object at 0x7f9f58460950>, <sqlite3.Row object at 0x7f9f58460c30>], 'Study': [<sqlite3.Row object at 0x7f9f58460b70>, <sqlite3.Row object at 0x7f9f58460b50>], 'Work': [<sqlite3.Row object at 0x7f9f58460890>]}

      Each sqlite3.Row object will contain the data you retrieved from the items table using the SQL query in the index() function. To represent this data better, let’s make a program that goes through the lists dictionary and displays each list and its items.

      Open a file called list_example.py in your flask_todo directory:

      Then add the following code:

      flask_todo/list_example.py

      
      from itertools import groupby
      from app import get_db_connection
      
      conn = get_db_connection()
      todos = conn.execute('SELECT i.content, l.title FROM items i JOIN lists l 
                              ON i.list_id = l.id ORDER BY l.title;').fetchall()
      
      lists = {}
      
      for k, g in groupby(todos, key=lambda t: t['title']):
          lists[k] = list(g)
      
      for list_, items in lists.items():
          print(list_)
          for item in items:
              print('    ', item['content'])
      

      Save and close the file.

      This is very similar to the content in your index() view function. The last for loop here illustrates how the lists dictionary is structured. You first go through the dictionary’s items, print the list title (which is in the list_ variable), then go through each group of to-do items that belong to the list and print the content value of the item.

      Run the list_example.py program:

      Here is the output of list_example.py:

      Output

      Home Buy fruit Cook dinner Study Learn Flask Learn SQLite Work Morning meeting

      Now that you understand each part of the index() function, let’s create a base template and create the index.html file you rendered using the line return render_template('index.html', lists=lists).

      In your flask_todo 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, see Step 3 of How To Make a Web Application Using Flask in Python 3:

      flask_todo/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/{{ 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="https://www.digitalocean.com/#">About</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% 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>
      

      Save and close the file.

      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.

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

      • nano templates/index.html

      Add the following code to index.html:

      flask_todo/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskTodo {% endblock %}</h1>
          {% for list, items in lists.items() %}
              <div class="card" style="width: 18rem; margin-bottom: 50px;">
                  <div class="card-header">
                      <h3>{{ list }}</h3>
                  </div>
                  <ul class="list-group list-group-flush">
                      {% for item in items %}
                          <li class="list-group-item">{{ item['content'] }}</li>
                      {% endfor %}
                  </ul>
              </div>
          {% endfor %}
      {% endblock %}
      

      Here you use a for loop to go through each item of the lists dictionary, you display the list title as a card header inside an <h3> tag, and then use a list group to display each to-do item that belongs to the list in an <li> tag. This follows the same rules explained in the list_example.py program.

      You will now set the environment variables Flask needs and run the application using the following commands:

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

      Once the development server is running, you can visit the URL http://127.0.0.1:5000/ in your browser. You will see a web page with the “Welcome to FlaskTodo” and your list items.

      Home Page

      You can now type CTRL + C to stop your development server.

      You’ve created a Flask application that displays the to-do lists and the items of each list. In the next step, you will add a new page for creating new to-do items.

      Step 3 — Adding New To-do Items

      In this step, you will make a new route for creating to-do items, you will insert data into database tables, and associate items with the lists they belong to.

      First, open the app.py file:

      Then, add a new /create route with a view function called create() at the end of the file:

      flask_todo/app.py

      ...
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          conn = get_db_connection()
          lists = conn.execute('SELECT title FROM lists;').fetchall()
      
          conn.close()
          return render_template('create.html', lists=lists)
      

      Save and close the file.

      Because 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 then get all the list titles available in the database, close the connection, and render a create.html template passing it the list titles.

      Next, open a new template file called create.html:

      • nano templates/create.html

      Add the following HTML code to create.html:

      flask_todo/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
      <h1>{% block title %} Create a New Item {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="content">Content</label>
              <input type="text" name="content"
                     placeholder="Todo content" class="form-control"
                     value="{{ request.form['content'] }}"></input>
          </div>
      
          <div class="form-group">
              <label for="list">List</label>
              <select class="form-control" name="list">
                  {% for list in lists %}
                      {% if list['title'] == request.form['list'] %}
                          <option value="{{ request.form['list'] }}" selected>
                              {{ request.form['list'] }}
                          </option>
                      {% else %}
                          <option value="{{ list['title'] }}">
                              {{ list['title'] }}
                          </option>
                      {% endif %}
                  {% endfor %}
              </select>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      {% endblock %}
      

      Save and close the file.

      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 to-do content was provided). In the <select> element, you loop through the lists you retrieved from the database in the create() function. If the list title is equal to what is stored in request.form then the selected option is that list title, otherwise, you display the list title in a normal non-selected <option> tag.

      Now, in the terminal, run your Flask application:

      Then visit http://127.0.0.1:5000/create in your browser, you will see a form for creating a new to-do item, note that the form doesn’t work yet because you have no code to handle POST requests that get sent by the browser when submitting the form.

      Type CTRL + C to stop your development server.

      Next, let’s add the code for handling POST requests to the create() function and make the form function properly, open app.py:

      Then edit the create() function to look like so:

      flask_todo/app.py

      ...
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          conn = get_db_connection()
      
          if request.method == 'POST':
              content = request.form['content']
              list_title = request.form['list']
      
              if not content:
                  flash('Content is required!')
                  return redirect(url_for('index'))
      
              list_id = conn.execute('SELECT id FROM lists WHERE title = (?);',
                                       (list_title,)).fetchone()['id']
              conn.execute('INSERT INTO items (content, list_id) VALUES (?, ?)',
                           (content, list_id))
              conn.commit()
              conn.close()
              return redirect(url_for('index'))
      
          lists = conn.execute('SELECT title FROM lists;').fetchall()
      
          conn.close()
          return render_template('create.html', lists=lists)
      

      Save and close the file.

      Inside the request.method == 'POST' condition you get the to-do item’s content and the list’s title from the form data. If no content was submitted, you send the user a message using the flash() function and redirect to the index page. If this condition was not triggered, then you execute a SELECT statement to get the list ID from the provided list title and save it in a variable called list_id. You then execute an INSERT INTO statement to insert the new to-do item into the items table. You use the list_id variable to link the item to the list it belongs to. Finally, you commit the transaction, close the connection, and redirect to the index page.

      As a last step, you will add a link to /create in the navigation bar and display flashed messages below it, to do this, open base.html:

      Edit the file by adding a new <li> navigation item that links to the create() view function. Then display the flashed messages using a for loop above the content block. These are available in the get_flashed_messages() Flask function:

      flask_todo/templates/base.html

      <nav class="navbar navbar-expand-md navbar-light bg-light">
          <a class="navbar-brand" href="https://www.digitalocean.com/{{ 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="https://www.digitalocean.com/{{ url_for("create') }}">New</a>
              </li>
      
              <li class="nav-item active">
                  <a class="nav-link" href="https://www.digitalocean.com/#">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>
      

      Save and close the file.

      Now, in the terminal, run your Flask application:

      A new link to /create will appear in the navigation bar. If you navigate to this page and try to add a new to-do item with no content, you’ll receive a flashed message saying Content is required!. If you fill in the content form, a new to-do item will appear on the index page.

      In this step, you have added the ability to create new to-do items and save them to the database.

      You can find the source code for this project in this repository.

      Conclusion

      You now have an application to manage to-do lists and items. Each list has several to-do items and each to-do item belongs to a single list in a one-to-many relationship. You learned how to use Flask and SQLite to manage multiple related database tables, how to use foreign keys and how to retrieve and display related data from two tables in a web application using SQLite joins.

      Furthermore, you grouped results using the groupby() function, inserted new data to the database, and associated database table rows with the tables they are related to. You can learn more about foreign keys and database relationships from the SQLite documentation.

      You can also read more of our Python Framework content. If you want to check out the sqlite3 Python module, read our tutorial on How To Use the sqlite3 Module in Python 3.



      Source link