django를 사용하여 뉴스 앱 빌드

이 기사에서는 django, 요청, 부트스트랩을 사용하고 NewsApi의 뉴스 피드를 사용하여 간단한 뉴스 앱을 빌드합니다.

이 앱은 Google News에서 영감을 받았습니다.

프로젝트에 대한 종속성 설치

   pip install django
   pip install requests


django 프로젝트 설정



django에 대한 자세한 내용은 페이지를 참조하십시오.
이제 프로젝트를 만들 수 있습니다.

   django-admin startproject newsapp
   cd newsapp


프로젝트가 작동하는지 확인하려면 다음 명령을 실행하십시오.

   python manage.py runserver




프로젝트 생성이 성공했음을 의미하는 로켓 페이지가 표시될 것입니다. 이 명령을 실행하면 제거하라는 경고가 미적용 마이그레이션과 같은 터미널에 표시되더라도 말입니다.
이 명령은 기본 마이그레이션을 세션, 사용자 테이블과 같은 데이터베이스로 이동합니다.

  python manage.py migration


이 디렉터리에서 manage.py 파일과 newsapp 디렉터리를 찾을 수 있습니다. 그런 다음 앱 호출 리더를 만듭니다.

   python manage.py startapp reader


이제 setting.py에 앱 리더를 추가해야 합니다.
이것은 django가 레코드 앱을 식별하는 데 도움이 됩니다.

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


https://newsapi.org/ 에서 API 키를 받는 것을 잊지 마세요.
그런 다음 setting.py에 api 키를 추가하십시오.

APIKEY = "XXXXXXXXXXXXXXXXXXX"


보기 및 URL
우리가 논리를 쓸 곳

reader 디렉토리 내에 urls.py를 생성한 후 reader/urls.py 파일을 newsapp/urls.py에 연결합니다.
이것은 모든 URL의 마스터 역할을 할 것입니다.

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



이 API는 모든 출처에서 최고의 뉴스를 얻을 것입니다.

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


데이터를 얻기 위해 사용자 ** 파이썬 요청 **

   r = requests.get(url=url) 
   data = r.json()


데이터는 다음과 같습니다

{
"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..."
}]}




콘텐츠를 표시하는 보기를 만들 수 있습니다.


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)


위의 홈 보기는 트렌드 뉴스 또는 요청된 검색어를 반환한 다음 템플릿에 전달하여/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>


그런 다음/reader/template/sidebar.html 안에 다른 템플릿 파일을 만듭니다.
Get 메서드에서 쿼리 매개변수를 보내는 django 방법

<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>


홈 보기 기능에서 html 페이지를 렌더링하고 사용자가 페이지를 스크롤하는 동안 뉴스를 업데이트하기 위해 추가 API가 필요하므로 전체 페이지를 로드하지 않고도 웹을 더 다루기 어렵게 만듭니다.
ajax 요청을 사용하여 이 loadcontent api에서 json 값을 가져옵니다.

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})


뷰와 템플릿을 만들었고 액세스하려면 URL을 제공해야 합니다.

from django.urls import path
from reader import views

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


이 API를 사용하여 Json 데이터를 가져오고 그에 따라 쿼리 매개변수를 보낼 수 있습니다.

   path('next', views.loadcontent, name="Loadcontent"),


이제 앱을 실행하면 볼 수 있습니다.

마침내 우리는 해냈다


프로젝트 구조입니다


감사합니다. 좋은 하루 되세요.🤪😎

좋은 웹페이지 즐겨찾기