DEV Community

Cover image for Build a News app using django
Mageshwaran
Mageshwaran

Posted on • Updated on

Build a News app using django

In this article we will build a simple news app using django, requests, bootstrap and with the help of the news feed from NewsApi

This app is an inspiration from Google News

Installing the dependency for the project

   pip install django
   pip install requests
Enter fullscreen mode Exit fullscreen mode

Setting up the django project

for detailed reference about django visit the page
Now we can create our project

   django-admin startproject newsapp
   cd newsapp
Enter fullscreen mode Exit fullscreen mode

To check the project is working, run this command

   python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Alt Text

You will see a rocket page that means project creation is success, even though you will see warnings is the terminal like unapplied migrations, to remove that run this command.
This command will move the default migration to database like session, user table

  python manage.py migration
Enter fullscreen mode Exit fullscreen mode

In this directory you can find manage.py file and newsapp directory, after this create a app call reader

   python manage.py startapp reader
Enter fullscreen mode Exit fullscreen mode

now we need to add the app reader in setting.py
this will helps django to identity the record app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'reader',
]
Enter fullscreen mode Exit fullscreen mode

Don't forget to get the Api key from https://newsapi.org/ .
Then add the api key in setting.py

APIKEY = "XXXXXXXXXXXXXXXXXXX"
Enter fullscreen mode Exit fullscreen mode

View and Urls
Where we will write the logic

create a urls.py inside reader directory, after that connect the reader/urls.py file to newsapp/urls.py,
this one will act as master all the urls

from django.contrib import admin
from django.urls import path
from django.conf.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include("reader.urls")),
]
Enter fullscreen mode Exit fullscreen mode

NewsApi

This api will get the top news from all the sources.

url = https://newsapi.org/v2/top-headlines?country=us&apiKey=XXXXXXXXXXXXXXXXXXX
Enter fullscreen mode Exit fullscreen mode

to get the data we are going to use python requests package to read data in python dictionaries

   r = requests.get(url=url) 
   data = r.json()
Enter fullscreen mode Exit fullscreen mode

data will look like this

{
"status": "ok",
"totalResults": 38,
-"articles": [
-{
-"source": {
"id": null,
"name": "CNET"
},
"author": "Amanda Kooser",
"title": "Ancient underground lakes discovered on Mars - CNET",
"description": "Mars could be home to more liquid water than we originally thought.",
"url": "https://www.cnet.com/news/ancient-underground-lakes-found-on-mars/",
"urlToImage": "https://cnet2.cbsistatic.com/img/OJOEhYPBRrJmmwv03JD_1RF2wPI=/1200x630/2015/02/09/3a560b3c-d9eb-4f0c-af5c-9e9671844c93/mars-ice-cap.jpg",
"publishedAt": "2020-09-29T16:14:00Z",
"content": "This beautiful..."
}]}
Enter fullscreen mode Exit fullscreen mode

Alt Text

let create a view to show the content


def home(request):
    page = request.GET.get('page', 1)
    search = request.GET.get('search', None)

    if search is None or search=="top":
        # get the top news
        url = "https://newsapi.org/v2/top-headlines?country={}&page={}&apiKey={}".format(
            "us",1,settings.APIKEY
        )
    else:
        # get the search query request
        url = "https://newsapi.org/v2/everything?q={}&sortBy={}&page={}&apiKey={}".format(
            search,"popularity",page,settings.APIKEY
        )
    r = requests.get(url=url)

    data = r.json()
    if data["status"] != "ok":
        return HttpResponse("<h1>Request Failed</h1>")
    data = data["articles"]
    context = {
        "success": True,
        "data": [],
        "search": search
    }
    # seprating the necessary data
    for i in data:
        context["data"].append({
            "title": i["title"],
            "description":  "" if i["description"] is None else i["description"],
            "url": i["url"],
            "image": temp_img if i["urlToImage"] is None else i["urlToImage"],
            "publishedat": i["publishedAt"]
        })
    # send the news feed to template in context
    return render(request, 'index.html', context=context)
Enter fullscreen mode Exit fullscreen mode

the above home view will return the trending news or the requested query and then, pass it to the template, to render it in the frontend for that we need to created a template file inside /reader/templates/index.html

<!-- to get the next page content -->
<!-- in each ajax success request page value gets incremented -->
var page = 2;
<!-- we are using infinity scrolling in this page to get data -->
<!-- to prevent the unnecessary call -->
var window_scroll = true;
<!-- to get the news related to the page on -->
var search = "{{ search }}";
Enter fullscreen mode Exit fullscreen mode
<!--  this will listen when scrolling -->
window.addEventListener('scroll', function(e) {
   console.log("Hi Dude...");
}
Enter fullscreen mode Exit fullscreen mode
<!DOCTYPE html>
<html lang="en">
<head>
  <title>News App</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>
<body>

    <div>
        {% include 'sidebar.html' %}
    </div>
    <div class="container" id="articles-container">
        {% for value in data %}
        <div class="card mb-3 box" style="max-width: 640px; margin:auto;">
            <div class="row">
                <div class="col-md-8">
                    <div class="card-body">
                        <h5 class="card-title"><a href="{{value.url}}" target="_blanck">{{value.title}}</a></h5>
                        <p class="card-text">{{value.description}}</p>
                        <p class="card-text"><small class="text-muted">{{value.publishedat}}</small></p>
                    </div>
                </div>

                <div class="col-md-4 img-box">
                    <img src="{{value.image}}" class="card-img" alt="..." height="100%">
                </div>
            </div>
        </div>
        {% endfor %}
    </div>
    <h1 id="loading">Loading....</h1>

</body>
<style>
#articles-container{
    margin-left: 250px;
}
.box{
    transition-property: box-shadow, transform;
    transition-duration: 1s;
}
.box:hover{
    /* box-shadow: 2px 2px 5px #8080807a; */
    box-shadow: 1px 2px 8px 0px #00bcffb0;
    transform: translateY(-9px);
}
.img-box{
    padding: 20px;
}
.img-box img{
    border-radius: 15px;
    object-fit: cover;
}
#loading{
    text-align: center;
    display: none;
}
</style>
<script>

$(document).ready(function() {
    console.log( "ready!" );
    el = document.getElementsByClassName("text-muted")
    for (i = 0; i < el.length; i++) {
        t = document.getElementsByClassName("text-muted")[i].innerText
        var d = new Date(t)
        document.getElementsByClassName("text-muted")[i].innerText = d.toDateString()
    }
});
var page = 2;
var window_scroll = true;
var search = "{{ search }}";

window.addEventListener('scroll', function(e) {
    // scroll check
    console.log("scroll check...")
    if(window_scroll){
        if((window.innerHeight + window.scrollY) >= (document.body.scrollHeight-200)){
            window_scroll = false;
            document.getElementById("loading").style.display = "block";
            $.ajax({
                url: '/next?page=' + page + '&search=' + search,
                dataType: 'json',
                success: function (data) {
                    if (data["success"]) {
                        articles = data["data"]
                        articles_html = ''
                        for (i = 0; i < articles.length; i++) {
                            articles_html += ' \
                            <div class="card mb-3 box" style="max-width: 640px; margin:auto;">\
                                <div class="row">\
                                    <div class="col-md-8">\
                                        <div class="card-body">\
                                            <h5 class="card-title"><a href="'+ articles[i]["url"] +'" target="_blanck">'+ articles[i]["title"] +'</a></h5>\
                                            <p class="card-text">'+ articles[i]["description"] +'</p>\
                                            <p class="card-text"><small class="text-muted">'+ articles[i]["publishedat"] +'</small></p>\
                                        </div>\
                                    </div>\
                                            \
                                    <div class="col-md-4 img-box">\
                                        <img src="'+ articles[i]["image"] +'" class="card-img" alt="..." height="100%">\
                                    </div>\
                                </div>\
                            </div>\
                            '
                        }
                        $("#articles-container").append(articles_html);
                        page += 1
                        window_scroll = true;
                        document.getElementById("loading").style.display = "none";
                    }
                    else {
                        console.log("Failed")
                        window_scroll = true;
                        document.getElementById("loading").style.display = "none";
                    }
                }
            });
        }
    }
})
</script>
</html>
Enter fullscreen mode Exit fullscreen mode

after this create a another template file inside /reader/template/sidebar.html

django way of send the query params in Get method

<a href="{% url 'Home' %}?search=Business">Business</a>
Enter fullscreen mode Exit fullscreen mode
<style>
    .sidebar{
        position: fixed;
        width: 200px;
        height: 100%;
        top: 0px;
        left: 0px;
    }
    .sidebar a{
        width: 250px;
        display: block;
        margin: 24px 0px;
        padding: 10px 5px;
        font-weight: bolder;
        font-size: large;
        text-decoration: none;
        background-color: #d9e4ffc7;
        border-top-right-radius: 30px;
        border-bottom-right-radius: 30px;
        box-shadow: 0px 0.5px 1px grey;
        text-align: center;
    }
</style>

<div class="sidebar">
    <a href="{% url 'Home' %}">Home</a>
    <a href="{% url 'Home' %}?search=top">Top News</a>

    <a href="{% url 'Home' %}?search=Business">Business</a>
    <a href="{% url 'Home' %}?search=Technology">Technology</a>
    <a href="{% url 'Home' %}?search=Entertainment">Entertainment</a>
    <a href="{% url 'Home' %}?search=Sports">Sports</a>
    <a href="{% url 'Home' %}?search=Science">Science</a>
    <a href="{% url 'Home' %}?search=Health">Health</a>
</div>
Enter fullscreen mode Exit fullscreen mode

In the home view function it render the html page, it also require an additional api to update the news while the user scrolls the page, this will make the web more intractable without loading the entire page.
Use ajax request to get the json value from this loadcontent api

def loadcontent(request):
    try:
        page = request.GET.get('page', 1)
        search = request.GET.get('search', None)
        # url = "https://newsapi.org/v2/everything?q={}&sortBy={}&page={}&apiKey={}".format(
        #     "Technology","popularity",page,settings.APIKEY
        # )
        if search is None or search=="top":
            url = "https://newsapi.org/v2/top-headlines?country={}&page={}&apiKey={}".format(
                "us",page,settings.APIKEY
            )
        else:
            url = "https://newsapi.org/v2/everything?q={}&sortBy={}&page={}&apiKey={}".format(
                search,"popularity",page,settings.APIKEY
            )
        print("url:",url)
        r = requests.get(url=url)

        data = r.json()
        if data["status"] != "ok":
            return JsonResponse({"success":False})
        data = data["articles"]
        context = {
            "success": True,
            "data": [],
            "search": search
        }
        for i in data:
            context["data"].append({
                "title": i["title"],
                "description":  "" if i["description"] is None else i["description"],
                "url": i["url"],
                "image": temp_img if i["urlToImage"] is None else i["urlToImage"],
                "publishedat": i["publishedAt"]
            })

        return JsonResponse(context)
    except Exception as e:
        return JsonResponse({"success":False})
Enter fullscreen mode Exit fullscreen mode

we made the view and templates, to access it we need to provide the urls

from django.urls import path
from reader import views

urlpatterns = [
    path('', views.home, name="Home"),
    path('next', views.loadcontent, name="Loadcontent"),
]
Enter fullscreen mode Exit fullscreen mode

You can use this api only to get the Json data, send the query params accordingly

   path('next', views.loadcontent, name="Loadcontent"),
Enter fullscreen mode Exit fullscreen mode

Now run the app
Finally we made it
Alt Text

This is the project structure
Alt Text

Thanks you, have a great day ahead.🤪😎

Top comments (10)

Collapse
 
dabjazz profile image
Yash_Jaiswal

You are using the concept of AJAX right in the frontend? I understood the working of the whole project except that java script code in index.html. Can you explain that on a high level. like what's happening with that

Collapse
 
max236 profile image
Mageshwaran

data in context holds the list of new data, django will populate in SSR.
On scrolling it will do a pagination to get the next news set.

Collapse
 
shiviroy profile image
Shivi roy

Hello sir! I am getting difficulty in fetching India's news in different categories. It's only coming in top headlines category.
Can you please help me in resolving this issue?

Collapse
 
max236 profile image
Mageshwaran

Check this Api newsapi.org/v2/top-headlines?count...

Now they provide python client, you can you this too
newsapi.org/docs/client-libraries/...

Collapse
 
kirmir78 profile image
KirMir78

Hi! Can i found this on GitHub, dude?

Collapse
 
max236 profile image
Mageshwaran
Collapse
 
kirmir78 profile image
KirMir78

Thank you man))

Collapse
 
max236 profile image
Mageshwaran

Nope, will update it and I'll share it to you

Collapse
 
pavankum profile image
Pavan-Kum

In views.py, could you explain what is the purpose of the code from lines #48 onwards?
Thank You.

Collapse
 
max236 profile image
Mageshwaran

it will load the next page content, when user scroll in browser it will call the api to fetch the next news list

Some comments may only be visible to logged-in visitors. Sign in to view all comments.