One place for hosting & domains

      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

      Erstellen einer Webanwendung mit Flask in Python 3


      Der Autor hat den Free and Open Source Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Flask ist ein kleines und schlankes Python-Web-Framework mit nützlichen Tools und Funktionen, die das Erstellen von Webanwendungen in Python erleichtern. Es bietet Entwicklern mehr Flexibilität und ist ein besser zugängliches Framework für neue Entwickler, da Sie Webanwendungen schnell unter Verwendung einer einzigen Python-Datei erstellen können. Außerdem ist Flask erweiterbar und setzt keine bestimmte Verzeichnisstruktur oder komplizierte Codebausteine voraus, bevor Sie loslegen können.

      Als Teil dieses Tutorials nutzen Sie das Bootstrap-Toolkit, um Ihre Anwendung so zu gestalten, dass sie optisch ansprechender aussieht. Bootstrap wird Ihnen helfen, responsive Webseiten in Ihre Webanwendung zu integrieren, sodass sie problemlos auch mit mobilen Browsern funktioniert, ohne dass Sie dafür eigenen HTML-, CSS- und JavaScript-Code schreiben müssen. Mit dem Toolkit können Sie sich darauf konzentrieren, die Funktionsweise von Flask zu erlernen.

      Flask verwendet die Jinja-Vorlagen-Engine für das dynamische Einrichten von HTML-Seiten mit bekannten Python-Konzepten wie Variablen, Schleifen, Listen usw. Sie werden diese Vorlagen im Rahmen dieses Projekts nutzen.

      In diesem Tutorial entwickeln Sie einen kleinen Weblog mit Flask und SQLite in Python 3. Benutzer der Anwendung können alle Beiträge in Ihrer Datenbank anzeigen und auf den Titel eines Beitrags klicken, um dessen Inhalt anzuzeigen und bei Bedarf der Datenbank einen neuen Beitrag hinzuzufügen bzw. einen bestehenden Beitrag zu bearbeiten oder zu löschen.

      Voraussetzungen

      Bevor Sie mit diesem Leitfaden fortfahren, benötigen Sie Folgendes:

      Schritt 1 – Installieren von Flask

      In diesem Schritt aktivieren Sie Ihre Python-Umgebung und installieren Flask mit dem Package Installer pip.

      Wenn Sie Ihre Programmierumgebung noch nicht aktiviert haben, stellen Sie sicher, dass Sie sich in Ihrem Projektverzeichnis befinden (flask_blog) und den folgenden Befehl nutzen, um die Umgebung zu aktivieren:

      Sobald Ihre Programmierumgebung aktiviert ist, weist Ihre Eingabeaufforderung das Präfix env auf, was wie folgt aussieht:

      Dieses Präfix ist ein Hinweis darauf, dass die Umgebung env derzeit aktiv ist; sie trägt möglicherweise einen anderen Namen, je nach dem Namen, den Sie der Umgebung bei der Erstellung gegeben haben.

      Anmerkung: Sie können Git, ein System für die Versionskontrolle, verwenden, um den Entwicklungsprozess für Ihr Projekt zu verwalten und zu verfolgen. Um mehr über die Verwendung von Git zu erfahren, lesen Sie unseren Artikel Einleitung zu Installation, Nutzung und Verzweigungen von Git.

      Wenn Sie Git verwenden, ist es eine gute Idee, das neu erstellte Verzeichnis env in Ihrer Datei .gitignore zu ignorieren, um eine Verfolgung von Dateien zu vermeiden, die nicht mit dem Projekt in Verbindung stehen.

      Nun installieren Sie Python-Pakete und isolieren Ihren Projektcode abseits von der Hauptinstallation des Python-Systems. Sie werden das mithilfe von pip und python tun.

      Um Flask zu installieren, führen Sie den folgenden Befehl aus:

      Sobald die Installation abgeschlossen ist, führen Sie den folgenden Befehl aus, um die Installation zu überprüfen:

      • python -c "import flask; print(flask.__version__)"

      Sie nutzen die python-Befehlszeilenschnittstelle mit der Option -c, um Python-Code auszuführen. Als Nächstes importieren Sie das flask-Paket mit import flask und drucken dann die Flask-Version, die über die Variable flask.__version__ verfügbar ist.

      Die Ausgabe wird eine Versionsnummer sein, die der folgenden ähnelt:

      Output

      1.1.2

      Sie haben den Projektordner und eine virtuelle Umgebung eingerichtet sowie Flask installiert. Sie können nun mit der Einrichtung Ihrer Basisanwendung fortfahren.

      Schritt 2 — Erstellen einer Basisanwendung

      Nachdem Sie Ihre Programmierumgebung eingerichtet haben, beginnen Sie nun mit der Verwendung von Flask. In diesem Schritt erstellen Sie eine kleine Webanwendung in einer Python-Datei und führen sie aus, um den Server zu starten. Dadurch werden im Browser verschiedene Informationen angezeigt.

      Öffnen Sie in Ihrem Verzeichnis flask_blog eine Datei namens hello.py zum Bearbeiten; verwenden Sie dazu nano oder Ihren bevorzugten Texteditor:

      Die Datei hello.py wird als Minimalbeispiel für die Handhabung von HTTP-Anfragen dienen. Darin werden Sie das Flask-Objekt importieren und eine Funktion erstellen, die eine HTTP-Antwort zurückgibt. Schreiben Sie den folgenden Code in hello.py:

      flask_blog/hello.py

      from flask import Flask
      
      app = Flask(__name__)
      
      
      @app.route('/')
      def hello():
          return 'Hello, World!'
      

      Im vorherigen Codeblock importieren Sie zuerst das Flask-Objekt aus dem flask-Paket. Anschließend nutzen Sie es, um Ihre Flask-Anwendungsinstanz mit dem Namen app zu erstellen. Sie übergeben die spezielle Variable __name__, die den Namen des aktuellen Python-Moduls enthält. Damit wird der Instanz mitgeteilt, wo sie sich befindet. Das ist nötig, da Flask einige Pfade im Hintergrund einrichtet.

      Nachdem Sie die Instanz app erstellt haben, verwenden Sie sie zum Handhaben eingehender Webanfragen und zum Senden von Antworten an den Benutzer. @app.route ist ein Decorator, der eine reguläre Python-Funktion in eine Flask-Anzeigefunktion verwandelt. Diese verwandelt den Rückgabewert der Funktion in eine HTTP-Antwort, die von einem HTTP-Client (wie einem Webbrowser) angezeigt wird. Sie übergeben den Wert '/' an @app.route(), um anzugeben, dass diese Funktion auf Webanfragen bezüglich der URL / reagiert. Dabei handelt es sich um die Haupt-URL.

      Die Anzeigefunktion hello() gibt die Zeichenfolge 'Hello, World!' als Antwort zurück.

      Speichern und schließen Sie die Datei.

      Um Ihre Webanwendung auszuführen, teilen Sie Flask zuerst mit, wo sich die Anwendung (in Ihrem Fall die Datei hello.py) befindet. Nutzen Sie dazu die Umgebungsvariable FLASK_APP:

      Führen Sie sie dann im Entwicklungsmodus mit der Umgebungsvariable FLASK_ENV aus:

      • export FLASK_ENV=development

      Führen Sie die Anwendung abschließend mit dem Befehl flask run aus:

      Sobald die Anwendung ausgeführt wird, sieht die Ausgabe in etwa wie folgt aus:

      Output

      * Serving Flask app "hello" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 813-894-335

      Die vorangehende Ausgabe weist mehrere Informationen auf, zum Beispiel:

      • Der Name der Anwendung, die Sie ausführen.
      • Die Umgebung, in der die Anwendung ausgeführt wird.
      • Debug mode: on bedeutet, dass der Flask-Debugger ausgeführt wird. Das ist bei der Entwicklung nützlich, da wir detaillierte Fehlermeldungen erhalten, wenn etwas schiefgeht. Das erleichtert die Fehlerbehebung.
      • Die Anwendung wird lokal an der URL http://127.0.0.1:5000/ ausgeführt; 127.0.0.1​​ ist die IP-Adresse, die den localhost Ihres Computers darstellt, während :5000 die Portnummer ist.

      Öffnen Sie einen Browser und geben Sie die URL http://127.0.0.1:5000/ ein; Sie erhalten die Zeichenfolge Hello, World! als Antwort. Das bestätigt, dass Ihre Anwendung erfolgreich ausgeführt wird.

      Warnung Flask verwendet einen einfachen Webserver, um unsere Anwendung in einer Entwicklungsumgebung bereitzustellen. Das bedeutet auch, dass der Flask-Debugger ausgeführt wird, um die Erkennung von Fehlern zu erleichtern. Dieser Entwicklungsserver sollte nicht in Produktionsbereitstellungen verwendet werden. Weitere Informationen finden Sie auf der Seite Deploymment Options in der Flask-Dokumentation. Sie können sich auch dieses Flask-Bereitstellungs-Tutorial ansehen.

      Sie können den Entwicklungsserver nun im Terminal laufen lassen und ein anderes Terminalfenster öffnen. Gehen Sie zum Projektordner, in dem sich hello.py befindet, aktivieren Sie die virtuelle Umgebung, setzen Sie die Umgebungsvariablen FLASK_ENV und FLASK_APP und fahren Sie mit den nächsten Schritten fort. (Diese Befehle sind in diesem Schritt zuvor aufgelistet.)

      Anmerkung: Beim Öffnen eines neuen Terminals ist es wichtig, die virtuelle Umgebung zu aktivieren und die Umgebungsvariablen FLASK_ENV und FLASK_APP festzulegen.

      Wenn bereits ein Entwicklungsserver einer Flask-Anwendung ausgeführt wird, ist es nicht möglich, eine weitere Flask-Anwendung mit dem gleichen Befehl flask run auszuführen. Das liegt daran, dass flask run die Portnummer 5000 standardmäßig verwendet. Sobald sie vergeben ist, ist sie nicht mehr zur Ausführung einer weiteren Anwendung verfügbar. Sie erhalten einen Fehler, der dem folgenden ähnelt:

      Output

      OSError: [Errno 98] Address already in use

      Um dieses Problem zu lösen, stoppen Sie entweder den gerade ausgeführten Server mit STRG+C und führen flask run erneut aus. Wenn Sie beide Server gleichzeitig ausführen möchten, können Sie eine andere Portnummer an das Argument -p übergeben. Um zum Beispiel eine weitere Anwendung an Port 5001 auszuführen, verwenden Sie den folgenden Befehl:

      Sie verfügen nun über eine kleine Flask-Webabwendung. Sie haben Ihre Anwendung ausgeführt und Informationen im Webbrowser angezeigt. Als Nächstes nutzen Sie in Ihrer Anwendung HTML-Dateien.

      Schritt 3 — Verwenden von HTML-Vorlagen

      Derzeit zeigt Ihre Anwendung nur eine einfache Meldung ohne HTML an. Webanwendungen nutzen HTML hauptsächlich zum Anzeigen von Informationen für den Besucher. Daher arbeiten Sie nun an der Einbindung von HTML-Dateien in Ihre App, die im Webbrowser angezeigt werden können.

      Flask bietet eine render_template()-Hilfsfunktion, die eine Verwendung der Jinja-Vorlagen-Engine ermöglicht. Dadurch wird das Verwalten von HTML wesentlich erleichtert, da Ihr HTML-Code in .html-Dateien geschrieben und in Ihrem HTML-Code Logik verwendet wird. Sie werden diese HTML-Dateien (templates) zum Erstellen aller Ihrer Anwendungsseiten nutzen. Dazu gehören zum Beispiel die Hauptseite, auf der Sie die aktuellen Blogbeiträge anzeigen, die Seite des Blogbeitrags, die Seite, auf der Benutzer einen neuen Beitrag hinzufügen können, und so weiter.

      In diesem Schritt erstellen Sie Ihre Flask-Hauptanwendung in einer neuen Datei.

      Verwenden Sie zuerst in Ihrem Verzeichnis flask_blog nano oder Ihren bevorzugten Editor, um Ihre Datei app.py zu erstellen und zu bearbeiten. Diese wird den gesamten Code enthalten, den Sie zum Erstellen der Bloganwendung verwenden.

      In dieser neuen Datei importieren Sie das Flask-Objekt, um eine Flask-Anwendungsinstanz zu erstellen, wie Sie dies zuvor getan haben. Außerdem importieren Sie die render_template()-Hilfsfunktion, mit der Sie im Ordner templates, den Sie gleich erstellen werden, vorhandene HTML-Vorlagendateien rendern können. Die Datei wird eine Einzelansichtfunktion aufweisen, die für die Bearbeitung von Anfragen an die Hauptroute / zuständig ist​​. Fügen Sie den folgenden Inhalt hinzu:

      flask_blog/app.py

      from flask import Flask, render_template
      
      app = Flask(__name__)
      
      @app.route('/')
      def index():
          return render_template('index.html')
      

      Die Ansichtsfunktion index() gibt das Ergebnis des Aufrufs von render_template() mit index.html als Argument zurück. Das teilt render_template() mit, nach einer Datei namens index.html im Ordner templates zu suchen. Sowohl der Ordner als auch die Datei existieren noch nicht. Sie erhalten einen Fehler, wenn Sie die Anwendung an dieser Stelle ausführen würden. Sie werden sie trotzdem ausführen, damit Sie sich mit dieser häufig auftretenden Ausnahme vertraut machen können. Dann beheben Sie die Ausnahme durch Erstellung des erforderlichen Ordners und der Datei.

      Speichern und schließen Sie die Datei.

      Stoppen Sie den Entwicklungsserver im anderen Terminal, in dem die Anwendung hello ausgeführt wird, mit STRG+C.

      Bevor Sie die Anwendung ausführen, stellen Sie sicher, dass Sie den Wert für die Umgebungsvariable FLASK_APP richtig angeben, da Sie nicht mehr die Anwendung hello verwenden:

      • export FLASK_APP=app
      • flask run

      Durch Öffnen der URL http://127.0.0.1:5000/ in Ihrem Browser wird die Debugger-Seite angezeigt, die Ihnen mitteilt, dass die Vorlage index.html nicht gefunden wurde. Die Hauptzeile im Code, die für diesen Fehler verantwortlich war, wird hervorgehoben. In diesem Fall ist es die Zeile return render_template('index.html')​.

      Wenn Sie auf diese Zeile klicken, zeigt der Debugger weiteren Code an, damit Sie über mehr Kontext zum Lösen des Problems verfügen.

      Der Flask-Debugger

      Um diesen Fehler zu beheben, erstellen Sie ein Verzeichnis namens templates in Ihrem Verzeichnis flask_blog. Öffnen Sie dann darin eine Datei namens index.html zum Bearbeiten:

      • mkdir templates
      • nano templates/index.html

      Fügen Sie anschließend in index.html den folgenden HTML-Code hinzu:

      flask_blog/templates/index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>FlaskBlog</title>
      </head>
      <body>
         <h1>Welcome to FlaskBlog</h1>
      </body>
      </html>
      

      Speichern Sie die Datei und navigieren Sie in Ihrem Browser erneut zu http://127.0.0.1:5000/ oder aktualisieren Sie die Seite. Dieses Mal sollte der Browser den Text Welcome to FlaskBlog in einem <h1>-Tag anzeigen.

      Zusätzlich zum Ordner templates weisen Flask-Webanwendungen typischerweise einen Ordner static zum Hosting von statischen Dateien auf, wie z. B. CSS-Dateien, JavaScript-Dateien und Bilder, die die Anwendung verwendet.

      Sie können eine Stylesheetdatei style.css erstellen, um CSS Ihrer Anwendung hinzuzufügen. Erstellen Sie zuerst ein Verzeichnis namens static im Hauptverzeichnis flask_blog:

      Erstellen Sie dann ein anderes Verzeichnis namens css im Verzeichnis static, um .css-Dateien zu hosten. Das dient normalerweise der Organisation von statischen Dateien in dedizierten Ordnern. So befinden sich JavaScript-Dateien meist in einem Verzeichnis namens js, Bilder in einem Verzeichnis namens images (oder img) und so weiter. Der folgende Befehl erstellt das Verzeichnis css im Verzeichnis static:

      Öffnen Sie dann eine style.css-Datei im css-Verzeichnis zur Bearbeitung:

      • nano static/css/style.css

      Fügen Sie Ihrer Datei style.css die folgende CSS-Regel hinzu:

      flask_blog/static/css/style.css

      h1 {
          border: 2px #eee solid;
          color: brown;
          text-align: center;
          padding: 10px;
      }
      

      Der CSS-Code fügt einen Rahmen hinzu, ändert die Farbe in braun, zentriert den Text und fügt <h1>-Tags ein wenig Abstand hinzu.

      Speichern und schließen Sie die Datei.

      Öffnen Sie als Nächstes die Vorlagendatei index.html zur Bearbeitung:

      • nano templates/index.html

      Sie fügen der Datei style.css im Abschnitt <head> der Vorlagendatei index.html einen Link hinzu:

      flask_blog/templates/index.html

      . . .
      <head>
          <meta charset="UTF-8">
          <link rel="stylesheet" href="https://www.digitalocean.com/{{ url_for("static', filename="css/style.css") }}">
          <title>FlaskBlog</title>
      </head>
      . . .
      

      Hier verwenden Sie die Hilfsfunktion url_for(), um den entsprechenden Ort der Datei zu generieren. Das erste Argument gibt an, dass Sie mit einer statischen Datei verknüpfen, und das zweite Argument ist der Pfad der Datei im static-Verzeichnis.

      Speichern und schließen Sie die Datei.

      Wenn Sie die Indexseite Ihrer Anwendung aktualisieren, werden Sie bemerken, dass der Text Welcome to FlaskBlog jetzt braun, zentriert und in einen Rahmen eingeschlossen ist.

      Sie können die CSS-Sprache verwenden, um den Stil der Anwendung zu verändern und nach Ihrem eigenen Geschmack attraktiver zu gestalten. Wenn Sie jedoch kein Webdesigner sind oder nicht mit CSS vertraut sind, können Sie das Bootstrap-Toolkit verwenden, das anwenderfreundliche Komponenten für die Anpassung des Stils Ihrer Anwendung bereitstellt. In diesem Projekt verwenden wir Bootstrap.

      Vielleicht haben Sie sich schon gedacht, dass die Einrichtung einer weiteren HTML-Vorlage eine Wiederholung des größten Teils des HTML-Codes bedeutet, den Sie in der Vorlage index.html bereits geschrieben haben. Sie können mithilfe einer Basisvorlagen-Datei, aus der alle Ihre HTML-Dateien erben werden, unnötige Codewiederholungen vermeiden. Weitere Informationen finden Sie in Vorlagenvererbung in Jinja.

      Um eine Basisvorlage einzurichten, erstellen Sie zuerst eine Datei namens base.html in Ihrem Verzeichnis templates:

      Geben Sie in Ihrer Vorlage base.html den folgenden Code ein:

      flask_blog/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')}}">FlaskBlog</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>
      

      Speichern und schließen Sie die Datei, sobald Sie die Bearbeitung abgeschlossen haben.

      Der größte Teil des Codes im vorherigen Block ist Standard-HTML und Code, der für Bootstrap benötigt wird. Die <meta>-Tags bieten Informationen für den Webbrowser, das <link>-Tag verknüpft die Bootstrap CSS-Dateien und die <script>-Tags sind Links zu JavaScript-Code, der einige zusätzliche Bootstrap-Funktionen bietet. Konsultieren Sie die Bootstrap-Dokumentation, um mehr zu erfahren.

      Die folgenden hervorgehobenen Teile sind jedoch für die Jinja-Vorlagen-Engine spezifisch:

      • {% block title %} {% endblock %}: Ein Block, der als Platzhalter für einen Titel dient; Sie werden ihn später in anderen Vorlagen nutzen, um einen benutzerdefinierten Titel für jede Seite in Ihrer Anwendung zu vergeben, ohne den gesamten Abschnitt <head> neu schreiben zu müssen.
      • {{ url_for('index')}}: Ein Funktionsaufruf, der die URL für die Ansichtsfunktion index() zurückgibt. Das unterscheidet sich von dem vorherigen Aufruf url_for(), den Sie verwendet haben, um eine statische CSS-Datei zu verknüpfen. Er weist nur ein Argument auf, bei dem es sich um den Namen der Ansichtsfunktion handelt, und verknüpft mit der Route, die mit der Funktion verbunden ist (anstelle einer statischen Datei).
      • {% block content %} {% endblock %}: Ein weiterer Block, der durch Inhalt ersetzt wird, je nach der untergeordneten Vorlage (Vorlagen, die von base.html erben), die ihn überschreiben wird.

      Nachdem Sie nun über eine Basisvorlage verfügen, können Sie sie mithilfe von Vererbung nutzen. Öffnen Sie die Datei index.html:

      • nano templates/index.html

      Ersetzen Sie die Inhalte mit Folgendem:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
      {% endblock %}
      

      In dieser neuen Version der Vorlage index.html verwenden Sie das {% extends %}-Tag, um von der Vorlage base.html zu erben. Dann erweitern Sie sie, indem Sie den content-Block in der Basisvorlage durch das ersetzen, was innerhalb des content-Blocks im vorherigen Codeblock enthalten ist.

      Dieser content-Block enthält ein <h1>-Tag mit dem Text Welcome to FlaskBlog in einem title-Block, der wiederum den ursprünglichen title-Block in der Vorlage base.html durch den Text Welcome to FlaskBlog ersetzt. So müssen Sie den gleichen Text nicht zweimal wiederholen, da er sowohl als Titel für die Seite als auch Überschrift dienen kann, die unterhalb der Navigationsleiste erscheint, geerbt von der Basisvorlage.

      Außerdem bietet Ihnen Vererbung mit Vorlagen die Möglichkeit, den HTML-Code, über den Sie in anderen Vorlagen verfügen (in diesem Fall base.html), wiederzuverwenden, ohne ihn jedes Mal wiederholen zu müssen, wenn er benötigt wird.

      Speichern und schließen Sie die Datei und aktualisieren Sie die Indexseite in Ihrem Browser. Sie werden Ihre Seite mit einer Navigationsleiste und einem formatierten Titel sehen.

      Indexseite mit Bootstrap

      Sie haben in Flask HTML-Vorlagen und statische Dateien verwendet. Außerdem haben Sie Bootstrap genutzt, um die Optik Ihrer Seite und eine Basisvorlage anzupassen, um Codewiederholungen zu vermeiden. Im nächsten Schritt richten Sie eine Datenbank ein, die Ihre Anwendungsdaten speichern wird.

      Schritt 4 — Einrichten der Datenbank

      In diesem Schritt richten Sie eine Datenbank zur Speicherung von Daten ein, d. h. die Blogbeiträge für Ihre Anwendung. Außerdem befüllen Sie die Datenbank mit einigen Beispieleinträgen.

      Sie werden eine SQLite-Datenbankdatei verwenden, um Ihre Daten zu speichern, da das Modul sqlite3, das wir zur Interaktion mit der Datenbank verwenden, in der Python-Standardbibliothek einsatzbereit ist. Weitere Informationen zu SQLite finden Sie in diesem Tutorial.

      Da Daten in SQLite in Tabellen und Spalten gespeichert werden und Ihre Daten hauptsächlich aus Blogbeiträgen bestehen, müssen Sie zunächst eine Tabelle namens posts mit den erforderlichen Spalten erstellen. Sie werden eine .sql-Datei erstellen, die SQL-Befehle enthält, um die Tabelle posts mit einigen Spalten zu erstellen. Dann verwenden Sie diese Datei zur Erstellung der Datenbank.

      Öffnen Sie eine Datei namens schema.sql in Ihrem Verzeichnis flask_blog:

      Geben Sie in dieser Datei die folgenden SQL-Befehle ein:

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

      Speichern und schließen Sie die Datei.

      Der erste SQL-Befehl ist DROP TABLE IF EXISTS posts; – damit werden alle bereits vorhandenen Tabellen namens posts gelöscht, damit Sie nicht durcheinander kommen. Beachten Sie, dass bei Nutzung dieser SQL-Befehle stets alle in der Datenbank vorhandenen Inhalte gelöscht werden. Schreiben Sie also keine wichtigen Inhalte in die Webanwendung, bis Sie dieses Tutorial abgeschlossen und mit dem Endergebnis experimentiert haben. Als Nächstes verwenden Sie CREATE TABLE posts zur Erstellung der Tabelle posts mit den folgenden Spalten:

      • id: Eine ganze Zahl, die einen Primärschlüssel darstellt; diesem wird von der Datenbank für jeden Eintrag (also jeden Blogbeitrag) ein eindeutiger Wert zugewiesen.
      • created: Die Zeit, zu der der Blogbeitrag erstellt wurde. NOT NULL bedeutet, dass diese Spalte nicht leer sein darf und der DEFAULT-Wert der CURRENT_TIMESTAMP-Wert ist. Das ist der Zeitpunkt, zu dem der Blogbeitrag der Datenbank hinzugefügt wurde. Genau wie bei id müssen Sie keinen Wert für diese Spalte angeben, da er automatisch ausgefüllt wird.
      • title: Der Titel des Beitrags.
      • content: Der Inhalt des Beitrags.

      Nachdem Sie in der Datei schema.sql über ein SQL-Schema verfügen, verwenden Sie es nun zur Erstellung der Datenbank mit einer Python-Datei, die eine SQLite .db-Datenbank generieren wird. Öffnen Sie mit Ihrem bevorzugten Editor eine Datei namens init_db.py im Verzeichnis flask_blog:

      Und fügen Sie dann den folgenden Code hinzu.

      flask_blog/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()
      

      Zuerst importieren Sie das Modul sqlite3 und öffnen dann eine Verbindung zu einer Datenbankdatei namens database.db, die erstellt wird, sobald Sie die Python-Datei ausführen. Dann verwenden Sie die Funktion open(), um die Datei schema.sql zu öffnen. Als Nächstes führen Sie ihre Inhalte mit der Methode executescript() aus, die mehrere SQL-Anweisungen auf einmal ausführt. Dadurch wird die Tabelle posts erstellt. Sie erstellen ein Cursor-Objekt, mit dem Sie die Methode execute() verwenden können, um zwei INSERT SQL-Anweisungen auszuführen und Ihrer Tabelle posts zwei Blogbeiträge hinzuzufügen. Schließlich committen Sie die Änderungen und schließen die Verbindung.

      Speichern und schließen Sie die Datei und führen Sie sie dann im Terminal mit dem python-Befehl aus:

      Sobald die Ausführung der Datei beendet ist, wird in Ihrem Verzeichnis flask_blog eine neue Datei namens database.db angezeigt. Das bedeutet, dass Sie Ihre Datenbank erfolgreich eingerichtet haben.

      Im nächsten Schritt rufen Sie die Beiträge ab, die Sie in Ihre Datenbank eingefügt haben, und zeigen sie auf der Homepage Ihrer Anwendung an.

      Schritt 5 — Anzeigen aller Beiträge

      Nachdem Sie Ihre Datenbank eingerichtet haben, können Sie die Ansichtsfunktion index() nun so ändern, dass sie alle Beiträge anzeigt, die Sie in Ihrer Datenbank haben.

      Öffnen Sie die Datei app.py, um die folgenden Änderungen vorzunehmen:

      Als erste Änderung importieren Sie das Modul sqlite3 an den Anfang der Datei:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template
      
      . . .
      

      Als Nächstes erstellen Sie eine Funktion, die eine Datenbankverbindung erstellt und zurückgibt. Fügen Sie sie direkt nach den Importen hinzu:

      flask_blog/app.py

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

      Diese get_db_connection()-Funktion öffnet eine Verbindung zur Datenbankdatei database.db und legt dann das Attribut row_factory auf sqlite3. Row fest, damit Sie namenbasierten Zugriff auf Spalten erhalten. Das bedeutet, dass die Datenbankverbindung Zeilen zurückgibt, die sich wie reguelmäßige Python-Wörterbücher verhalten. Schließlich gibt die Funktion das Verbindungsobjekt conn zurück, das Sie zum Zugriff auf die Datenbank verwenden werden.

      Nach der Definition der get_db_connection()-Funktion ändern Sie die Funktion index(), damit sie wie folgt aussieht:

      flask_blog/app.py

      . . .
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          posts = conn.execute('SELECT * FROM posts').fetchall()
          conn.close()
          return render_template('index.html"https://www.digitalocean.com/, posts=posts)
      

      In dieser neuen Version der Funktion index() öffnen Sie zuerst mit der Funktion get_db_connection(), die Sie zuvor definiert haben, eine Datenbankverbindung. Dann führen Sie eine SQL-Abfrage aus, um alle Einträge aus der Tabelle posts auszuwählen. Sie implementieren die Methode fetchall(), um alle Zeilen des Abfrageergebnisses abzurufen. Dadurch wird eine Liste der Beiträge, die Sie der Datenbank im vorherigen Schritt hinzugefügt haben, zurückgegeben.

      Sie schließen die Datenbankverbindung mit dem Befehl close() und geben das Ergebnis vom Rendern der Vorlage index.html zurück. Außerdem übergeben Sie das Objekt posts als Argument, das die Ergebnisse enthält, die Sie aus der Datenbank erhalten haben. Das ermöglicht Ihnen, auf die Blogbeiträge in der Vorlage index.html zuzugreifen.

      Speichern und schließen Sie die Datei app.py nach Vornahme der Änderungen.

      Nachdem Sie die Beiträge, die Sie von der Datenbank abgerufen haben, an die Vorlage index.html übergeben haben, können Sie eine for-Schleife verwenden, um einzelne Beiträge auf Ihrer Indexseite anzuzeigen.

      Öffnen Sie die Datei index.html:

      • nano templates/index.html

      Ändern Sie sie dann so, dass sie wie folgt aussieht:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
          {% for post in posts %}
              <a href="https://www.digitalocean.com/#">
                  <h2>{{ post['title'] }}</h2>
              </a>
              <span class="badge badge-primary">{{ post['created'] }}</span>
              <hr>
          {% endfor %}
      {% endblock %}
      

      Hier ist die Syntax {% for post in posts %} eine Jinja-for-Schleife, die einer Python-for-Schleife ähnelt; sie muss allerdings später mit der {% endfor %}-Syntax geschlossen werden. Sie verwenden diese Syntax, um für jedes Element in der Liste posts, die von der Funktion index() in der Zeile return render_template('index.html', posts=posts) übergeben wurde, eine Schleife zu durchlaufen. Innerhalb dieser for-Schleife zeigen Sie den Titel des Beitrags in einer <h2>-Überschrift in einem <a>-Tag an (Sie verwenden dieses Tag später, um Verknüpfungen für die einzelnen Beiträge zu erstellen).

      Sie zeigen den Titel an mit einem Literal-Variablen-Trennzeichen ({{ ... }}). Denken Sie daran, dass post ein wörterbuchähnliches Objekt sein wird, damit Sie mit post['title'] auf den Beitragstitel zugreifen können. Außerdem zeigen Sie mit der gleichen Methode das Erstellungsdatum des Beitrags an.

      Sobald Sie die Bearbeitung der Datei abgeschlossen haben, speichern und schließen Sie sie. Navigieren Sie dann in Ihrem Browser zur Indexseite. Sie werden die beiden Beiträge, die Sie der Datenbank hinzugefügt haben, auf Ihrer Seite sehen.

      Indexseite mit den angezeigten Beiträgen

      Nachdem Sie die Ansichtsfunktion index() modifiziert haben, um alle Beiträge, die Sie in der Datenbank haben, auf der Homepage Ihrer Anwendung anzeigen, zeigen Sie nun jeden Beitrag auf einer einzelnen Seite an und ermöglichen es Benutzern, Links zu den einzelnen Beiträgen zu nutzen.

      Schritt 6 — Anzeigen eines einzelnen Beitrags

      In diesem Schritt erstellen Sie eine neue Flask-Route mit einer Ansichtsfunktion und eine neue HTML-Vorlage zur Anzeige eines einzelnen Blogeintrags anhand seiner ID.

      Am Ende dieses Schritts wird die URL http://127.0.0.1:5000/1 eine Seite sein, die den ersten Beitrag anzeigt (weil dieser die ID 1 hat). Die URL http://127.0.0.1:5000/ID zeigt den Beitrag mit der zugehörigen ID-Nummer an, so sie vorhanden ist.

      Öffnen Sie app.py zum Bearbeiten:

      Da Sie in diesem Projekt noch an unterschiedlichen Stellen einen Blogeintrag aus der Datenbank abrufen müssen, erstellen Sie eine Standalone-Funktion namens get_post(). Sie können sie aufrufen, indem Sie ihr eine ID übergeben, und erhalten den Blogbeitrag zurück, der mit der bereitgestellten ID verknüpft ist; oder Sie sorgen dafür, dass Flask mit einer 404 Nicht gefunden-Nachricht antwortet, wenn der Blogbeitrag nicht existiert.

      Um mit einer 404-Seite zu antworten, müssen Sie die Funktion abort() aus der Werkzeug-Bibliothek, die zusammen mit Flask installiert wurde, am Anfang der Datei importieren:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template
      from werkzeug.exceptions import abort
      
      . . .
      

      Fügen Sie die Funktion get_post() direkt nach der Funktion get_db_connection() hinzu, die Sie im vorherigen Schritt erstellt haben:

      flask_blog/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
      
      . . .
      

      Diese neue Funktion verfügt über ein post_id-Argument, das bestimmt, welcher Blogbeitrag zurückgegeben wird.

      Innerhalb der Funktion verwenden Sie die Funktion get_db_connection() zum Öffnen einer Datenbankverbindung und Ausführen einer SQL-Abfrage, um den Blogbeitrag zu erhalten, der mit dem angegebenen post_id-Wert verknüpft ist. Sie fügen die Methode fetchone() hinzu, um das Ergebnis zu erhalten und in der Variable post zu speichern. Dann schließen Sie die Verbindung. Wenn die Variable post den Wert None (Keine) hat, was bedeutet, dass in der Datenbank kein Ergebnis gefunden wird, verwenden Sie die Funktion abort(), die Sie zuvor importiert haben, um mit einem 404-Fehlercode zu reagieren; die Ausführung der Funktion wird beendet. Wenn jedoch ein Beitrag gefunden wurde, geben Sie den Wert der Variable post zurück.

      Fügen Sie als Nächstes die folgende Ansichtsfunktion am Ende der Datei app.py hinzu:

      flask_blog/app.py

      . . .
      
      @app.route('/<int:post_id>')
      def post(post_id):
          post = get_post(post_id)
          return render_template('post.html', post=post)
      

      In dieser neuen Ansichtsfunktion fügen Sie eine Variablenregel <int:post_id> hinzu, um anzugeben, dass der Teil nach dem Schrägstrich (/) eine positive ganze Zahl ist (markiert mit dem int-Konverter), die Sie in Ihrer Ansichtsfunktion aufrufen müssen. Flask erkennt das und übergibt ihren Wert an das Schlüsselwortargument post_id Ihrer post()-Ansichtsfunktion. Dann verwenden Sie die Funktion get_post(), um den Blogbeitrag abzurufen, der mit der angegebenen ID verknüpft ist, und speichern das Ergebnis in der Variable post, die Sie an eine post.html-Vorlage, die Sie bald erstellen werden, übergeben.

      Speichern Sie die Datei app.py und öffnen Sie eine neue post.html-Vorlagendatei zur Bearbeitung:

      Geben Sie in dieser neuen post.html-Datei den folgenden Code ein. Das wird ähnlich aussehen wie bei der Datei index.html; es wird jedoch nur ein einziger Blogbeitrag angezeigt, und zudem mit dem Inhalt des Beitrags:

      flask_blog/templates/post.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
          <span class="badge badge-primary">{{ post['created'] }}</span>
          <p>{{ post['content'] }}</p>
      {% endblock %}
      

      Sie fügen den title-Block hinzu, den Sie in der Vorlage base.html definiert haben, um den Titel der Seite an den Titel des Beitrags anzupassen, der gleichzeitig in einer <h2>-Überschrift angezeigt wird.

      Speichern und schließen Sie die Datei.

      Sie können nun zu den folgenden URLs navigieren, um die beiden Beiträge, die Sie in Ihrer Datenbank haben, sowie eine Seite anzuzeigen, die dem Benutzer mitteilt, dass der angeforderte Blogbeitrag nicht gefunden wurde (da bisher kein Beitrag mit der ID-Nummer 3 vorhanden ist):

      http://127.0.0.1:5000/1
      http://127.0.0.1:5000/2
      http://127.0.0.1:5000/3
      

      Zurück auf der Indexseite verknüpfen Sie jeden Titel eines Beitrags mit seiner jeweiligen Seite. Dazu verwenden Sie die Funktion url_for(). Öffnen Sie zuerst die Vorlage index.html zur Bearbeitung:

      • nano templates/index.html

      Ändern Sie dann den Wert des Attributs href von # in {{ url_for('post', post_id=post['id']) }}, damit die for-Schleife genau wie folgt aussieht:

      flask_blog/templates/index.html

      {% for post in posts %}
          <a href="https://www.digitalocean.com/{{ url_for('post', post_id=post['id']) }}">
              <h2>{{ post['title'] }}</h2>
          </a>
          <span class="badge badge-primary">{{ post['created'] }}</span>
          <hr>
      {% endfor %}
      

      Hier übergeben Sie 'post' an die Funktion url_for() als erstes Argument. Das ist der Name der post()-Ansichtsfunktion; da sie ein post_id-Argument akzeptiert, geben Sie den Wert post['id']. Die Funktion url_for() gibt für jeden Beitrag auf Grundlage seiner ID die richtige URL zurück.

      Speichern und schließen Sie die Datei.

      Die Links auf der Indexseite funktionieren nun wie erwartet. Damit haben Sie die Erstellung des Teils der Anwendung abgeschlossen, der für die Anzeige der Blogbeiträge in Ihrer Datenbank verantwortlich ist. Als Nächstes fügen Sie die Möglichkeit zur Erstellung, Bearbeitung und Löschung von Blogbeiträgen zu Ihrer Anwendung hinzu.

      Schritt 7 — Bearbeiten von Beiträgen

      Nachdem Sie die Anzeige der Blogbeiträge, die in der Datenbank vorhanden sind, in der Webanwendung abgeschlossen haben, müssen Sie es den Benutzern Ihrer Anwendung nun ermöglichen, neue Blogbeiträge zu schreiben und der Datenbank hinzuzufügen, vorhandene Beiträge zu bearbeiten und unnötige Blogbeiträge zu löschen.

      Erstellen eines neuen Beitrags

      Bislang verfügen Sie über eine Anwendung, die die Beiträge in Ihrer Datenbank anzeigt, aber keine Möglichkeit zum Hinzufügen eines neuen Beitrags, es sei denn, Sie verbinden sich direkt mit der SQLite-Datenbank und fügen manuell einen Beitrag hinzu. In diesem Abschnitt richten Sie eine Seite ein, auf der Sie einen Beitrag erstellen können, indem Sie den Titel und den Inhalt bereitstellen.

      Öffnen Sie die Datei app.py zur Bearbeitung:

      Zuerst importieren Sie Folgendes aus dem Flask-Framework:

      • Das globale request-Objekt für den Zugriff auf eingehende Anfragedaten, die über ein HTML-Formular übermittelt werden.
      • Die Funktion url_for() zur Erstellung von URLs.
      • Die flash()-Funktion zur Anzeige einer Meldung, wenn eine Anfrage verarbeitet wird.
      • Die Funktion redirect() zum Umleiten des Clients an einen anderen Ort.

      Fügen Sie die Importe wie folgt in Ihre Datei ein:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template, request, url_for, flash, redirect
      from werkzeug.exceptions import abort
      
      . . .
      

      Die flash()-Funktion speichert geflashte Nachrichten in der Browsersitzung des Clients, was die Einrichtung eines geheimen Schlüssels erfordert. Dieser geheime Schlüssel dient zur Sicherung von Sitzungen, sodass sich Flask Informationen von einer Anfrage zu einer anderen merken kann, zum Beispiel beim Navigieren von der Seite für neue Beiträge zur Indexseite. Der Benutzer kann auf die in der Sitzung gespeicherten Daten zugreifen, sie aber nicht ändern, es sei denn, er verfügt über den geheimen Schlüssel; das bedeutet, dass Sie niemandem Zugriff auf Ihren geheimen Schlüssel gewähren dürfen. Weitere Informationen finden Sie in der Flask-Dokumentation für Sitzungen.

      Um einen geheimen Schlüssel einzurichten, fügen Sie Ihrer Anwendung eine SECRET_KEY-Konfiguration über das Objekt app.config hinzu. Fügen Sie sie unmittelbar nach der app-Definition hinzu, bevor Sie die Ansichtsfunktion index() definieren:

      flask_blog/app.py

      . . .
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'your secret key'
      
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          posts = conn.execute('SELECT * FROM posts').fetchall()
          conn.close()
          return render_template('index.html', posts=posts)
      
      . . .
      

      Denken Sie daran, dass der geheime Schlüssel eine lange Zufallszeichenfolge sein sollte.

      Nach der Einrichtung eines geheimen Schlüssels erstellen Sie eine Ansichtsfunktion, die eine Vorlage rendern wird, um ein Formular anzuzeigen, das Sie zum Erstellen eines neuen Blogbeitrags ausfüllen können. Fügen Sie diese neue Funktion am Ende der Datei hinzu:

      flask_blog/app.py

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

      Dadurch wird eine /create-Route erstellt, die sowohl GET- als auch POST-Anfragen akzeptiert. GET-Anfragen werden standardmäßig akzeptiert. Um auch POST-Anfragen zu akzeptieren, die vom Browser beim Übermitteln von Formularen gesendet werden, übergeben Sie ein Tupel mit den akzeptierten Arten von Anfragen an das methods-Argument des @app.route()-Decorators.

      Speichern und schließen Sie die Datei.

      Um die Vorlage zu erstellen, öffnen Sie eine Datei namens create.html in Ihrem Ordner templates:

      • nano templates/create.html

      Fügen Sie in dieser neuen Datei den folgenden Code hinzu:

      flask_blog/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
      <h1>{% block title %} Create a New Post {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="title">Title</label>
              <input type="text" name="title"
                     placeholder="Post title" class="form-control"
                     value="{{ request.form['title'] }}"></input>
          </div>
      
          <div class="form-group">
              <label for="content">Content</label>
              <textarea name="content" placeholder="Post content"
                        class="form-control">{{ request.form['content'] }}</textarea>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      {% endblock %}
      

      Der größte Teil dieses Codes ist Standard-HTML. Er zeigt ein Eingabefeld für den Titel des Beitrags, einen Textbereich für den Inhalt des Beitrags und eine Schaltfläche zum Übermitteln des Formulars an.

      Der Wert der Beitragstiteleingabe ist {{ request.form['title'] }} und der Textbereich hat den Wert {{ request.form['content'] }}; das sorgt dafür, dass die eingegebenen Daten nicht verloren gehen, wenn etwas schiefläuft. Wenn Sie zum Beispiel einen langen Beitrag verfassen und vergessen, ihm einen Titel zu geben, wird Ihnen eine Meldung angezeigt, die Sie daran erinnert, dass der Titel erforderlich ist. Das geschieht, ohne dass Sie den geschriebenen Beitrag verlieren, da er im globalen request-Objekt gespeichert wird, auf das Sie in Ihren Vorlagen Zugriff haben.

      Nachdem der Entwicklungsserver ausgeführt wird, verwenden Sie nun Ihren Browser, um zur Route /create zu navigieren:

      http://127.0.0.1:5000/create
      

      Sie werden eine Seite für Create a New Post mit einem Feld für Titel und Inhalt sehen.

      Seite zum Erstellen eines neuen Beitrags

      Dieses Formular übermittelt eine POST-Anfrage an Ihre create()-Funktion. In der Funktion gibt es jedoch noch keinen Code, um eine POST-Anfrage zu bearbeiten; also wird nach dem Ausfüllen und Übermitteln des Formulars nichts geschehen.

      Sie werden die eingehende POST-Anfrage bearbeiten, wenn ein Formular übermittelt wird. Das tun Sie in der create()-Ansichtsfunktion. Sie können die POST-Anfrage separat bearbeiten, indem Sie den Wert von request.method überprüfen. Wenn der Wert auf 'POST' festgelegt ist, bedeutet das, dass die Anfrage eine POST-Anfrage ist. Dann fahren Sie mit dem Extrahieren der übermittelten Daten sowie dem Validieren und Einfügen der Daten in Ihre Datenbank fort.

      Öffnen Sie die Datei app.py zur Bearbeitung:

      Ändern Sie die create()-Ansichtsfunktion, damit sie genau wie folgt aussieht:

      flask_blog/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!')
              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')
      

      Stellen Sie in der if-Anweisung sicher, dass der darauf folgende Code nur ausgeführt wird, wenn die Anfrage eine POST-Anfrage ist. Nutzen Sie dazu den Vergleich request.method == 'POST'.

      Dann extrahieren Sie den übermittelten Titel und Inhalt aus dem request.from-Objekt, das Ihnen Zugriff auf die Formulardaten in der Anfrage bietet. Wenn der Titel nicht angegeben ist, wäre die Bedingung if not title erfüllt. In dem Fall wird dem Benutzer eine Meldung angezeigt, die ihm mitteilt, dass ein Titel erforderlich ist. Wenn der Titel hingegen angegeben ist, öffnen Sie eine Verbindung mit der Funktion get_db_connection() und fügen den Titel und Inhalt, die Sie erhalten haben, in die Tabelle posts ein.

      Dann übergeben Sie die Änderungen mit „commit“ an die Datenbank und schließen die Verbindung. Nachdem Sie den Blogbeitrag in die Datenbank eingefügt haben, leiten Sie den Client mit der redirect()-Funktion auf die Indexseite um. Dazu übergeben Sie ihr die von der Funktion url_for() mit dem Wert 'index' als Argument generierte URL.

      Speichern und schließen Sie die Datei.

      Navigieren Sie nun mit Ihrem Webbrowser zur Route /create:

      http://127.0.0.1:5000/create
      

      Füllen Sie das Formular mit einem Titel Ihrer Wahl und einem Inhalt aus. Sobald Sie das Formular übermitteln, sehen Sie, dass der neue Beitrag auf der Indexseite aufgelistet wird.

      Schließlich zeigen Sie geflashte Nachrichten an und fügen zur Navigationsleiste in der Vorlage base.html eine Verknüpfung hinzu, um einfachen Zugriff auf diese neue Seite zu ermöglichen. Öffnen Sie die Vorlagendatei:

      Bearbeiten Sie die Datei, indem Sie ein neues <li>-Tag nach dem Link About im Tag <nav> hinzufügen. Fügen Sie dann direkt über dem content-Block eine neue for-Schleife hinzu, um die geflashten Nachrichten unterhalb der Navigationsleiste anzuzeigen. Diese Nachrichten sind in der speziellen Funktion get_flashed_messages() von Flask verfügbar:

      flask_blog/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')}}">FlaskBlog</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">
                  <a class="nav-link" href="https://www.digitalocean.com/#">About</a>
              </li>
              <li class="nav-item">
                  <a class="nav-link" href="https://www.digitalocean.com/{{url_for("create')}}">New Post</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>
      

      Speichern und schließen Sie die Datei. Die Navigationsleiste verfügt nun über ein Element New Post, das mit der Route /create verknüpft ist.

      Bearbeiten eines Beitrags

      Damit Ihr Blog aktuell bleibt, müssen Sie Ihre vorhandenen Beiträge bearbeiten können. Dieser Abschnitt führt Sie durch die Erstellung einer neuen Seite in Ihrer Anwendung, um das Bearbeiten eines Beitrags zu vereinfachen.

      Zuerst fügen Sie der Datei app.py eine neue Route hinzu. Ihre Ansichtsfunktion empfängt die ID des Beitrags, der bearbeitet werden soll; die URL wird im Format /post_id/edit vorliegen, wobei die Variable post_id die ID des Beitrags ist. Öffnen Sie die Datei app.py zur Bearbeitung:

      Fügen Sie als Nächstes die folgende Ansichtsfunktion edit() am Ende der Datei hinzu: Die Bearbeitung eines bestehenden Beitrags ähnelt der Erstellung eines neuen Beitrags. Daher wird diese Ansichtsfunktion ähnlich aussehen wie die create()-Ansichtsfunktion:

      flask_blog/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!')
              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)
      

      Der Beitrag, den Sie bearbeiten, wird von der URL bestimmt; Flask übergibt die ID-Nummer an die Funktion edit() über das Argument id. Sie fügen diesen Wert der Funktion get_post() hinzu, um den Beitrag, der mit der bereitgestellten ID verknüpft ist, aus der Datenbank abzurufen. Die neuen Daten kommen in einer POST-Anfrage, die in der Bedingung if request.method == 'POST' verwaltet wird.

      Genau wie bei der Erstellung eines neuen Beitrags extrahieren Sie die Daten zuerst aus dem request.form-Objekt und flashen dann eine Nachricht, wenn der Titel einen leeren Wert hat; andernfalls öffnen Sie eine Datenbankverbindung. Dann aktualisieren Sie die Tabelle posts durch Festlegen eines neuen Titels und eines neuen Inhalts, wobei die ID des Beitrags in der Datenbank gleich der ID ist, die in der URL enthalten war.

      Im Fall einer GET-Anfrage rendern Sie eine Vorlage edit.html, indem Sie die Variable post übergeben, die den zurückgegebenen Wert der Funktion get_post() enthält. Sie tun das, um den bestehenden Titel und Inhalt auf der Bearbeitungsseite anzuzeigen.

      Speichern und schließen Sie die Datei und erstellen Sie dann eine neue edit.html-Vorlage:

      Schreiben Sie in dieser neuen Datei den folgenden Code:

      flask_blog/templates/edit.html

      {% extends 'base.html' %}
      
      {% block content %}
      <h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="title">Title</label>
              <input type="text" name="title" placeholder="Post title"
                     class="form-control"
                     value="{{ request.form['title'] or post['title'] }}">
              </input>
          </div>
      
          <div class="form-group">
              <label for="content">Content</label>
              <textarea name="content" placeholder="Post content"
                        class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      <hr>
      {% endblock %}
      

      Speichern und schließen Sie die Datei.

      Dieser Code folgt dem gleichen Muster, außer der Syntax {{ request.form['title'] or post['title'] }} und {{ request.form['content'] or post['content'] }}. Das zeigt die in der Anfrage gespeicherten Daten an, so vorhanden. Andernfalls werden die Daten aus der Variablen post angezeigt, die mit den aktuellen Datenbankdaten an die Vorlage übergeben wurde.

      Navigieren Sie nun zur folgenden URL, um den ersten Beitrag zu bearbeiten:

      http://127.0.0.1:5000/1/edit
      

      Sie werden eine Seite Edit “First Post” sehen.

      Seite zum Bearbeiten eines Beitrags

      Bearbeiten Sie den Beitrag und übermitteln Sie das Formular; überprüfen Sie dann, ob der Beitrag aktualisiert wurde.

      Nun müssen Sie für jeden Beitrag auf der Indexseite einen Link hinzufügen, der auf die Bearbeitungsseite verweist. Öffnen Sie die Vorlagendatei index.html:

      • nano templates/index.html

      Bearbeiten Sie die Datei, damit sie genau wie folgt aussieht:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
          {% for post in posts %}
              <a href="https://www.digitalocean.com/{{ url_for("post', post_id=post['id']) }}">
                  <h2>{{ post['title'] }}</h2>
              </a>
              <span class="badge badge-primary">{{ post['created'] }}</span>
              <a href="https://www.digitalocean.com/{{ url_for("edit', id=post['id']) }}">
                  <span class="badge badge-warning">Edit</span>
              </a>
              <hr>
          {% endfor %}
      {% endblock %}
      

      Speichern und schließen Sie die Datei.

      Hier fügen Sie ein <a>-Tag hinzu, um eine Verknüpfung zur Ansichtsfunktion edit() herzustellen. Dazu übergeben Sie den Wert post['id'], um mit dem Link Edit eine Verknüpfung zur Bearbeitungsseite der einzelnen Beiträge herzustellen.

      Löschen eines Beitrags

      Manchmal müssen Beiträge nicht mehr öffentlich verfügbar sein. Dafür gibt es die Funktion zum Löschen von Beiträgen. In diesem Schritt fügen Sie Ihrer Anwendung die Löschfunktion hinzu.

      Zuerst fügen Sie eine neue Route /ID/delete hinzu, die POST-Anfragen akzeptiert, ähnlich wie bei der Ansichtsfunktion edit(). Ihre neue delete()-Ansichtsfunktion empfängt die ID des Beitrags, der gelöscht werden soll, aus der URL. Öffnen Sie die Datei app.py:

      Fügen Sie am Ende der Datei die folgende Ansichtsfunktion hinzu:

      flask_blog/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'))
      

      Diese Ansichtsfunktion akzeptiert nur POST-Anfragen. Das bedeutet, dass, wenn Sie in Ihrem Browser zur Route /ID/delete navigieren, ein Fehler zurückgegeben wird, da Webbrowser standardmäßig GET-Anfragen nutzen.

      Sie können diese Route jedoch über ein Formular aufrufen, das eine POST-Anfrage sendet, mit der die ID des Beitrags übergeben wird, den Sie löschen möchten. Die Funktion empfängt den ID-Wert und verwendet ihn, um den Beitrag mit der Funktion get_post() aus der Datenbank abzurufen.

      Dann öffnen Sie eine Datenbankverbindung und führen einen DELETE FROM-SQL-Befehl aus, um den Beitrag zu löschen. Sie übergeben die Änderung an die Datenbank und schließen die Verbindung, während Sie dem Benutzer eine Nachricht flashen, dass der Beitrag erfolgreich gelöscht wurde, und leiten ihn zur Indexseite weiter.

      Beachten Sie, dass Sie eine Vorlagendatei nicht rendern, da Sie der Bearbeitungsseite einfach eine Delete-Schaltfläche hinzufügen.

      Öffnen Sie die Vorlagendatei edit.html:

      Fügen Sie dann das folgende <form>-Tag nach dem Tag <hr> und direkt vor der Zeile {% endblock %} hinzu:

      flask_blog/templates/edit.html

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

      Sie verwenden die Methode confirm(), um eine Bestätigungsmeldung anzuzeigen, bevor die Anfrage übermittelt wird.

      Navigieren Sie nun erneut zur Bearbeitungsseite eines Blogbeitrags und versuchen Sie, ihn zu löschen:

      http://127.0.0.1:5000/1/edit
      

      Am Ende dieses Schritts sieht der Quellcode Ihres Projekts wie der Code auf dieser Seite aus.

      Damit können die Benutzer Ihrer Anwendung nun neue Blogbeiträge schreiben und der Datenbank hinzufügen sowie bestehende Beiträge bearbeiten und löschen.

      Zusammenfassung

      Dieses Tutorial hat eine Einleitung in grundlegende Konzepte des Flask-Python-Frameworks geboten. Sie haben gelernt, wie Sie eine kleine Webanwendung erstellen und auf einem Entwicklungsserver ausführen sowie dem Benutzer erlauben, über URL-Parameter und Webformulare benutzerdefinierte Daten anzugeben. Außerdem haben Sie die Jinja-Vorlagen-Engine verwendet, um HTML-Dateien wiederzuverwenden und Logik darin zu nutzen. Am Ende dieses Tutorials verfügen Sie nun über einen voll funktierende Weblog, der mit einer SQLite-Datenbank interagiert, um mithilfe der Python-Sprache und SQL-Abfragen das Erstellen, Anzeigen, Bearbeiten und Löschen von Blogbeiträgen zu ermöglichen.

      Sie können diese Anwendung weiter entwickeln, indem Sie Benutzerauthentifizierung hinzufügen, damit nur registrierte Benutzer Blogbeiträge erstellen und ändern können. Außerdem können Sie zu jedem Blogbeitrag Kommentare und Tags sowie Datei-Upload-Funktionen hinzufügen, damit Benutzer die Möglichkeit haben, Bilder im Beitrag zu verwenden. Weitere Informationen finden Sie in der Flask-Dokumentation.

      Flask verfügt über eine Vielzahl von Flask-Erweiterungen, die von der Community entwickelt wurden. Im Folgenden finden Sie eine Liste von Erweiterungen, die Sie verwenden können, um Ihren Entwicklungsprozess zu erleichtern:

      • Flask-Login: verwaltet die Benutzersitzung und übernimmt die An- und Abmeldung sowie das Erinnern angemeldeter Benutzer.
      • Flask-SQLAlchemy: vereinfacht die Verwendung von Flask mit SQLAlchemy, einem Python-SQL-Toolkit, und Object Relational Mapper zur Interaktion mit SQL-Datenbanken.
      • Flask-Mail: hilft beim Versand von E-Mail-Nachrichten in Ihrer Flask-Anwendung.



      Source link

      Cómo presentar aplicaciones de Flask con Gunicorn y Nginx en Ubuntu 20.04


      Introducción

      A través de esta guía, creará una aplicación de Python utilizando el microframework de Flask en Ubuntu 20.04. En la mayor parte de este artículo se abordarán la configuración del servidor de la aplicación Gunicorn y la forma de iniciar la aplicación y configurar Nginx para que funcione como un proxy inverso de cliente.

      Requisitos previos

      Antes de comenzar con esta guía, deberá contar con lo siguiente:

      • Un servidor con Ubuntu 20.04 instalado y un usuario no root con privilegios sudo. Siga nuestra guía de configuración inicial para servidores a modo de orientación.
      • Nginx instalado conforme a los pasos 1 y 2 de Cómo instalar Nginx en Ubuntu 20.04.
      • Un nombre de dominio configurado para que apunte a su servidor. Puede adquirir uno en Namecheap u obtener uno de forma gratuita en Freenom. Puede aprender a apuntar dominios a DigitalOcean siguiendo la documentación sobre dominios y DNS pertinente. Asegúrese de crear los siguientes registros DNS:

        • Un registro A con your_domain orientado a la dirección IP pública de su servidor.
        • Un registro A con www.your_domain orientado a la dirección IP pública de su servidor.
      • Conocimientos sobre la especificación WSGI, que el servidor de Gunicorn usará para comunicarse con su aplicación Flask. En esta discusión se abarca WSGI de forma más deallada.

      Paso 1: Instalar los componentes desde los repositorios de Ubuntu

      Nuestro primer paso será instalar todo lo que necesitamos desde los repositorios de Ubuntu. Esto incluye pip, el administrador de paquetes de Python, que gestionará nuestros componentes de Python. También obtendremos los archivos de desarrollo de Python necesarios para crear algunos de los componentes de Gunicorn.

      Primero, actualizaremos el índice de paquetes locales e instalaremos los paquetes que nos permitirán crear nuestro entorno de Python. Entre ellos está phyton3-pip, junto con paquetes y herramientas de desarrollo adicionales que se necesitan para un entorno de programación sólido:

      • sudo apt update
      • sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

      Una vez implementados estos paquetes, crearemos un entorno virtual para nuestro proyecto.

      Paso 2: Crear un entorno virtual de Python

      A continuación, configuraremos un entorno virtual para aislar nuestra aplicación de Flask de los otros archivos de Python del sistema.

      Comience instalando el paquete python3-venv, que instalará el módulo venv:

      • sudo apt install python3-venv

      Luego, crearemos un directorio principal para nuestro proyecto de Flask. Después de crearlo, posiciónese en él:

      • mkdir ~/myproject
      • cd ~/myproject

      Cree un entorno virtual para almacenar los requisitos de Python de su proyecto de Flask escribiendo lo siguiente:

      • python3 -m venv myprojectenv

      Con esto se instalará una copia local de Python y pip en un directorio llamado myprojectenv dentro del directorio de su proyecto.

      Antes de instalar aplicaciones dentro del entorno virtual, deberá activarlo. Hágalo escribiendo lo siguiente:

      • source myprojectenv/bin/activate

      Su mensaje cambiará para indicar que ahora realiza operaciones dentro del entorno virtual. Se parecerá a esto: (myprojectenv)user@host:~/myproject$.

      Paso 3: Configurar una aplicación de Flask

      Ahora que se encuentra en su entorno virtual, podrá instalar Flask y Gunicorn y comenzar a diseñar su aplicación.

      Primero, instalaremos wheel con la instancia local de pip para asegurarnos de que nuestros paquetes se instalen aunque falten archivos de wheel:

      Nota


      Independientemente de la versión de Phyton que use, cuando se active el entorno virtual deberá utilizar el comando pip (no pip3).

      A continuación, instalaremos Flask y Gunicorn:

      • pip install gunicorn flask

      Creación de una aplicación de ejemplo

      Ahora que dispone de Flask, puede crear una aplicación sencilla. Flask es un microframework. No cuenta con muchas de las herramientas que podrían incluirse en frameworks con más características y existe sobre todo como un módulo que puede importar a sus proyectos para que pueda inicializar una aplicación web.

      Aunque la complejidad podría ser mayor, crearemos nuestra app de Flask en un único archivo, llamado myproject.py:

      • nano ~/myproject/myproject.py

      El código de aplicación residirá en este archivo. Importará Flask y creará una instancia de un objeto de Flask. Puede utilizarlo para definir las funciones que deberían ejecutarse cuando se solicita una ruta específica:

      ~/myproject/myproject.py

      from flask import Flask
      app = Flask(__name__)
      
      @app.route("/")
      def hello():
          return "<h1 style="color:blue">Hello There!</h1>"
      
      if __name__ == "__main__":
          app.run(host="0.0.0.0")
      

      Esto define básicamente el contenido que se presentará al acceder al dominio root. Guarde y cierre el archivo cuando termine.

      Si siguió la guía de configuración inicial para servidores, debería tener activado un firewall UFW. Para probar la aplicación, debe permitir el acceso al puerto 5000:

      Ahora podrá probar su aplicación de Flask escribiendo lo siguiente:

      Verá un resultado como el siguiente, en el cual se incluirá una advertencia útil que le recordará no utilizar esta configuración de servidor en la producción:

      Output

      * Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

      Agregue :5000 al final de la dirección IP de su servidor en su navegador web y visítela:

      http://your_server_ip:5000
      

      Debería ver algo como esto:

      Aplicación de ejemplo de Flask

      Cuanto termine, pulse CTRL-C en la ventana de su terminal para detener el servidor de desarrollo Flask.

      Creación de un punto de entrada de WSGI

      A continuación, crearemos un archivo que servirá como punto de entrada para nuestra aplicación. Esto indicará a nuestro servidor de Gunicorn cómo interactuar con la aplicación.

      Llamemos al archivo wsgi.py:

      En él, importaremos la instancia de Flask desde nuestra aplicación y luego la ejecutaremos:

      ~/myproject/wsgi.py

      from myproject import app
      
      if __name__ == "__main__":
          app.run()
      

      Guarde y cierre el archivo cuando termine.

      Paso 4: Configurar Gunicorn

      Su aplicación quedará, así, escrita con un punto de entrada establecido. Ahora, podemos continuar con la configuración de Gunicorn.

      Antes de continuar, debe comprobar que Gunicorn pueda proveer correctamente la aplicación.

      Podemos hacerlo con solo pasarle el nombre de nuestro punto de entrada. Se construye como el nombre del módulo (menos la extensión .py) más el nombre del elemento invocable dentro de la aplicación. En nuestro caso, es wsgi:app.

      También especificaremos la interfaz y el puerto que se vinculará para que la aplicación se inicie en una interfaz disponible de forma pública:

      • cd ~/myproject
      • gunicorn --bind 0.0.0.0:5000 wsgi:app

      Debería ver un resultado como el siguiente:

      Output

      [2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

      Visite de nuevo la dirección IP de su servidor con :5000 agregado al final en su navegador web:

      http://your_server_ip:5000
      

      Debería ver el resultado de su aplicación:

      Aplicación de ejemplo de Flask

      Cuando confirme que funciona correctamente, pulse CTRL-C en la ventana de su terminal.

      Ya completamos las tareas de nuestro entorno virtual, por lo que podemos desactivarlo:

      Ahora todos los comandos de Python usarán de nuevo el entorno de Phyton del sistema.

      A continuación, crearemos el archivo de unidad de servicio systemd. Crear un archivo de unidad systemd permitirá que el sistema init de Ubuntu inicie automáticamente Gunicorn y haga funcionar la aplicación de Flask cuando el servidor se cargue.

      Cree un archivo de unidad terminado en .service dentro del directorio /etc/systemd/system para empezar:

      • sudo nano /etc/systemd/system/myproject.service

      En su interior, empezaremos con la sección [Unit] que se usa para especificar metadatos y dependencias. Aquí agregaremos una descripción de nuestro servicio e indicaremos al sistema init que lo inicie solo tras haber alcanzado el objetivo de red:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      

      A continuación, abriremos la sección [Service]. Esto especificará el usuario y el grupo con los cuales deseamos que se ejecute el proceso. Otorgaremos la propiedad del proceso a nuestra cuenta de usuario normal, ya que tiene la propiedad de todos los archivos pertinentes. También otorgaremos la propiedad del grupo al grupo www-data para que Nginx pueda comunicarse fácilmente con los procesos de Gunicorn. No se olvide de sustituir el nombre de usuario por el suyo:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      

      A continuación, planearemos los detalles del directorio de trabajo y estableceremos el entorno variable PATH para que el sistema init sepa que los ejecutables para el proceso están ubicados dentro de nuestro entorno virtual. También especificaremos el comando para iniciar el servicio. Este comando hará lo siguiente:

      • Iniciar 3 procesos de trabajadores (debería, no obstante, ajustar esto si es necesario)
      • Crear un archivo de socket de Unix, myproject.sock, dentro del directorio de nuestro proyecto y establecer un vínculo con él. Estableceremos un valor sin máscara de 007 para que se cree el archivo de socket, se proporcione acceso al propietario y, al mismo tiempo, se restrinjan otros accesos.
      • Especifique el nombre del archivo del punto de entrada de WSGI junto con el elemento invocable de Python dentro de ese archivo (wsgi:app).

      Systemd necesita que le proporcionemos la ruta completa al ejecutable de Gunicorn, que se instala dentro de nuestro entorno virtual.

      No se olvide de sustituir el nombre del usuario y las rutas del proyecto por su propia información:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      

      Por último, vamos a añadiremos una sección [Install]. Esto indicará a systemd a qué deberá vincular este servicio si lo habilitamos para que se cargue en el inicio. Queremos que este servicio se inicie cuando el sistema multiusuario normal esté en funcionamiento:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      
      [Install]
      WantedBy=multi-user.target
      

      Con eso, nuestro archivo de servicio de systemd quedará completo. Guárdelo y ciérrelo ahora.

      Ya podemos iniciar el servicio Gunicorn que creamos y activarlo para que se cargue en el inicio:

      • sudo systemctl start myproject
      • sudo systemctl enable myproject

      Comprobemos el estado:

      • sudo systemctl status myproject

      Debería ver el siguiente resultado:

      Output

      ● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

      Si detecta errores, asegúrese de resolverlos antes de continuar con el tutorial.

      Paso 5: Configurar Nginx para solicitudes de proxy

      Ahora, nuestro servidor de aplicación Gunicorn debería estar funcionando, esperando solicitudes en el archivo de socket del directorio del proyecto. Configuraremos Nginx para que transmita las solicitudes web al socket haciendo algunas pequeñas adiciones a su archivo de configuración.

      Comencemos creando un nuevo archivo de configuración de bloque de servidor en el directorio sites-available de Nginx. Lo llamaremos myproject para que se adecue al resto de esta guía:

      • sudo nano /etc/nginx/sites-available/myproject

      Abra un bloque de servidor e indique a Nginx que escuche en el puerto predeterminado 80. También le indicaremos que utilice este bloque para solicitudes para el nombre de dominio de nuestro servidor:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      }
      

      A continuación, agregaremos un bloque de ubicación que coincida con cada solicitud. Dentro de este bloque, incluiremos el archivo proxy_params que especifica algunos parámetros de proxy generales que deben configurarse. Luego, pasaremos las solicitudes al socket que definimos usando la directiva proxy_pass:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
          }
      }
      

      Guarde y cierre el archivo al finalizar.

      Para habilitar la configuración del bloque de servidor de Nginx que acaba de crear, vincule el archivo al directorio sites-enabled​​​:

      • sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

      Con el archivo en ese directorio, puede realizar una verificación en busca de errores de sintaxis:

      Si no se indican problemas, reinicie el proceso de Nginx para que lea la nueva configuración:

      • sudo systemctl restart nginx

      Por último, ajustaremos el firewall de nuevo. Ya no necesitamos acceso a través del puerto 5000, por lo que podemos eliminar esta regla. Luego, podemos permitir el acceso completo al servidor de Nginx:

      • sudo ufw delete allow 5000
      • sudo ufw allow 'Nginx Full'

      Ahora debería poder visitar el nombre de dominio de su servidor en su navegador web:

      http://your_domain
      

      Debería ver el resultado de su aplicación:

      Aplicación de ejemplo de Flask

      Si encuentra algún error, intente verificar lo siguiente:

      • sudo less /var/log/nginx/error.log: verifica los registros de error de Nginx.
      • sudo less /var/log/nginx/access.log: verifica los registros de acceso de Nginx.
      • sudo journalctl -u nginx: verifica los registros de proceso de Nginx.
      • sudo journalctl -u myproject: verifica los registros de Gunicorn de su app de Flask.

      Paso 6: Proteger la aplicación

      Para asegurarse de que el tráfico hacia su servidor siga siendo seguro, obtendremos un certificado SSL para su dominio. Existen varias formas de hacer esto, incluyendo obtener un certificado gratuito de Let´s Encrypt, generar un certificado auto firmado, o comprar uno de otro proveedor, y configurar Nginx para usarlo siguiendo los Pasos 2 al 6 de Cómo crear un certificado SSL auto firmado para Nginx en Ubuntu 20.04. Por motivos de conveniencia, elegiremos la primera opción.

      Instale el paquete de Nginx de Certbot con apt:

      • sudo apt install python3-certbot-nginx

      Certbot ofrece varias alternativas para obtener certificados SSL a través de complementos. El complemento de Nginx se encargará de reconfigurar Nginx y volver a cargar la configuración cuando sea necesario. Para utilizar este complemento, escriba lo siguiente:

      • sudo certbot --nginx -d your_domain -d www.your_domain

      Con esto, se ejecuta certbot con el complemento --nginx usando -d para especificar los nombres para los cuales deseamos que el certificado tenga validez.

      Si es la primera vez que ejecuta certbot, se le solicitará introducir una dirección de correo electrónico y aceptar las condiciones de servicio. A continuación, certbot se comunicará con el servidor de Let’s Encrypt y, luego, realizará una comprobación para verificar que usted controle el dominio para el que solicita un certificado.

      Si la comprobación se realiza correctamente, certbot le preguntará cómo desea configurar sus ajustes de HTTPS:

      Output

      Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

      Seleccione su elección y luego ENTER. La configuración se actualizará y Nginx se volverá a cargar para aplicar los ajustes nuevos. certbot concluirá con un mensaje que le indicará que el proceso tuvo éxito e indicará la ubicación de almacenamiento de sus certificados:

      Output

      IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

      Si siguió las instrucciones de instalación de Nginx en los requisitos previos, ya no necesitará la asignación de perfil HTTP redundante:

      • sudo ufw delete allow 'Nginx HTTP'

      Para verificar la configuración, acceda una vez más a su dominio utilizando https://:

      https://your_domain
      

      Una vez más, debería ver el resultado de su aplicación junto con el indicador de seguridad de su navegador, el cual debería indicar que el sitio está protegido.

      Conclusión

      A través de esta guía, creó y aseguró una aplicación de Flask simple dentro de un entorno virtual de Python. Creó un punto de entrada de WSGI para que cualquier servidor de aplicación con capacidad para WSGI pueda interactuar con él y configuró el servidor de aplicación de Gunicorn para proporcionar esta función. Luego, creó un archivo de servicio systemd para iniciar automáticamente el servidor de aplicación en el inicio. También creó un bloque de servidor de Nginx que transmite el tráfico de clientes web al servidor de la aplicación, y reenvía solicitudes externas, y protegió el tráfico hacia su servidor con Let’s Encrypt.

      Flask es un framework muy sencillo, pero extremadamente flexible, diseñado para proporcionar funcionalidad a sus aplicaciones sin ser demasiado restrictivo respecto de la estructura y del diseño. Puede utilizar la pila general descrita en esta guía para hacer funcionar las aplicaciones de Flask que diseñe.



      Source link