Create Your First Web Application With Flask

Create Your First Web Application With Flask

Introduction

In the last part, we had seen a simple Hello World application using Flask. In this part, we'll continue with the same codebase and create a News Application. We'll make use of Templates and Static files to make the application look attractive. For the news, we'll be using News API. So, let's dive in!

Templates

Templates are the HTML files that we'll use to serve HTML pages on the application. As we have already discussed in the previous blog, these files will exist in a templates directory inside the core package. So, let's create a templates directory inside the core package. Also, let's create an index.html file inside the templates directory.

$ mkdir core/templates
$ touch core/templates/index.html

Now that, we'll be using HTML files for our application, why don't we use Bootstrap for this? Bootstrap helps us quickly design and customize responsive mobile-first sites. With that being said, head over to the starter template given by Bootstrap and copy and paste it inside the index.html file:

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- Bootstrap CSS -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />

    <title>Hello, {{ user.name }}!</title>
  </head>
  <body>
    <h1>Hello, {{ user.name }}!</h1>

    <!-- Bootstrap Bundle with Popper -->
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
      crossorigin="anonymous"
    ></script>
  </body>
</html>

This is a simple starter template provided by Bootstrap. It includes CSS and Javascript given by Bootstrap. If you open this, it will just render a message. Instead of the Hello World message, we want to make it customized. So, we have used Hello, {{ user.name }} which makes use of the Jinja2 templating engine. Now, let's see how we can serve this page using Flask.

from core import app
from flask import render_template # Add this line

@app.route('/')
def say_hello():
    user = {"name": "Ashutosh"}
    return render_template("index.html", user=user) # Modify this line

To render an HTML page from our flask application, we make use of the render_template() method provided by the flask itself. This method takes a template filename and a variable list of template arguments and returns the same template, but with all the placeholders in it replaced with actual values. The render_template() method invokes the Jinja2 template engine that comes bundled with the Flask framework. Jinja2 substitutes {{ ... }} blocks with the corresponding values, given by the arguments provided in the render_template() call.

Now, let's run the server and see what the output looks like:

Did you see, whatever name we passed in the user dictionary, we have the same name in the output. This is how it works.

Static Files

Now, let us see how we can serve static files in the HTML templates. Static files include CSS, Javascript, Images, Fonts, etc. Since we already have CSS and Javascript served by Bootstrap, let us serve an image. For that create a static directory inside the core package, inside that directory, create an images folder.

$ mkdir core/static
$ mkdir core/static/images

You can put any image inside the images folder. I am using a demo.jpg image, for example. This is how we can use static files in our HTML templates:

<img src="{{ url_for('static', filename='images/demo.jpg') }}" alt="" />

Flask provides a url_for function that generates URLs dynamically. We are referencing the images/demo.jpg file inside the static folder. Let's put this line below the Hello message. Here's what our output will look like:

News API

Now, let us jump to the main part of the application - how we will get the news headlines. For this purpose, we are going to use News API. Signup for a free account and get an API Key from News API. Once you get the API Key, create a .env file at the top-level directory and store your API Key there as:

NEWS_API_KEY=YOUR-API-KEY-HERE

You can learn more about environment variables here. To use environment variables, we are using an external library called python-decouple. You can install it as:

$ pip install python-decouple

Once installed, create a utils.py file inside the core package. Inside this file, we're going to fetch the latest news headlines. News API has a Python library called newsapi-python. But instead of using that, we will be using the requests library. You need to install it as:

$ pip install requests

You can learn how to work with APIs using Python in this blog.

Now, that we have everything ready, let us fetch the latest news headlines in the utils.py file. Open the utils.py file and add the following code there:

import requests
from decouple import config

NEWS_API_KEY = config('NEWS_API_KEY')
COUNTRY = 'in'

def get_latest_news():
    news_data = requests.get(f'https://newsapi.org/v2/top-headlines?country={COUNTRY}&apiKey={NEWS_API_KEY}').json()
    return news_data['articles']

In the above script, we are having two imports - requests to get the news and config to get values of environment variables stored in .env file. We are then storing the value of API Key inside a variable NEWS_API_KEY. We are also setting the COUNTRY to in , i.e., India. You can find the country code of various countries in the News API documentation. The next thing we are doing is creating a get_latest_news() function. Inside that, we are making a GET request to the URL provided by News API here with the country and API Key parameters. We are converting the response into JSON. It will look something like this:

{
  "status": "ok",
  "totalResults": 38,
  "articles": [
    {
      "source": {
        "id": null,
        "name": "Sportskeeda"
      },
      "author": "Aniket Thakkar",
      "title": "Latest Free Fire redeem code to get Weapon loot crate today (14 October 2021) - Sportskeeda",
      "description": "Gun crates are one of the ways that players in Free Fire can obtain impressive and appealing gun skins.",
      "url": "https://www.sportskeeda.com/free-fire/latest-free-fire-redeem-code-get-weapon-loot-crate-today-14-october-2021",
      "urlToImage": "https://staticg.sportskeeda.com/editor/2021/10/d0b83-16341799119781-1920.jpg",
      "publishedAt": "2021-10-14T03:51:50Z",
      "content": null
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "BSF Gets Increased Powers In 3 Border States: What It Means - NDTV",
      "description": "Border Security Force (BSF) officers will now have the power toarrest, search, and of seizure to the extent of 50 km inside three newstates sharing international boundaries with Pakistan and Bangladesh.",
      "url": "https://www.ndtv.com/india-news/bsf-gets-increased-powers-in-3-border-states-what-it-means-2574644",
      "urlToImage": "https://c.ndtvimg.com/2021-08/eglno7qk_-bsf-recruitment-2021_625x300_10_August_21.jpg",
      "publishedAt": "2021-10-14T03:44:00Z",
      "content": "This move is quickly snowballing into a debate on state autonomy. New Delhi: Border Security Force (BSF) officers will now have the power to arrest, search, and of seizure to the extent of 50 km ins… [+4143 chars]"
    },
    {
      "source": {
        "id": "the-times-of-india",
        "name": "The Times of India"
      },
      "author": "TIMESOFINDIA.COM",
      "title": "5 health conditions that can make your joints hurt - Times of India",
      "description": "Joint pain caused by these everyday issues generally goes away on its own when you stretch yourself a little and flex your muscles.",
      "url": "https://timesofindia.indiatimes.com/life-style/health-fitness/health-news/5-health-conditions-that-can-make-your-joints-hurt/photostory/86994969.cms",
      "urlToImage": "https://static.toiimg.com/photo/86995017.cms",
      "publishedAt": "2021-10-14T03:30:00Z",
      "content": "Depression is a mental health condition, but the symptoms may manifest even on your physical health. Unexpected aches and pain in the joints that you may experience when suffering from chronic depres… [+373 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Devendra Pandey",
      "title": "Rahul Dravid likely to be interim coach for New Zealand series - The Indian Express",
      "description": "It’s learnt that a few Australian coaches expressed interest in the job, but the BCCI isn’t keen as they are focussing on an Indian for the role, before they look elsewhere.",
      "url": "https://indianexpress.com/article/sports/cricket/rahul-dravid-likely-to-be-interim-coach-for-new-zealand-series-7570990/",
      "urlToImage": "https://images.indianexpress.com/2021/05/rahul-dravid.jpg",
      "publishedAt": "2021-10-14T03:26:09Z",
      "content": "Rahul Dravid is likely to be approached by the Indian cricket board to be the interim coach for Indias home series against New Zealand. Head coach Ravi Shastri and the core of the support staff will … [+1972 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "CNBCTV18"
      },
      "author": null,
      "title": "Thursday's top brokerage calls: Infosys, Wipro and more - CNBCTV18",
      "description": "Goldman Sachs has maintained its 'sell' rating on Mindtree largely due to expensive valuations, while UBS expects a muted reaction from Wipro's stock. Here are the top brokerage calls for the day:",
      "url": "https://www.cnbctv18.com/market/stocks/thursdays-top-brokerage-calls-infosys-wipro-and-more-11101072.htm",
      "urlToImage": "https://images.cnbctv18.com/wp-content/uploads/2019/03/buy-sell.jpg",
      "publishedAt": "2021-10-14T03:26:03Z",
      "content": "MiniGoldman Sachs has maintained its 'sell' rating on Mindtree largely due to expensive valuations, while UBS expects a muted reaction from Wipro's stock. Here are the top brokerage calls for the day:"
    }
  ]
}

Since the news is contained in a list called articles, we are returning news_data['articles'] from the function. Now we are ready to use this function in our routes.

Designing News Page

We're going to design the news page using Bootstrap only. So, create a news.html file inside the templates directory and add the following code there:

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- Bootstrap CSS -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />

    <title>Latest News Headlines</title>
  </head>
  <body>
    <h1 class="text-center mt-4 mb-5">Latest News Headlines</h1>
    <div class="row mt-5">
      <div class="col-md-2"></div>
      <div class="col-md-8">
        <div class="row">
          {% for news in news_articles %}
          <div class="col-md-4 mb-5">
            <div class="card" style="width: 18rem">
              <img src="{{ news.urlToImage }}" class="card-img-top" alt="..." />
              <div class="card-body">
                <h5 class="card-title">{{ news.title }}</h5>
                <p class="card-text">{{ news.description }}</p>
                <a href="{{ news.url }}" class="btn btn-primary">Read More</a>
              </div>
            </div>
          </div>
          {% endfor %}
        </div>
      </div>
      <div class="col-md-2"></div>
    </div>

    <!-- Bootstrap Bundle with Popper -->
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
      crossorigin="anonymous"
    ></script>
  </body>
</html>

The code would look familiar to you now. In the body of the template, we have added a heading. Below that we have used Jinja2 for loop to iterate over the news_articles list passed by our routes. We have used Bootstrap cards for each news. It contains an image , the news title , its description and a button to read the complete news. If you take a look at the sample response given above, you will find title, description, URL and urlToImage in the response.

Creating Route for News page

Now, open the routes.py file and create a route "/news" for the news page.

from core import app
from flask import render_template
from .utils import get_latest_news # Add this line

@app.route('/')
def say_hello():
    user = {"name": "Ashutosh"}
    return render_template("index.html", user=user)

## Add this part
@app.route('/news')
def news_headlines():
    news_articles = get_latest_news()
    return render_template("news.html", news_articles=news_articles)

In the above script, we have imported get_latest_news() function from utils.py file. We have then created a view function news_headlines() mapped with the URL "/news" using the @app.route decorator. Inside the function, we have got the data from get_latest_news() function in a news_articles variable which is nothing but a list. We have then rendered our news.html file and passed the news_articles list.

Now we are ready to run the server and see the output:

Doesn't that look amazing? We just created our first news application using Flask.

Conclusion

In this blog, we have created a News application. Go through the News API Documentation and explore some more API endpoints. You can make the page look more attractive by using custom CSS. Hope you liked the tutorial. You can find the entire application code here: https://github.com/ashutoshkrris/Flask-News-Application

Did you find this article valuable?

Support Ashutosh Writes by becoming a sponsor. Any amount is appreciated!