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
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
To check the project is working, run this command
python manage.py runserver
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
In this directory you can find manage.py file and newsapp directory, after this create a app call reader
python manage.py startapp reader
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',
]
Don't forget to get the Api key from https://newsapi.org/ .
Then add the api key in setting.py
APIKEY = "XXXXXXXXXXXXXXXXXXX"
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")),
]
NewsApi
This api will get the top news from all the sources.
url = https://newsapi.org/v2/top-headlines?country=us&apiKey=XXXXXXXXXXXXXXXXXXX
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()
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..."
}]}
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)
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 }}";
<!-- this will listen when scrolling -->
window.addEventListener('scroll', function(e) {
console.log("Hi Dude...");
}
<!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>
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>
<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>
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})
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"),
]
You can use this api only to get the Json data, send the query params accordingly
path('next', views.loadcontent, name="Loadcontent"),
Now run the app
Finally we made it
Thanks you, have a great day ahead.🤪😎
Top comments (10)
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
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.
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?
Check this Api newsapi.org/v2/top-headlines?count...
Now they provide python client, you can you this too
newsapi.org/docs/client-libraries/...
Hi! Can i found this on GitHub, dude?
@kirmir78 check this
github.com/Mageshwaran2314/newsapp/
Thank you man))
Nope, will update it and I'll share it to you
In views.py, could you explain what is the purpose of the code from lines #48 onwards?
Thank You.
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.