DEV Community

loading...
Cover image for Build a News app using django

Build a News app using django

magesh236 profile image Mageshwaran ãƒŧ6 min read

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 user ** python requests **

   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, you will see
Alt Text
Finally we made it
Alt Text

This is the project structure
Alt Text

Thanks you, have a great day ahead.ðŸĪŠðŸ˜Ž

Discussion (4)

pic
Editor guide
Collapse
kirmir78 profile image
KirMir78

Hi! Can i found this on GitHub, dude?

Collapse
magesh236 profile image
Collapse
kirmir78 profile image
KirMir78

Thank you man))

Collapse
magesh236 profile image
Mageshwaran Author

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