Difference between revisions of "Make a simple blog using Flask"

From Parallel Library Services
Jump to navigation Jump to search
(Replaced content with "Flask is a micro web framework written in Python, for making web applications. A good tutorial is provided by Abdelhadi Dyouri on the website digitalocean.com: https...")
Tag: Replaced
Line 1: Line 1:
<div class="outside_viewport">
[[Flask]] is a micro web framework written in Python, for making web applications.


<div class="table-of-contents has-init">
A good tutorial is provided by Abdelhadi Dyouri on the website digitalocean.com:


<div class="tocify" js="tableOfContentsDesktop">
https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3


* [[#prerequisites|Prerequisites]]
With this tutorial, you can make a simple flask blog web application, with ways to post, edit and delete content. Future augmentation could also include adding secure features. At the end of the tutorial are links to ways the app can be extended.


* [[#step-1-—-installing-flask|Step 1 — Installing Flask]]
[[Category:Cookbook]]
 
[[Category:Flask]]
* [[#step-2-—-creating-a-base-application|Step 2 — Creating a Base Application]]
 
* [[#step-3-—-using-html-templates|Step 3 — Using HTML templates]]
 
* [[#step-4-—-setting-up-the-database|Step 4 — Setting up the Database]]
 
* [[#step-5-—-displaying-all-posts|Step 5 — Displaying All Posts]]
 
* [[#step-6-—-displaying-a-single-post|Step 6 — Displaying a Single Post]]
 
* [[#step-7-—-modifying-posts|Step 7 — Modifying Posts]]
 
* [[#conclusion|Conclusion]]
 
 
==== Tutorial ====
 
= How To Make a Web Application Using Flask in Python 3 =
 
<li>[[community/users/adyouri|[[File:https://secure.gravatar.com/avatar/c11acca84b7b1a7a5cb3e44c42a2789f?secure=true&d=identicon|80x80px|class=avatar avatar-large|adyouri]]]]</li>
<li>
 
By Abdelhadi Dyouri, April 16, 2020
 
=== Introduction ===
 
[http://flask.pocoo.org/ Flask] is a small and lightweight Python web framework that provides useful tools and features that make creating web applications in Python easier. It gives developers flexibility and is a more accessible framework for new developers since you can build a web application quickly using only a single Python file. Flask is also extensible and doesn’t force a particular directory structure or require complicated boilerplate code before getting started.
 
As part of this tutorial, you’ll use the [https://getbootstrap.com/ Bootstrap toolkit] to style your application so it is more visually appealing. Bootstrap will help you incorporate responsive web pages in your web application so that it also works well on mobile browsers without writing your own HTML, CSS, and JavaScript code to achieve these goals. The toolkit will allow you to focus on learning how Flask works.
 
Flask uses the [http://jinja.palletsprojects.com/ Jinja template engine] to dynamically build HTML pages using familiar Python concepts such as variables, loops, lists, and so on. You’ll use these templates as part of this project.
 
In this tutorial, you’ll build a small web blog using Flask and [https://sqlite.org SQLite] in Python 3. Users of the application can view all the posts in your database and click on the title of a post to view its contents with the ability to add a new post to the database and edit or delete an existing post.
 
== Prerequisites ==
 
Before you start following this guide, you will need:
 
* A local Python 3 programming environment, follow the tutorial for your distribution in [https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3 How To Install and Set Up a Local Programming Environment for Python 3] series for your local machine. In this tutorial we’ll call our project directory <code>flask_blog</code>.
* An understanding of Python 3 concepts, such as [https://www.digitalocean.com/community/tutorials/understanding-data-types-in-python-3 data types], [https://www.digitalocean.com/community/tutorials/how-to-write-conditional-statements-in-python-3-2 conditional statements], [https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3 for loops], [https://www.digitalocean.com/community/tutorials/how-to-define-functions-in-python-3 functions], and other such concepts. If you are not familiar with Python, check out our [https://www.digitalocean.com/community/tutorial_series/how-to-code-in-python-3 How To Code in Python 3] series.
 
== Step 1 — Installing Flask ==
 
In this step, you’ll activate your Python environment and install Flask using the [https://pypi.org/project/pip/ <code>pip</code>] package installer.
 
If you haven’t already activated your programming environment, make sure you’re in your project directory (<code>flask_blog</code>) and use the following command to activate the environment:
 
<pre class="code-pre command prefixed">source env/bin/activate</pre>
Once your programming environment is activated, your prompt will now have an <code>env</code> prefix that may look as follows:
 
<pre class="code-pre custom_prefix prefixed">(env)</pre>
This prefix is an indication that the environment <code>env</code> is currently active, which might have another name depending on how you named it during creation.
 
<span class="note"></span>
'''Note:''' You can use [https://git-scm.com/ Git], a version control system, to effectively manage and track the development process for your project. To learn how to use Git, you might want to check out our [https://www.digitalocean.com/community/tutorial_series/introduction-to-git-installation-usage-and-branches Introduction to Git Installation Usage and Branches] article.
 
If you are using Git, it is a good idea to ignore the newly created <code>env</code> directory in your <code>.gitignore</code> file to avoid tracking files not related to the project.<br />
 
 
Now you’ll install Python packages and isolate your project code away from the main Python system installation. You’ll do this using <code>pip</code> and <code>python</code>.
 
To install Flask, run the following command:
 
<pre class="code-pre custom_prefix prefixed">pip install flask</pre>
Once the installation is complete, run the following command to confirm the installation:
 
<pre class="code-pre custom_prefix prefixed">python -c &quot;import flask; print(flask.__version__)&quot;</pre>
You use the [https://docs.python.org/3/using/cmdline.html <code>python</code> command line interface] with the option <code>-c</code> to execute Python code. Next you import the <code>flask</code> package with <code>import flask;</code> then print the Flask version, which is provided via the <code>flask.__version__</code> variable.
 
The output will be a version number similar to the following:
 
<pre class="code-pre">Output1.1.2</pre>
You’ve created the project folder, a virtual environment, and installed Flask. You’re now ready to move on to setting up your base application.
 
== Step 2 — Creating a Base Application ==
 
Now that you have your programming environment set up, you’ll start using Flask. In this step, you’ll make a small web application inside a Python file and run it to start the server, which will display some information on the browser.
 
In your <code>flask_blog</code> directory, open a file named <code>hello.py</code> for editing, use <code>nano</code> or your favorite text editor:
 
<pre class="code-pre custom_prefix prefixed">nano hello.py</pre>
This <code>hello.py</code> file will serve as a minimal example of how to handle HTTP requests. Inside it, you’ll import the [https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask <code>Flask</code> object], and create a function that returns an HTTP response. Write the following code inside <code>hello.py</code>:
 
<div class="code-label" title="flask_blog/hello.py">
 
flask_blog/hello.py
 
</div>
<pre class="code-pre">from flask import Flask
 
app = Flask(__name__)
 
 
@app.route('/')
def hello():
    return 'Hello, World!'</pre>
In the preceding code block, you first import the <code>Flask</code> object from the <code>flask</code> package. You then use it to create your Flask application instance with the name <code>app</code>. You pass the special variable <code>__name__</code> that holds the name of the current Python module. It’s used to tell the instance where it’s located—you need this because Flask sets up some paths behind the scenes.
 
Once you create the <code>app</code> instance, you use it to handle incoming web requests and send responses to the user. <code>@app.route</code> is a [https://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators decorator] that turns a regular Python function into a Flask ''view function'', which converts the function’s return value into an HTTP response to be displayed by an HTTP client, such as a web browser. You pass the value <code>'/'</code> to <code>@app.route()</code> to signify that this function will respond to web requests for the URL <code>/</code>, which is the main URL.
 
The <code>hello()</code> view function returns the string <code>'Hello, World!'</code> as a response.
 
Save and close the file.
 
To run your web application, you’ll first tell Flask where to find the application (the <code>hello.py</code> file in your case) with the <code>FLASK_APP</code> environment variable:
 
<pre class="code-pre custom_prefix prefixed">export FLASK_APP=hello</pre>
Then run it in development mode with the <code>FLASK_ENV</code> environment variable:
 
<pre class="code-pre custom_prefix prefixed">export FLASK_ENV=development</pre>
Lastly, run the application using the <code>flask run</code> command:
 
<pre class="code-pre custom_prefix prefixed">flask run</pre>
Once the application is running the output will be something like this:
 
<pre class="code-pre">Output * Serving Flask app &quot;hello&quot; (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</pre>
The preceding output has several pieces of information, such as:
 
* The name of the application you’re running.
* The environment in which the application is being run.
* <code>Debug mode: on</code> signifies that the Flask debugger is running. This is useful when developing because it gives us detailed error messages when things go wrong, which makes troubleshooting easier.
* The application is running locally on the URL <code>http://127.0.0.1:5000/</code>, <code>127.0.0.1</code> is the IP that represents your machine’s <code>localhost</code> and <code>:5000</code> is the port number.
 
Open a browser and type in the URL <code>http://127.0.0.1:5000/</code>, you will receive the string <code>Hello, World!</code> as a response, this confirms that your application is successfully running.
 
<span class="warning">'''Warning''' Flask uses a simple web server to serve our application in a development environment, which also means that the Flask debugger is running to make catching errors easier. This development server should not be used in a production deployment. See the [https://flask.palletsprojects.com/en/1.1.x/deploying/ Deployment Options] page on the Flask documentation for more information, you can also check out this [https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-18-04 Flask deployment tutorial].<br />
</span>
 
You can now leave the development server running in the terminal and open another terminal window. Move into the project folder where <code>hello.py</code> is located, activate the virtual environment, set the environment variables <code>FLASK_ENV</code> and <code>FLASK_APP</code>, and continue to the next steps. (These commands are listed earlier in this step.)
 
<span class="note"></span>
'''Note''': When opening a new terminal, it is important to remember activating the virtual environment and setting the environment variables <code>FLASK_ENV</code> and <code>FLASK_APP</code>.
 
While a Flask application’s development server is already running, it is not possible to run another Flask application with the same <code>flask run</code> command. This is because <code>flask run</code> uses the port number <code>5000</code> by default, and once it is taken, it becomes unavailable to run another application on so you would receive an error similar to the following:
 
<pre class="code-pre">OutputOSError: [Errno 98] Address already in use</pre>
To solve this problem, either stop the server that’s currently running via <code>CTRL+C</code>, then run <code>flask run</code> again, or if you want to run both at the same time, you can pass a different port number to the <code>-p</code> argument, for example, to run another application on port <code>5001</code> use the following command:
 
<pre class="code-pre custom_prefix prefixed">flask run -p 5001</pre>
You now have a small Flask web application. You’ve run your application and displayed information on the web browser. Next, you’ll use HTML files in your application.
 
== Step 3 — Using HTML templates ==
 
Currently your application only displays a simple message without any HTML. Web applications mainly use HTML to display information for the visitor, so you’ll now work on incorporating HTML files in your app, which can be displayed on the web browser.
 
Flask provides a <code>render_template()</code> helper function that allows use of the [http://jinja.pocoo.org/ Jinja template engine]. This will make managing HTML much easier by writing your HTML code in <code>.html</code> files as well as using logic in your HTML code. You’ll use these HTML files, (''templates'') to build all of your application pages, such as the main page where you’ll display the current blog posts, the page of the blog post, the page where the user can add a new post, and so on.
 
In this step, you’ll create your main Flask application in a new file.
 
First, in your <code>flask_blog</code> directory, use <code>nano</code> or your favorite editor to create and edit your <code>app.py</code> file. This will hold all the code you’ll use to create the blogging application:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
In this new file, you’ll import the <code>Flask</code> object to create a Flask application instance as you previously did. You’ll also import the [https://flask.palletsprojects.com/en/1.1.x/api/#flask.render_template <code>render_template()</code>] helper function that lets you render HTML template files that exist in the <code>templates</code> folder you’re about to create. The file will have a single view function that will be responsible for handling requests to the main <code>/</code> route. Add the following content:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">from flask import Flask, render_template
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return render_template('index.html')</pre>
The <code>index()</code> view function returns the result of calling <code>render_template()</code> with <code>index.html</code> as an argument, this tells <code>render_template()</code> to look for a file called <code>index.html</code> in the ''templates folder''. Both the folder and the file do not yet exist, you will get an error if you were to run the application at this point. You’ll run it nonetheless so you’re familiar with this commonly encountered exception. You’ll then fix it by creating the needed folder and file.
 
Save and exit the file.
 
Stop the development server in your other terminal that runs the <code>hello</code> application with <code>CTRL+C</code>.
 
Before you run the application, make sure you correctly specify the value for the <code>FLASK_APP</code> environment variable, since you’re no longer using the application <code>hello</code>:
 
<pre class="code-pre custom_prefix prefixed">export FLASK_APP=app
flask run</pre>
Opening the URL <code>http://127.0.0.1:5000/</code> in your browser will result in the debugger page informing you that the <code>index.html</code> template was not found. The main line in the code that was responsible for this error will be highlighted. In this case, it is the line <code>return render_template('index.html')</code>.
 
If you click this line, the debugger will reveal more code so that you have more context to help you solve the problem.
 
[[File:https://assets.digitalocean.com/articles/webflaskapp/step3a.png|The Flask Debugger]]
 
To fix this error, create a directory called <code>templates</code> inside your <code>flask_blog</code> directory. Then inside it, open a file called <code>index.html</code> for editing:
 
<pre class="code-pre custom_prefix prefixed">mkdir templates
nano templates/index.html</pre>
Next, add the following HTML code inside <code>index.html</code>:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;FlaskBlog&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Welcome to FlaskBlog&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
Save the file and use your browser to navigate to <code>http://127.0.0.1:5000/</code> again, or refresh the page. This time the browser should display the text <code>Welcome to FlaskBlog</code> in an <code>&lt;h1&gt;</code> tag.
 
In addition to the <code>templates</code> folder, Flask web applications also typically have a <code>static</code> folder for hosting static files, such as CSS files, JavaScript files, and images the application uses.
 
You can create a <code>style.css</code> style sheet file to add CSS to your application. First, create a directory called <code>static</code> inside your main <code>flask_blog</code> directory:
 
<pre class="code-pre custom_prefix prefixed">mkdir static</pre>
Then create another directory called <code>css</code> inside the <code>static</code> directory to host <code>.css</code> files. This is typically done to organize static files in dedicated folders, as such, JavaScript files typically live inside a directory called <code>js</code>, images are put in a directory called <code>images</code> (or <code>img</code>), and so on. The following command will create the <code>css</code> directory inside the <code>static</code> directory:
 
<pre class="code-pre custom_prefix prefixed">mkdir static/css</pre>
Then open a <code>style.css</code> file inside the <code>css</code> directory for editing:
 
<pre class="code-pre custom_prefix prefixed">nano static/css/style.css</pre>
Add the following CSS rule to your <code>style.css</code> file:
 
<div class="code-label" title="flask_blog/static/css/style.css">
 
flask_blog/static/css/style.css
 
</div>
<pre class="code-pre">h1 {
    border: 2px #eee solid;
    color: brown;
    text-align: center;
    padding: 10px;
}</pre>
The CSS code will add a border, change the color to brown, center the text, and add a little padding to <code>&lt;h1&gt;</code> tags.
 
Save and close the file.
 
Next, open the <code>index.html</code> template file for editing:
 
<pre class="code-pre custom_prefix prefixed">nano templates/index.html</pre>
You’ll add a link to the <code>style.css</code> file inside the <code>&lt;head&gt;</code> section of the <code>index.html</code> template file:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">. . .
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;{{ url_for('static', filename= 'css/style.css') }}&quot;&gt;
    &lt;title&gt;FlaskBlog&lt;/title&gt;
&lt;/head&gt;
. . .</pre>
Here you use the [https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for <code>url_for()</code>] helper function to generate the appropriate location of the file. The first argument specifies that you’re linking to a static file and the second argument is the path of the file inside the static directory.
 
Save and close the file.
 
Upon refreshing the index page of your application, you will notice that the text <code>Welcome to FlaskBlog</code> is now in brown, centered, and enclosed inside a border.
 
You can use the CSS language to style the application and make it more appealing using your own design. However, if you’re not a web designer, or if you aren’t familiar with CSS, then you can use the [https://getbootstrap.com/ Bootstrap toolkit], which provides easy-to-use components for styling your application. In this project, we’ll use Bootstrap.
 
You might have guessed that making another HTML template would mean repeating most of the HTML code you already wrote in the <code>index.html</code> template. You can avoid unnecessary code repetition with the help of a ''base template'' file, which all of your HTML files will inherit from. See [https://jinja.palletsprojects.com/en/2.10.x/templates/#template-inheritance Template Inheritance in Jinja] for more information.
 
To make a base template, first create a file called <code>base.html</code> inside your <code>templates</code> directory:
 
<pre class="code-pre custom_prefix prefixed">nano templates/base.html</pre>
Type the following code in your <code>base.html</code> template:
 
<div class="code-label" title="flask_blog/templates/base.html">
 
flask_blog/templates/base.html
 
</div>
<pre class="code-pre">&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
 
    &lt;!-- Bootstrap CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css&quot; integrity=&quot;sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T&quot; crossorigin=&quot;anonymous&quot;&gt;
 
    &lt;title&gt;{% block title %} {% endblock %}&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;nav class=&quot;navbar navbar-expand-md navbar-light bg-light&quot;&gt;
        &lt;a class=&quot;navbar-brand&quot; href=&quot;{{ url_for('index')}}&quot;&gt;FlaskBlog&lt;/a&gt;
        &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
            &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
        &lt;/button&gt;
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
            &lt;ul class=&quot;navbar-nav&quot;&gt;
            &lt;li class=&quot;nav-item active&quot;&gt;
                &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;
            &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/nav&gt;
    &lt;div class=&quot;container&quot;&gt;
        {% block content %} {% endblock %}
    &lt;/div&gt;
 
    &lt;!-- Optional JavaScript --&gt;
    &lt;!-- jQuery first, then Popper.js, then Bootstrap JS --&gt;
    &lt;script src=&quot;https://code.jquery.com/jquery-3.3.1.slim.min.js&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js&quot; integrity=&quot;sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js&quot; integrity=&quot;sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
Save and close the file once you’re done editing it.
 
Most of the code in the preceding block is standard HTML and code required for Bootstrap. The <code>&lt;meta&gt;</code> tags provide information for the web browser, the <code>&lt;link&gt;</code> tag links the Bootstrap CSS files, and the <code>&lt;script&gt;</code> tags are links to JavaScript code that allows some additional Bootstrap features, check out the [https://getbootstrap.com/ Bootstrap documentation] for more.
 
However, the following highlighted parts are specific to the Jinja template engine:
 
* <code>{% block title %} {% endblock %}</code>: A [https://jinja.palletsprojects.com/en/2.10.x/templates/#blocks block] that serves as a placeholder for a title, you’ll later use it in other templates to give a custom title for each page in your application without rewriting the entire <code>&lt;head&gt;</code> section each time.
* <code>{{ url_for('index')}}</code>: A function call that will return the URL for the <code>index()</code> view function. This is different from the past <code>url_for()</code> call you used to link a static CSS file, because it only takes one argument, which is the view function’s name, and links to the route associated with the function instead of a static file.
* <code>{% block content %} {% endblock %}</code>: Another block that will be replaced by content depending on the ''child template'' (templates that inherit from <code>base.html</code>) that will override it.
 
Now that you have a base template, you can take advantage of it using inheritance. Open the <code>index.html</code> file:
 
<pre class="code-pre custom_prefix prefixed">nano templates/index.html</pre>
Then replace its contents with the following:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
    &lt;h1&gt;{% block title %} Welcome to FlaskBlog {% endblock %}&lt;/h1&gt;
{% endblock %}</pre>
In this new version of the <code>index.html</code> template, you use the <code>{% extends %}</code> tag to inherit from the <code>base.html</code> template. You then extend it via replacing the <code>content</code> block in the base template with what is inside the <code>content</code> block in the preceding code block.
 
This <code>content</code> block contains an <code>&lt;h1&gt;</code> tag with the text <code>Welcome to FlaskBlog</code> inside a <code>title</code> block, which in turn replaces the original <code>title</code> block in the <code>base.html</code> template with the text <code>Welcome to FlaskBlog</code>. This way, you can avoid repeating the same text twice, as it works both as a title for the page and a heading that appears below the navigation bar inherited from the base template.
 
Template inheritance also gives you the ability to reuse the HTML code you have in other templates (<code>base.html</code> in this case) without having to repeat it each time it is needed.
 
Save and close the file and refresh the index page on your browser. You’ll see your page with a navigation bar and styled title.
 
[[File:https://assets.digitalocean.com/articles/webflaskapp/step3b.png|Index Page with Bootstrap]]
 
You’ve used HTML templates and static files in Flask. You also used Bootstrap to start refining the look of your page and a base template to avoid code repetition. In the next step, you’ll set up a database that will store your application data.
 
== Step 4 — Setting up the Database ==
 
In this step, you’ll set up a database to store data, that is, the blog posts for your application. You’ll also populate the database with a few example entries.
 
You’ll use a [https://sqlite.org SQLite database] file to store your data because the [https://docs.python.org/3/library/sqlite3.html <code>sqlite3</code>] module, which we will use to interact with the database, is readily available in the standard Python library. For more information about SQLite, check out [https://www.digitalocean.com/community/tutorials/how-and-when-to-use-sqlite this tutorial].
 
First, because data in SQLite is stored in tables and columns, and since your data mainly consists of blog posts, you first need to create a table called <code>posts</code> with the necessary columns. You’ll create a <code>.sql</code> file that contains SQL commands to create the <code>posts</code> table with a few columns. You’ll then use this file to create the database.
 
Open a file called <code>schema.sql</code> inside your <code>flask_blog</code> directory:
 
<pre class="code-pre custom_prefix prefixed">nano schema.sql</pre>
Type the following SQL commands inside this file:
 
<div class="code-label" title="flask_blog/schema.sql">
 
flask_blog/schema.sql
 
</div>
<pre class="code-pre">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
);</pre>
Save and close the file.
 
The first SQL command is <code>DROP TABLE IF EXISTS posts;</code>, this deletes any already existing tables named <code>posts</code> so you don’t get 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, <code>CREATE TABLE posts</code> is used to create the <code>posts</code> table with the following columns:
 
* <code>id</code>: An integer that represents a ''primary key'', this will get assigned a unique value by the database for each entry (that is a blog post).
* <code>created</code>: The time the blog post was created at. <code>NOT NULL</code> signifies that this column should not be empty and the <code>DEFAULT</code> value is the <code>CURRENT_TIMESTAMP</code> value, which is the time at which the post was added to the database. Just like <code>id</code>, you don’t need to specify a value for this column, as it will be automatically filled in.
* <code>title</code>: The post title.
* <code>content</code>: The post content.
 
Now that you have a SQL schema in the <code>schema.sql</code> file, you’ll use it to create the database using a Python file that will generate an SQLite <code>.db</code> database file. Open a file named <code>init_db.py</code> inside the <code>flask_blog</code> directory using your preferred editor:
 
<pre class="code-pre custom_prefix prefixed">nano init_db.py</pre>
And then add the following code.
 
<div class="code-label" title="flask_blog/init_db.py">
 
flask_blog/init_db.py
 
</div>
<pre class="code-pre">import sqlite3
 
connection = sqlite3.connect('database.db')
 
 
with open('schema.sql') as f:
    connection.executescript(f.read())
 
cur = connection.cursor()
 
cur.execute(&quot;INSERT INTO posts (title, content) VALUES (?, ?)&quot;,
            ('First Post', 'Content for the first post')
            )
 
cur.execute(&quot;INSERT INTO posts (title, content) VALUES (?, ?)&quot;,
            ('Second Post', 'Content for the second post')
            )
 
connection.commit()
connection.close()</pre>
You first import the <code>sqlite3</code> module and then open a connection to a database file named <code>database.db</code>, which will be created once you run the Python file. Then you use the <code>open()</code> function to open the <code>schema.sql</code> file. Next you execute its contents using the [https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.executescript <code>executescript()</code>] method that executes multiple SQL statements at once, which will create the <code>posts</code> table. You create a [https://docs.python.org/3/library/sqlite3.html#cursor-objects Cursor object] that allows you to use its [https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.execute <code>execute()</code>] method to execute two <code>INSERT</code> SQL statements to add two blog posts to your <code>posts</code> table. Finally, you commit the changes and close the connection.
 
Save and close the file and then run it in the terminal using the <code>python</code> command:
 
<pre class="code-pre custom_prefix prefixed">python init_db.py</pre>
Once the file finishes execution, a new file called <code>database.db</code> will appear in your <code>flask_blog</code> directory. This means you’ve successfully set up your database.
 
In the next step, you’ll retrieve the posts you inserted into your database and display them in your application’s homepage.
 
== Step 5 — Displaying All Posts ==
 
Now that you’ve set up your database, you can now modify the <code>index()</code> view function to display all the posts you have in your database.
 
Open the <code>app.py</code> file to make the following modifications:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
For your first modification, you’ll import the <code>sqlite3</code> module at the top of the file:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">import sqlite3
from flask import Flask, render_template
 
. . .</pre>
Next, you’ll create a function that creates a database connection and return it. Add it directly after the imports:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
from flask import Flask, render_template
 
def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn
 
. . .</pre>
This <code>get_db_connection()</code> function opens a connection to the <code>database.db</code> database file, and then sets the [https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.row_factory <code>row_factory</code>] attribute to <code>sqlite3.Row</code> so you can have name-based access to columns. This means that the database connection will return rows that behave like regular Python dictionaries. Lastly, the function returns the <code>conn</code> connection object you’ll be using to access the database.
 
After defining the <code>get_db_connection()</code> function, modify the <code>index()</code> function to look like the following:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)</pre>
In this new version of the <code>index()</code> function, you first open a database connection using the <code>get_db_connection()</code> function you defined earlier. Then you execute an SQL query to select all entries from the <code>posts</code> table. You implement the [https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchall <code>fetchall()</code>] method to fetch all the rows of the query result, this will return a list of the posts you inserted into the database in the previous step.
 
You close the database connection using the <code>close()</code> method and return the result of rendering the <code>index.html</code> template. You also pass the <code>posts</code> object as an argument, which contains the results you got from the database, this will allow you to access the blog posts in the <code>index.html</code> template.
 
With these modifications in place, save and close the <code>app.py</code> file.
 
Now that you’ve passed the posts you fetched from the database to the <code>index.html</code> template, you can use a [https://jinja.palletsprojects.com/en/2.10.x/templates/#for <code>for</code> loop] to display each post on your index page.
 
Open the <code>index.html</code> file:
 
<pre class="code-pre custom_prefix prefixed">nano templates/index.html</pre>
Then, modify it to look as follows:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
    &lt;h1&gt;{% block title %} Welcome to FlaskBlog {% endblock %}&lt;/h1&gt;
    {% for post in posts %}
        &lt;a href=&quot;#&quot;&gt;
            &lt;h2&gt;{{ post['title'] }}&lt;/h2&gt;
        &lt;/a&gt;
        &lt;span class=&quot;badge badge-primary&quot;&gt;{{ post['created'] }}&lt;/span&gt;
        &lt;hr&gt;
    {% endfor %}
{% endblock %}</pre>
Here, the syntax <code>{% for post in posts %}</code> is a Jinja <code>for</code> loop, which is similar to a Python <code>for</code> loop except that it has to be later closed with the <code>{% endfor %}</code> syntax. You use this syntax to loop over each item in the <code>posts</code> list that was passed by the <code>index()</code> function in the line <code>return render_template('index.html', posts=posts)</code>. Inside this <code>for</code> loop, you display the post title in an <code>&lt;h2&gt;</code> heading inside an <code>&lt;a&gt;</code> tag (you’ll later use this tag to link to each post individually).
 
You display the title using a literal variable delimiter (<code>{{ ... }}</code>). Remember that <code>post</code> will be a dictionary-like object, so you can access the post title with <code>post['title']</code>. You also display the post creation date using the same method.
 
Once you are done editing the file, save and close it. Then navigate to the index page in your browser. You’ll see the two posts you added to the database on your page.
 
[[File:https://assets.digitalocean.com/articles/webflaskapp/step5.png|Index Page with Posts Displayed]]
 
Now that you’ve modified the <code>index()</code> view function to display all the posts you have in the database on your application’s homepage, you’ll move on to display each post in a single page and allow users to link to each individual post.
 
== Step 6 — Displaying a Single Post ==
 
In this step, you’ll create a new Flask route with a view function and a new HTML template to display an individual blog post by its ID.
 
By the end of this step, the URL <code>http://127.0.0.1:5000/1</code> will be a page that displays the first post (because it has the ID <code>1</code>). The <code>http://127.0.0.1:5000/ID</code> URL will display the post with the associated <code>ID</code> number if it exists.
 
Open <code>app.py</code> for editing:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
Since you’ll need to get a blog post by its ID from the database in multiple locations later in this project, you’ll create a standalone function called <code>get_post()</code>. You can call it by passing it an ID and receive back the blog post associated with the provided ID, or make Flask respond with a <code>404 Not Found</code> message if the blog post does not exist.
 
To respond with a <code>404</code> page, you need to import the [https://werkzeug.palletsprojects.com/en/0.15.x/exceptions/#werkzeug.exceptions.abort <code>abort()</code>] function from the [https://werkzeug.palletsprojects.com/en/0.15.x/ <code>Werkzeug</code>] library, which was installed along with Flask, at the top of the file:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">import sqlite3
from flask import Flask, render_template
from werkzeug.exceptions import abort
 
. . .</pre>
Then, add the <code>get_post()</code> function right after the <code>get_db_connection()</code> function you created in the previous step:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
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
 
. . .</pre>
This new function has a <code>post_id</code> argument that determines what blog post to return.
 
Inside the function, you use the <code>get_db_connection()</code> function to open a database connection and execute a SQL query to get the blog post associated with the given <code>post_id</code> value. You add the <code>fetchone()</code> method to get the result and store it in the <code>post</code> variable then close the connection. If the <code>post</code> variable has the value <code>None</code>, meaning no result was found in the database, you use the <code>abort()</code> function you imported earlier to respond with a <code>404</code> error code and the function will finish execution. If however, a post was found, you return the value of the <code>post</code> variable.
 
Next, add the following view function at the end of the <code>app.py</code> file:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
@app.route('/&lt;int:post_id&gt;')
def post(post_id):
    post = get_post(post_id)
    return render_template('post.html', post=post)</pre>
In this new view function, you add a [https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules ''variable rule''] <code>&lt;int:post_id&gt;</code> to specify that the part after the slash (<code>/</code>) is a positive integer (marked with the <code>int</code> converter) that you need to access in your view function. Flask recognizes this and passes its value to the <code>post_id</code> keyword argument of your <code>post()</code> view function. You then use the <code>get_post()</code> function to get the blog post associated with the specified ID and store the result in the <code>post</code> variable, which you pass to a <code>post.html</code> template that you’ll soon create.
 
Save the <code>app.py</code> file and open a new <code>post.html</code> template file for editing:
 
<pre class="code-pre custom_prefix prefixed">nano templates/post.html</pre>
Type the following code in this new <code>post.html</code> file. This will be similar to the <code>index.html</code> file, except that it will only display a single post, in addition to also displaying the contents of the post:
 
<div class="code-label" title="flask_blog/templates/post.html">
 
flask_blog/templates/post.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
    &lt;h2&gt;{% block title %} {{ post['title'] }} {% endblock %}&lt;/h2&gt;
    &lt;span class=&quot;badge badge-primary&quot;&gt;{{ post['created'] }}&lt;/span&gt;
    &lt;p&gt;{{ post['content'] }}&lt;/p&gt;
{% endblock %}</pre>
You add the <code>title</code> block that you defined in the <code>base.html</code> template to make the title of the page reflect the post title that is displayed in an <code>&lt;h2&gt;</code> heading at the same time.
 
Save and close the file.
 
You can now navigate to the following URLs to see the two posts you have in your database, along with a page that tells the user that the requested blog post was not found (since there is no post with an ID number of <code>3</code> so far):
 
<pre class="code-pre">http://127.0.0.1:5000/1
http://127.0.0.1:5000/2
http://127.0.0.1:5000/3</pre>
Going back to the index page, you’ll make each post title link to its respective page. You’ll do this using the <code>url_for()</code> function. First, open the <code>index.html</code> template for editing:
 
<pre class="code-pre custom_prefix prefixed">nano templates/index.html</pre>
Then change the value of the <code>href</code> attribute from <code>#</code> to <code>{{ url_for('post', post_id=post['id']) }}</code> so that the <code>for</code> loop will look exactly as follows:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">{% for post in posts %}
    &lt;a href=&quot;{{ url_for('post', post_id=post['id']) }}&quot;&gt;
        &lt;h2&gt;{{ post['title'] }}&lt;/h2&gt;
    &lt;/a&gt;
    &lt;span class=&quot;badge badge-primary&quot;&gt;{{ post['created'] }}&lt;/span&gt;
    &lt;hr&gt;
{% endfor %}</pre>
Here, you pass <code>'post'</code> to the <code>url_for()</code> function as a first argument. This is the name of the <code>post()</code> view function and since it accepts a <code>post_id</code> argument, you give it the value <code>post['id']</code>. The <code>url_for()</code> function will return the proper URL for each post based on its ID.
 
Save and close the file.
 
The links on the index page will now function as expected. With this, you’ve now finished building the part of the application responsible for displaying the blog posts in your database. Next, you’ll add the ability to create, edit, and delete blog posts to your application.
 
== Step 7 — Modifying Posts ==
 
Now that you’ve finished displaying the blog posts that are present in the database on the web application, you need to allow the users of your application to write new blog posts and add them to the database, edit the existing ones, and delete unnecessary blog posts.
 
=== Creating a New Post ===
 
Up to this point, you have an application that displays the posts in your database but provides no way of adding a new post unless you directly connect to the SQLite database and add one manually. In this section, you’ll create a page on which you will be able to create a post by providing its title and content.
 
Open the <code>app.py</code> file for editing:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
First, you’ll import the following from the Flask framework:
 
* The global [https://flask.palletsprojects.com/en/1.1.x/api/#flask.request <code>request</code>] object to access incoming request data that will be submitted via an HTML form.
* The [https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for <code>url_for()</code>] function to generate URLs.
* The [https://flask.palletsprojects.com/en/1.1.x/api/#flask.flash <code>flash()</code>] function to flash a message when a request is processed.
* The [https://flask.palletsprojects.com/en/1.1.x/api/#flask.redirect <code>redirect()</code>] function to redirect the client to a different location.
 
Add the imports to your file like the following:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
from werkzeug.exceptions import abort
 
. . .</pre>
The <code>flash()</code> function stores flashed messages in the client’s browser session, which requires setting a ''secret key''. This secret key is used to secure sessions, which allow Flask to remember information from one request to another, such as moving from the new post page to the index page. The user can access the information stored in the session, but cannot modify it unless they have the secret key, so you must never allow anyone to access your secret key. See [https://flask.palletsprojects.com/en/1.1.x/api/#sessions the Flask documentation for sessions] for more information.
 
To set a ''secret key'', you’ll add a <code>SECRET_KEY</code> configuration to your application via the <code>app.config</code> object. Add it directly following the <code>app</code> definition before defining the <code>index()</code> view function:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
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)
 
. . .</pre>
Remember that the secret key should be a long random string.
 
After setting a secret key, you’ll create a view function that will render a template that displays a form you can fill in to create a new blog post. Add this new function at the bottom of the file:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
@app.route('/create', methods=('GET', 'POST'))
def create():
    return render_template('create.html')</pre>
This creates a <code>/create</code> route that accepts both GET and POST requests. GET requests are accepted by default. To also accept POST requests, which are sent by the browser when submitting forms, you’ll pass a [https://www.digitalocean.com/community/tutorials/understanding-tuples-in-python-3 tuple] with the accepted types of requests to the <code>methods</code> argument of the <code>@app.route()</code> decorator.
 
Save and close the file.
 
To create the template, open a file called <code>create.html</code> inside your <code>templates</code> folder:
 
<pre class="code-pre custom_prefix prefixed">nano templates/create.html</pre>
Add the following code inside this new file:
 
<div class="code-label" title="flask_blog/templates/create.html">
 
flask_blog/templates/create.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
&lt;h1&gt;{% block title %} Create a New Post {% endblock %}&lt;/h1&gt;
 
&lt;form method=&quot;post&quot;&gt;
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;title&quot;&gt;Title&lt;/label&gt;
        &lt;input type=&quot;text&quot; name=&quot;title&quot;
              placeholder=&quot;Post title&quot; class=&quot;form-control&quot;
              value=&quot;{{ request.form['title'] }}&quot;&gt;&lt;/input&gt;
    &lt;/div&gt;
 
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;content&quot;&gt;Content&lt;/label&gt;
        &lt;textarea name=&quot;content&quot; placeholder=&quot;Post content&quot;
                  class=&quot;form-control&quot;&gt;{{ request.form['content'] }}&lt;/textarea&gt;
    &lt;/div&gt;
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;/button&gt;
    &lt;/div&gt;
&lt;/form&gt;
{% endblock %}</pre>
Most of this code is standard HTML. It will display an input box for the post title, a text area for the post content, and a button to submit the form.
 
The value of the post title input is <code>{{ request.form['title'] }}</code> and the text area has the value <code>{{ request.form['content'] }}</code>, this is done so that the data you enter does not get lost if something goes wrong. For example, if you write a long post and you forget to give it a title, a message will be displayed informing you that the title is required. This will happen without losing the post you wrote since it will be stored in the <code>request</code> global object that you have access to in your templates.
 
Now, with the development server running, use your browser to navigate to the <code>/create</code> route:
 
<pre class="code-pre">http://127.0.0.1:5000/create</pre>
You will see a '''Create a New Post''' page with a box for a title and content.
 
[[File:https://assets.digitalocean.com/articles/webflaskapp/step7a.png|Create a New Post Page]]
 
This form submits a POST request to your <code>create()</code> view function. However, there is no code to handle a POST request in the function yet, so nothing happens after filling in the form and submitting it.
 
You’ll handle the incoming POST request when a form is submitted. You’ll do this inside the <code>create()</code> view function. You can separately handle the POST request by checking the value of <code>request.method</code>. When its value is set to <code>'POST'</code> it means the request is a POST request, you’ll then proceed to extract submitted data, validate it, and insert it into your database.
 
Open the <code>app.py</code> file for editing:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
Modify the <code>create()</code> view function to look exactly as follows:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
@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')</pre>
In the <code>if</code> statement you ensure that the code following it is only executed when the request is a POST request via the comparison <code>request.method == 'POST'</code>.
 
You then extract the submitted title and content from the <code>request.form</code> object that gives you access to the form data in the request. If the title is not provided, the condition <code>if not title</code> would be fulfilled, displaying a message to the user informing them that the title is required. If, on the other hand, the title is provided, you open a connection with the <code>get_db_connection()</code> function and insert the title and the content you received into the <code>posts</code> table.
 
You then commit the changes to the database and close the connection. After adding the blog post to the database, you redirect the client to the index page using the <code>redirect()</code> function passing it the URL generated by the <code>url_for()</code> function with the value <code>'index'</code> as an argument.
 
Save and close the file.
 
Now, navigate to the <code>/create</code> route using your web browser:
 
<pre class="code-pre">http://127.0.0.1:5000/create</pre>
Fill in the form with a title of your choice and some content. Once you submit the form, you will see the new post listed on the index page.
 
Lastly, you’ll display flashed messages and add a link to the navigation bar in the <code>base.html</code> template to have easy access to this new page. Open the template file:
 
<pre class="code-pre custom_prefix prefixed">nano templates/base.html</pre>
Edit the file by adding a new <code>&lt;li&gt;</code> tag following the <code>About</code> link inside the <code>&lt;nav&gt;</code> tag. Then add a new <code>for</code> loop directly above the <code>content</code> block to display the flashed messages below the navigation bar. These messages are available in the special <code>get_flashed_messages()</code> function Flask provides:
 
<div class="code-label" title="flask_blog/templates/base.html">
 
flask_blog/templates/base.html
 
</div>
<pre class="code-pre">&lt;nav class=&quot;navbar navbar-expand-md navbar-light bg-light&quot;&gt;
    &lt;a class=&quot;navbar-brand&quot; href=&quot;{{ url_for('index')}}&quot;&gt;FlaskBlog&lt;/a&gt;
    &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
        &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
        &lt;ul class=&quot;navbar-nav&quot;&gt;
        &lt;li class=&quot;nav-item&quot;&gt;
            &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;
        &lt;/li&gt;
        &lt;li class=&quot;nav-item&quot;&gt;
            &lt;a class=&quot;nav-link&quot; href=&quot;{{url_for('create')}}&quot;&gt;New Post&lt;/a&gt;
        &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/nav&gt;
&lt;div class=&quot;container&quot;&gt;
    {% for message in get_flashed_messages() %}
        &lt;div class=&quot;alert alert-danger&quot;&gt;{{ message }}&lt;/div&gt;
    {% endfor %}
    {% block content %} {% endblock %}
&lt;/div&gt;</pre>
Save and close the file. The navigation bar will now have a <code>New Post</code> item that links to the <code>/create</code> route.
 
=== Editing a Post ===
 
For a blog to be up to date, you’ll need to be able to edit your existing posts. This section will guide you through creating a new page in your application to simplify the process of editing a post.
 
First, you’ll add a new route to the <code>app.py</code> file. Its view function will receive the ID of the post that needs to be edited, the URL will be in the format <code>/post_id/edit</code> with the <code>post_id</code> variable being the ID of the post. Open the <code>app.py</code> file for editing:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
Next, add the following <code>edit()</code> view function at the end of the file. Editing an existing post is similar to creating a new one, so this view function will be similar to the <code>create()</code> view function:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre">. . .
 
@app.route('/&lt;int:id&gt;/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)</pre>
The post you edit is determined by the URL and Flask will pass the ID number to the <code>edit()</code> function via the <code>id</code> argument. You add this value to the <code>get_post()</code> function to fetch the post associated with the provided ID from the database. The new data will come in a POST request, which is handled inside the <code>if request.method == 'POST'</code> condition.
 
Just like when you create a new post, you first extract the data from the <code>request.form</code> object then flash a message if the title has an empty value, otherwise, you open a database connection. Then you update the <code>posts</code> table by setting a new title and new content where the ID of the post in the database is equal to the ID that was in the URL.
 
In the case of a GET request, you render an <code>edit.html</code> template passing in the <code>post</code> variable that holds the returned value of the <code>get_post()</code> function. You’ll use this to display the existing title and content on the edit page.
 
Save and close the file, then create a new <code>edit.html</code> template:
 
<pre class="code-pre custom_prefix prefixed">nano templates/edit.html</pre>
Write the following code inside this new file:
 
<div class="code-label" title="flask_blog/templates/edit.html">
 
flask_blog/templates/edit.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
&lt;h1&gt;{% block title %} Edit &quot;{{ post['title'] }}&quot; {% endblock %}&lt;/h1&gt;
 
&lt;form method=&quot;post&quot;&gt;
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;title&quot;&gt;Title&lt;/label&gt;
        &lt;input type=&quot;text&quot; name=&quot;title&quot; placeholder=&quot;Post title&quot;
              class=&quot;form-control&quot;
              value=&quot;{{ request.form['title'] or post['title'] }}&quot;&gt;
        &lt;/input&gt;
    &lt;/div&gt;
 
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;content&quot;&gt;Content&lt;/label&gt;
        &lt;textarea name=&quot;content&quot; placeholder=&quot;Post content&quot;
                  class=&quot;form-control&quot;&gt;{{ request.form['content'] or post['content'] }}&lt;/textarea&gt;
    &lt;/div&gt;
    &lt;div class=&quot;form-group&quot;&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;/button&gt;
    &lt;/div&gt;
&lt;/form&gt;
&lt;hr&gt;
{% endblock %}</pre>
Save and close the file.
 
This code follows the same pattern except for the <code>{{ request.form['title'] or post['title'] }}</code> and <code>{{ request.form['content'] or post['content'] }}</code> syntax. This displays the data stored in the request if it exists, otherwise it displays the data from the <code>post</code> variable that was passed to the template containing current database data.
 
Now, navigate to the following URL to edit the first post:
 
<pre class="code-pre">http://127.0.0.1:5000/1/edit</pre>
You will see an '''Edit “First Post”''' page.
 
[[File:https://assets.digitalocean.com/articles/webflaskapp/step7b.png|Edit a Post Page]]
 
Edit the post and submit the form, then make sure the post was updated.
 
You now need to add a link that points to the edit page for each post on the index page. Open the <code>index.html</code> template file:
 
<pre class="code-pre custom_prefix prefixed">nano templates/index.html</pre>
Edit the file to look exactly like the following:
 
<div class="code-label" title="flask_blog/templates/index.html">
 
flask_blog/templates/index.html
 
</div>
<pre class="code-pre">{% extends 'base.html' %}
 
{% block content %}
    &lt;h1&gt;{% block title %} Welcome to FlaskBlog {% endblock %}&lt;/h1&gt;
    {% for post in posts %}
        &lt;a href=&quot;{{ url_for('post', post_id=post['id']) }}&quot;&gt;
            &lt;h2&gt;{{ post['title'] }}&lt;/h2&gt;
        &lt;/a&gt;
        &lt;span class=&quot;badge badge-primary&quot;&gt;{{ post['created'] }}&lt;/span&gt;
        &lt;a href=&quot;{{ url_for('edit', id=post['id']) }}&quot;&gt;
            &lt;span class=&quot;badge badge-warning&quot;&gt;Edit&lt;/span&gt;
        &lt;/a&gt;
        &lt;hr&gt;
    {% endfor %}
{% endblock %}</pre>
Save and close the file.
 
Here you add an <code>&lt;a&gt;</code> tag to link to the <code>edit()</code> view function, passing in the <code>post['id']</code> value to link to the edit page of each post with the <code>Edit</code> link.
 
=== Deleting a Post ===
 
Sometimes a post no longer needs to be publicly available, which is why the functionality of deleting a post is crucial. In this step you will add the delete functionality to your application.
 
First, you’ll add a new <code>/ID/delete</code> route that accepts POST requests, similar to the <code>edit()</code> view function. Your new <code>delete()</code> view function will receive the ID of the post to be deleted from the URL. Open the <code>app.py</code> file:
 
<pre class="code-pre custom_prefix prefixed">nano app.py</pre>
Add the following view function at the bottom of the file:
 
<div class="code-label" title="flask_blog/app.py">
 
flask_blog/app.py
 
</div>
<pre class="code-pre"># ....
 
@app.route('/&lt;int:id&gt;/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('&quot;{}&quot; was successfully deleted!'.format(post['title']))
    return redirect(url_for('index'))</pre>
This view function only accepts POST requests. This means that navigating to the <code>/ID/delete</code> route on your browser will return an error because web browsers default to GET requests.
 
However you can access this route via a form that sends a POST request passing in the ID of the post you want to delete. The function will receive the ID value and use it to get the post from the database with the <code>get_post()</code> function.
 
Then you open a database connection and execute a <code>DELETE FROM</code> SQL command to delete the post. You commit the change to the database and close the connection while flashing a message to inform the user that the post was successfully deleted and redirect them to the index page.
 
Note that you don’t render a template file, this is because you’ll just add a <code>Delete</code> button to the edit page.
 
Open the <code>edit.html</code> template file:
 
<pre class="code-pre custom_prefix prefixed">nano templates/edit.html</pre>
Then add the following <code>&lt;form&gt;</code> tag after the <code>&lt;hr&gt;</code> tag and directly before the <code>{% endblock %}</code> line:
 
<div class="code-label" title="flask_blog/templates/edit.html">
 
flask_blog/templates/edit.html
 
</div>
<pre class="code-pre">&lt;hr&gt;
 
&lt;form action=&quot;{{ url_for('delete', id=post['id']) }}&quot; method=&quot;POST&quot;&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Delete Post&quot;
            class=&quot;btn btn-danger btn-sm&quot;
            onclick=&quot;return confirm('Are you sure you want to delete this post?')&quot;&gt;
&lt;/form&gt;
 
{% endblock %}</pre>
You use the [https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm <code>confirm()</code>] method to display a confirmation message before submitting the request.
 
Now navigate again to the edit page of a blog post and try deleting it:
 
<pre class="code-pre">http://127.0.0.1:5000/1/edit</pre>
At the end of this step, the source code of your project will look like [https://github.com/do-community/flask_blog the code on this page].
 
With this, the users of your application can now write new blog posts and add them to the database, edit, and delete existing posts.
 
== Conclusion ==
 
This tutorial introduced essential concepts of the Flask Python framework. You learned how to make a small web application, run it in a development server, and allow the user to provide custom data via URL parameters and web forms. You also used the [http://jinja.palletsprojects.com/ Jinja template engine] to reuse HTML files and use logic in them. At the end of this tutorial, you now have a fully functioning web blog that interacts with an [https://sqlite.org/ SQLite database] to create, display, edit, and delete blog posts using the Python language and SQL queries. If you would like to learn more about working with Flask and SQLite check out this tutorial on [https://www.digitalocean.com/community/tutorials/how-to-use-one-to-many-database-relationships-with-flask-and-sqlite How To Use One-to-Many Database Relationships with Flask and SQLite].
 
You can further develop this application by adding user authentication so that only registered users can create and modify blog posts, you may also add comments and tags for each blog post, and add file uploads to give users the ability to include images in the post. See the [https://flask.palletsprojects.com/en/1.1.x/ Flask documentation] for more information.
 
Flask has many community-made [http://flask.palletsprojects.com/en/1.1.x/extensions/ Flask extensions]. The following is a list of extensions you might consider using to make your development process easier:
 
* [https://flask-login.readthedocs.io/en/latest/ Flask-Login]: manages the user session and handles logging in and logging out and remembering logged-in users.
* [https://flask-sqlalchemy.palletsprojects.com/en/2.x/ Flask-SQLAlchemy]: simplifies using Flask with [https://www.sqlalchemy.org/ SQLAlchemy], a Python SQL toolkit and Object Relational Mapper for interacting with SQL databases.
* [https://pythonhosted.org/Flask-Mail/ Flask-Mail]: helps with the task of sending email messages in your Flask application.

Revision as of 14:05, 17 November 2021

Flask is a micro web framework written in Python, for making web applications.

A good tutorial is provided by Abdelhadi Dyouri on the website digitalocean.com:

https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3

With this tutorial, you can make a simple flask blog web application, with ways to post, edit and delete content. Future augmentation could also include adding secure features. At the end of the tutorial are links to ways the app can be extended.