Written by Super User.

26- Requests Library and fetching data from external APIs

In this article, we are going to fetch movie details from an API and display the data on our template. To do that, first install requests library. Request library allows us to make a http request to get information from other web sites.

Make sure virtual environment is activated and then run

python -m pip install requests

 

In the simplest form, we can define a view to fetch data from an api like below. The fetch data is being converted to json. Then it can be rendered in a template.

APIs might need some other parameters as well. We can find additional info in its documentation.

#views.py
import requests
import json

def imdbapi(request):
    response = requests.get('https://api.covid19api.com/countries').json()
    return render(request, 'imdbapi.html', {'response': response})

#test.html template
    {%for r in response%}
        Country: {{r.Country}}<br>
    {%endfor%}

 

In this article, I will be using rapidAPI (https://rapidapi.com ). It offers more than 30,000 APIs and there are free APIs that we can use.

Create your free account first. Navigate MyApps > Add New App (name it whatever you like and save). Iname it as IMDB. This will create an appkey for you. You can see it under the security menu. 

Then click API Hub on the top menu and search for "Online Movie Database" > Pricing > Subscribe for the basic plan which is free > Click End Points. In this area we can query and test the API and we can create our view function accordingly. Change RapidAPI App to your App (IMDB in my case).

I changed the required parameters as  The Hobbit: An Unexpected Journey

 

On the left, you can find Code Snippets. Select you desired programming language (Python) and it will generate the code you need. Create a view function in views.py and modify the generated code accordingly.

views.py

import requests
import json

def imdbapi(request):
    url = "https://online-movie-database.p.rapidapi.com/auto-complete"
    querystring = {"q":"The Hobbit: An Unexpected Journey"}

    headers = {
        "X-RapidAPI-Key": "This is the KEY that rapidAPI gives you",
        "X-RapidAPI-Host": "online-movie-database.p.rapidapi.com"
    }

    response = requests.get(url, headers=headers, params=querystring).json()
    return render(request, 'imdbapi.html', {'response': response})

 

 Click "Test EndPoint button" and now this will query the API and shows the data on the right. By looking at this, I can prepare my template and fetch the data. 

 

 I created an url and test template named imdbapi.html

{% extends 'base.html' %}

{% block title %}
    <title>MoviesApp</title>
{% endblock title %}
{%block content%}
	    <h1>TEST</h1>
    {% for key in response.d %}
        {{key.id}} - {{key.l}}-{{key.y}}<br>
    {% endfor %}
{% endblock content %}

 

I can successfully fetch movie id, title and year data from API.

 

 

My goal is to fetch movie details such as id, title, image url, year, rating, genres and description. However the endpoint I used has no information about these fields. After checking the API endpoint I found that title/get-overviewdetails endpoint has what I need. So I change my view function and template like below:

views.py

def imdbapi(request):
    url = "https://online-movie-database.p.rapidapi.com/title/get-overview-details"
    querystring = {"tconst":"tt0112573","currentCountry":"US"}

    headers = {
        "X-RapidAPI-Key": "4784.....",
        "X-RapidAPI-Host": "online-movie-database.p.rapidapi.com"
    }
    response = requests.get(url, headers=headers, params=querystring).json()
    return render(request, 'imdbapi.html', {'response': response})

 

 imdbapi.html

{% extends 'base.html' %}

{% block title %}
    <title>MoviesApp</title>
{% endblock title %}
{%block content%}
	    <h1>TEST</h1>
        {{response.id}} <br> 
        {{response.title.title}} <br> 
        {{response.title.image.url}} <br>
        {{response.title.year}} <br>
        {{response.ratings.rating}} <br>
        {% for g in response.genres %}
            {{g}} -
        {% endfor %}<br>
        {{response.plotSummary.text}}


{% endblock content %}

 

And I fetched the data I need:

 

I want to display this data on watchmovie.html. To do that I need to modify the views.py and template.  For tconst key, I assigned the movieobj.imdbID's value.

views.py

def watchmovie(request, id):
    if request.method == 'POST':
        initialform = UserCommentForm(request.POST) #form has only the comment data at this point
        form = initialform.save(commit=False) #I will add additional fields(movie and user data), therefore I skip saving the form
        form.user = request.user #get the logged on user and add him to the form as the user
        form.movie = Movie.objects.get(pk=id) #get this movie that is passed with the request and add it to the form
        form.save() #Now save it
        messages.success(request, "Comment is Added", extra_tags='green')
        #I want to return to the same page after a POST request. But the path in urls.py expects url + id
        # Therefore, The easiest way to return to the same page is using HttpResponseRedirect subclass
        return HttpResponseRedirect(request.path_info)

    else:
        movieobj = Movie.objects.get(pk=id)
        actors = Actor.objects.filter(movies__title__exact= movieobj.title)
        movieobj.numberOfViews += 1
        movieobj.save()

        #This part is for which user watched which movie
        if request.user.is_authenticated:
            Watched.objects.create(user= request.user, movie=movieobj)

        initialform = UserCommentForm()
        #Get user comments from UserComment model where movie field on UserComment model matches the passed id in the request
        usercomments = UserComment.objects.filter(movie = id)

        #The below is for fetching API data
        url = "https://online-movie-database.p.rapidapi.com/title/get-overview-details"
        querystring = {"tconst":movieobj.imdbID,"currentCountry":"US"}
        headers = {
            "X-RapidAPI-Key": "4784........",
            "X-RapidAPI-Host": "online-movie-database.p.rapidapi.com"
        }
        response = requests.get(url, headers=headers, params=querystring).json()

    return render(request, 'watchmovie.html', {'movieobj': movieobj, 'actors':actors , 'usercomments': usercomments , 'form': initialform, 'response': response},)

 

 watchmovie.html

{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}
    <title>Watch Movie</title>
{% endblock title %}

{% block content %}
        <table class="table table-borderless">
            <thead class="thead-dark">
                <tr>
                    <th scope="col">{{movieobj.title}}</th>
                    <th scope="col">Movie Details</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                <td>
                    <video controls> 
                    <source src="/{{ MEDIA_URL }}{{movieobj.moviefile}}" type="video/mp4"> 
                    </video>
                </td>
                <td>
                    <img src={{response.title.image.url}} width="200 height="200"> <br>
                    <strong>IMDB ID:</strong> {{response.id}} <br> 
                    <strong>Title:</strong> {{response.title.title}} <br> 
                    <strong>Year:</strong> {{response.title.year}} <br>
                    <strong>IMDB Rating:</strong> {{response.ratings.rating}} <br>
                    <strong>Genres:</strong> {% for g in response.genres %}
                        {{g}} /
                    {% endfor %}<br>
                    <hr>
                    <strong>Description:</strong> {{response.plotSummary.text}}
                </td>
                </tr>
                <tr>
                <td>Viewed: {{movieobj.numberOfViews}} | 
                Duration:{{movieobj.duration}} min. | 
                Published: {{movieobj.publishDate}} | 
                Date Added: {{movieobj.date_added}} | 
                MA Score: {{movieobj.movieapp_score}} | 
                IMDB Score: {{movieobj.imdb_score}}
                </td>
                </tr>
                <tr><td>Stars: 
                {%for actor in actors%}
                | <a href="/{% url 'displayactormovies' actor.id %}"> {{actor.actorname}} </a> |
                {%endfor%}
                </td></tr>
                <tr>
                    <td>
                        <h4>Comments:</h4>
                        {% if user.is_authenticated %}
                        <form method="POST">
                        {% csrf_token %}
                        {{ form.comment | as_crispy_field }}
                        <input type="submit" value="Save" class="btn btn-secondary mb-2">
                        </form>
                        {%endif%}
                        {% for uc in usercomments%}
                            <tr><td>
                            {{uc.user}}:
                            {{uc.comment}}
                            </td></tr>
                        {%endfor%}
                    </td>
                </tr>
            </tbody>
        </table>
{{actorobj}}
{% endblock content%}

 

API data is fetched and displayed on my template:

 

 

Saving API data to your database:

I was not actually planning to do that but I will just save data from API to my own DB in case of I might need to use in the future. Alright, we know that response object is converted to json format and that means it is a dictionary (keys and values). If you remember some dictionaries in my example are nested dictionaries such as response.ratings.rating. To get the value of rating and saving that value to database, I first get the ratings dictionary from the response. Then I get rating value from ratings dictionary. Finally I can  save that value to my database. 

        response = requests.get(url, headers=headers, params=querystring).json()
        ratings = response['ratings']
        rating = ratings['rating']
        movieobj.movieapp_score = rating
        movieobj.save()

 

We can do the same thing with other fields, description for example:

        response = requests.get(url, headers=headers, params=querystring).json()
        plotSummary = response['plotSummary']
        text = plotSummary['text']
        movieobj.description = text
        messages.success(request, text, extra_tags='green')
        movieobj.save()

 

 I think we can conclude this subject here, thanks for reading.