Django Project: 5. News Home Page

I. Functional Requirement Analysis

1. function

  1. Rotation chart

  2. List of Recommended Articles

  3. Article Label Navigation

  4. Article list

  5. paging

2. Model Design

Based on the functional analysis, we need the following table

1. Table and field analysis

  1. Articles Category Table

  2. Article table

  3. Commentary Table of Articles

  4. List of Recommended Articles

  5. Wheel chart

2. Model Definition

Define a class model to extract common fields

# stay utils Under the directory, create a models.py File, in which a class model is defined
from django.db import models


class BaseModel(models.Model):
    """
    //Base class, common field
    """
    create_time = models.DateTimeField('Creation time', auto_now_add=True)
    update_time = models.DateTimeField('Update time', auto_now=True)
    is_delete = models.BooleanField('Logical deletion', default=False)

    class Meta:
        # Abstract class for inheritance, not created during migration
        abstract = True

Define other models

# stay news Directory models.py The following data model is defined in the file
from django.db import models
from utils.models import BaseModel


class Tag(BaseModel):
    """
    //Articles Classification Label Model
    """
    name = models.CharField('Tag name', max_length=64, help_text='Tag name')

    class Meta:
        ordering = ['-update_time', '-id']      # sort
        db_table = "tb_tag"                     # Specify the database table name
        verbose_name = "Article Tags"                # stay admin Names displayed on the site
        verbose_name_plural = verbose_name      # The plural name displayed

    def __str__(self):
        return self.name


class News(BaseModel):
    """
    //Article model
    """
    title = models.CharField('Title', max_length=150, help_text='Title')
    digest = models.CharField('abstract', max_length=200, help_text='abstract')
    content = models.TextField('content', help_text='content')
    clicks = models.IntegerField('Clicks', default=0, help_text='Clicks')
    image_url = models.URLField('picture url', default='', help_text='picture url')
    tag = models.ForeignKey('Tag', on_delete=models.SET_NULL, null=True)

    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)

    class Meta:
        ordering = ['-update_time', '-id']  # sort
        db_table = "tb_news"  # Specify the database table name
        verbose_name = "Journalism"  # stay admin Names displayed on the site
        verbose_name_plural = verbose_name  # The plural name displayed

    def __str__(self):
        return self.title


class Comments(BaseModel):
    """
    //Comment model
    """
    content = models.TextField('content', help_text='content')
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
    news = models.ForeignKey('News', on_delete=models.CASCADE)

    class Meta:
        ordering = ['-update_time', '-id']  # sort
        db_table = "tb_comments"  # Specify the database table name
        verbose_name = "comment"  # stay admin Names displayed on the site
        verbose_name_plural = verbose_name  # The plural name displayed

    def __str__(self):
        return '<comment{}>'.format(self.id)


class HotNews(BaseModel):
    """
    //List of Recommended Articles
    """
    news = models.OneToOneField('News', on_delete=models.CASCADE)
    priority = models.IntegerField('priority', help_text='priority')

    class Meta:
        ordering = ['-update_time', '-id']  # sort
        db_table = "tb_hotnews"  # Specify the database table name
        verbose_name = "Hot news"  # stay admin Names displayed on the site
        verbose_name_plural = verbose_name  # The plural name displayed

    def __str__(self):
        return '<Hot news{}>'.format(self.id)


class Banner(BaseModel):
    """
    //Rotation chart
    """
    image_url = models.URLField('Rotation chart url', help_text='Rotation chart url')
    priority = models.IntegerField('priority', help_text='priority')

    news = models.OneToOneField('News', on_delete=models.CASCADE)

    class Meta:
        ordering = ['priority', '-update_time', '-id']  # sort
        db_table = "tb_banner"  # Specify the database table name
        verbose_name = "Rotation chart"  # stay admin Names displayed on the site
        verbose_name_plural = verbose_name  # The plural name displayed

    def __str__(self):
        return '<Rotation chart{}>'.format(self.id)

3. Article Label Navigation Function

1. Interface design

  1. Interface Description:

    Category Explain
    Request method GET
    url definition /
    Parameter format No parameter
  2. Return result

    Return to the news page and render directly in the template

2. Backend code

# stay news/views.py The following views are defined in the file
from django.shortcuts import render

from .models import Tag


def index(request):
    """
    //News Home View
    :param request:
    :return:
    """

    tags = Tag.objects.only('id', 'name').filter(is_delete=False)
    return render(request, 'news/index.html',
                  context={
                      'tags': tags
                  })

Import tag test data, or add data directly to the tb_tag table with Navicat software (see the contents in the quotation marks below, totaling 6), because the reason of the previous section setting must be the same as my content, which will be used later.

# insert news tag data

INSERT INTO tb_tag(name, create_time, update_time, is_delete) values
('Python Basics', now(), now(), 0),
('Python senior', now(), now(), 0),
('Python function', now(), now(), 0),
('PythonGUI', now(), now(), 0),
('Linux Course', now(), now(), 0),
('Python frame', now(), now(), 0);

3. Front-end code

# Modify the news-nav code in templates/news/index.html, such as:
        <!--  news-nav start-->
          <nav class="news-nav">
              <ul class="clearfix">
                  <li class="active"><a href="javascript:void(0)">Latest information</a></li>
                    {% for tag in tags %}
                        <li><a href="javascript:void(0)" data-id="{{ tag.id }}">{{ tag.name }}</a>
                  </li>
                    {% endfor %}
              </ul>
          </nav>
        <!--  news-nav end -->

4. News List Function

1. Business process analysis

  1. Determine whether the front-end pass label classification id is empty, integer, and beyond the scope

  2. Determine whether the number of pages of the current article passed by the front end is empty, integer, or beyond the scope

2. Interface design

Interface Description:

Category Explain
Request method GET
url definition /news/
Parameter format Query parameters

Description of parameters:

Parameter name type Is it necessary? describe
tag integer yes Label classification id
page integer yes Current Page Number of Articles

Returns the result:

{
    "errno": "0", 
     "errmsg": "", 
    "data": {
        "total_pages": 61,
        "news": [
            {
                'id': 'xxx',
                "digest": "stay python use import perhaps from...import perhaps from...import...as...To import the corresponding modules, functions and usage methods and C Linguistic include Header files are similar. In fact, it is the introduction....",
                "title": "import Method Introducing Module Details",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python Basics",
                "update_time": "2018 December 17, 14:48"
            },
            {
                'id': 'xxx'
                "digest": "If you were one php Programmer, you are right about php Functions are well understood( PS: The stationmaster used to be one php Programmers), but now for work or other reasons to learn python,however p...",
                "title": "For once phper Programmers recommend a learning website",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python Basics",
                "update_time": "2018 December 17, 14:48"
            }
        ]
    }
}

3. Backend code

Create a media folder in the project root directory to store news photos and upload files.

# stay settings.py Add in the file
# Media file configuration
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

django provides static file service in debugging mode. In order to be able to return media files in media, the following configuration needs to be done in root urls.py

# At root urls.py Medium plus static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('', include('news.urls')),
    path('', include('verification.urls')),
    path('user/', include('user.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Import test data. In order to test the import of data, make sure that the table names are consistent.

# Import test data in the xshell, upload tb_news_20181217.sql file to the virtual machine through rz command in the xshell

Mysql-u username-p-D database name < tb_news_20181217.sql

Documents I have placed in my file bar (name is sql packet. rar After decompression, there are four files: tag table, news table, popular news table and broadcast chart.

 

View code

import logging

from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator
from django.db.models import F

from .models import Tag, News
from . import constants
from utils.json_res import json_response

logger = logging.getLogger('django')

def index(request):
    """
    //News Home View
    url: /
    :param request:
    :return:
    """

    tags = Tag.objects.only('id', 'name').filter(is_delete=False)
    return render(request, 'news/index.html',
                  context={
                      'tags': tags
                  })


class NewsListView(View):
    """
    //News List View
    url: /news/
    args: tag, page
    """
    def get(self, request):
        # 1.Get parameters
        try:
            tag_id = int(request.GET.get('tag', 0))
        except Exception as e:
            logger.error('Label error:\n{}'.format(e))
            tag_id = 0

        try:
            page = int(request.GET.get('page', 0))
        except Exception as e:
            logger.error('Page number error:\n{}'.format(e))
            page = 1
        # Use only The object is returned, so it needs to be processed iteratively when passed to the front end
        # news_queryset = News.objects.select_related('tag', 'author').only(
        #     'title', 'digest', 'image_url', 'update_time', 'tag__name', 'author__username')
        # 2.Get the query set (values Returns a dictionary.,only The object returned is the object)
        ############## important start #################
        # queryset:Inertia does not go to the database 
        # structure,Section,filter,Usually query sets do not really go to the database when passing
        # When,Go to the database.:
            # 1.iteration
            # 2.Section,As long as you don't jump to look it up, you won't look it up in the database.,Only when there is a slice of data in the middle will the database be queried.,When cutting out an element
            # 3.Care will be printed on the console
            # 4.Serialization Cache,Save the contents of the query set to redis,Memory
            # 5.Use len()Method Gets Length,count
            # 6.Use list()Method transformation type
            # 7.bool,To judge whether it is empty or not
        # QuerySet Caching
        # When Not Cached
            # Execute only part of the query set
            # Simple Printing without Changing Cars:Q = News.objects.all()        print(Q[1]) # Not cached
            
           
        ############## important end #################
        news_queryset = News.objects.values('id', 'title', 'digest', 'image_url', 'update_time').annotate(
            tag_name=F('tag__name'), author=F('author__username'))

        # 3.filter
        # if tag_id:
        #     news = news_queryset.fileter(is_delete=False, tag_id=tag_id)
        # else:
        #     news = news_queryset.fileter(is_delete=False)
        news = news_queryset.filter(is_delete=False, tag_id=tag_id) or news_queryset.filter(is_delete=False)
        # 4.paging
        paginator = Paginator(news, constants.PER_PAGE_NEWS_COUNT) # See the explanation below.
        # Get the current page data get_page Fault tolerant
        news_info = paginator.get_page(page)
        # 5.Return data
        data = {
            'total_pages': paginator.num_pages, # How many pages are divided?
            'news':list(news_info)
        }
        return json_response(data=data)

Paginator method:

Paginator.get_page (number) This method is a new 2.0 method.

Returns that Page has a given index object starting from 1, and also handles out-of-range and invalid page numbers. If the page number is not a number, it returns to the first page. If the page number is negative or larger, return to the last page.

# 2.Paging parsing
        paginator = Paginator(docs, 5) #  Page every five copies of content
        # 2.2 Get the previous paragraph and send it. page The value of this query parameter
        page = paginator.get_page(request.GET.get('page',1))
        # request.GET.get('page',1) Obtain url Parameters, 127.0.0.1:8000/?page=<value>
        # Illegal values return 1 to be empty and 1 to be 127..0.0.1:8000/?page=asdsa
        # Get current(Page number)The required list of articles is equivalent to a container

 

Because json does not support datetime-type data by default, a custom json encoder (both to serialize news time)

# stay utils/json_res.py Add custom to the file json Encoder so that it can be serialized datetime data type
import json
import datetime
from django.http import JsonResponse

from .res_code import Code


# json Encoder
# Custom serializer 1, processing time fields(Older versions will be wrong.)
#class MyJSONEncoder(json.JSONEncoder):
#    def default(self, o):
#        if isinstance(o, datetime.datetime):
#            return o.astimezone().strftime('%Y-%m-%d %H:%M:%S')  # Conversion to local time
        
# Custom serializer 2 to process time fields(2.1.10django Edition DjangoJSONEncoder Can help us realize automatically)
class MyJSONEncoder(DjangoJSONEncoder):
    def default(self, o):
        if isinstance(o, datetime.datetime):
            return o.astimezone().strftime('%Y-%m-%d %H:%M:%S')  # Conversion to local time

def json_response(errno=Code.OK, errmsg='', data=None, kwargs=None):
    json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': data
    }
    if kwargs and isinstance(kwargs, dict) :
        json_dict.update(kwargs)

    return JsonResponse(json_dict, encoder=MyJSONEncoder)

Define constants

# stay news Directory constants.py The following constants are defined:

# Number of news per page
PER_PAGE_NEWS_COUNT = 5

Routing configuration

from django.urls import path
from . import views
# url Namespace
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # Name this route index
    path('news/', views.NewsListView.as_view(), name='news_list')
]

4. Front-end code

Front-end html page code modification

<!-- news-contain start empty ul Content in -->
<div class="news-contain">
    <ul class="news-list">



    </ul>
</div>
<!-- news-contain end -->

js code

// static/js/news/index.js
$(function () {
    // News list
    let $newNavLi = $('.news-nav ul li');   // Label li
    let iPage = 1;                          // Default page 1
    let iTotalPage = 1;                     // The default total number of pages is 1
    let iCurrentTagId = 0;                  // The default classification label is 0
    let bIsLoadData = true;                 // Are you loading data into the background?
    
    fn_load_content();

    // Click on the Classification Label
    $newNavLi.click(function () {
        // Click on the category label to add a label to the clicked label active Of class attribute
        // And remove the sibling elements active Of class attribute
        $(this).addClass('active').siblings('li').removeClass('active');
        // Get the binding in data-id Attribute tag_id
        let iClickTagId = $(this).children('a').attr('data-id');
        if (iClickTagId !== iCurrentTagId){
            iCurrentTagId = iClickTagId;  // Record current classification id
            // Reset Paging Parameters
            iPage = 1;
            iTotalPage = 1;
            fn_load_content()
        }

    });

    // Page scroll loading
    $(window).scroll(function () {
       // Browser window height
        let showHeigtht = $(window).height();
       // Whole page height
        let pageHeight = $(document).height();
        //How far a page can scroll
        let canScrollHeight = pageHeight - showHeigtht;
        // How many pages scroll, and the whole thing changes in real time as the pages scroll
        let nowScroll = $(document).scrollTop();
        if ((canScrollHeight - nowScroll) < 100){
            if(!bIsLoadData){
                bIsLoadData = true;
                //Judge the number of pages, update the news, load less than the total
                if(iPage < iTotalPage){
                    iPage += 1;
                    fn_load_content();

                }else {
                    message.showInfo('All loaded, no more content!');
                    $('a.btn-more').html('All loaded, no more content!')
                }

            }
        }
    });

    // Getting news list data from the back end
    function fn_load_content() {
        $.ajax({
            url: '/news/',
            type: 'GET',
            data:{
                tag: iCurrentTagId,
                page: iPage
            },
            dataType: 'json',
            success: function (res) {
                if(res.errno === '0'){
                    iTotalPage = res.data.total_pages;
                    if(iPage === 1){
                        // The first page is empty.
                        $('.news-list').html('')
                    }
                    res.data.news.forEach(function (one_news) {
                        let content = `                  <li class="news-item">
                      <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                         target="_blank">
                          <img src="${one_news.image_url}" alt="${one_news.title}"
                               title="${one_news.title}">
                      </a>
                      <div class="news-content">
                          <h4 class="news-title"><a
                                  href="#">${one_news.title}</a>
                          </h4>
                          <p class="news-details">${one_news.digest}</p>
                          <div class="news-other">
                              <span class="news-type">${one_news.tag_name}</span>
                              <span class="news-time">${one_news.update_time}</span>
                              <span class="news-author">${one_news.author}</span>
                          </div>
                      </div>
                  </li>`;
                        $('.news-list').append(content);
                    });
                  // $('.news-list').append($('<a href="javascript:void(0);" class="btn-more">Scroll Load More</a>'));
                  //When the data is loaded, set the data variable being loaded to false,Indicates that no data is currently loaded
                  bIsLoadData = false;
                  $('a.btn-more').html('Scroll Load More')
                }else {
                    // Load failure, print error message
                    message.showError(res.errmsg)
                }
            },
            error: function () {
                message.showError('Server timeout, please try again!')
            }
        });
    } 
});

V. Rotary Planting Function

1. Interface design

  1. Interface Description:

Category Explain
Request method GET
url definition /news/banners/
Parameter format No parameter

Returns the result:

{
    "errno": "0", 
     "errmsg": "OK", 
     "data": {
        "banners": [
            {
                'image_url': '/media/jichujiaochen.jpeg',
                'news_id': 221,
                'news_title': "python Fast sorting algorithm"
            },
            {
                "image_url": "/media/python_advanced.jpg",
                "news_id": 707,
                "news_title": "Python Unpacking operation of sequence and mapping"
            }
        ]
    }
}

2. Backend code

View code

# stay news Directory views.py Create the following view in
class NewsBannerView(View):
    """
    //Rotary Graph View
    url:/news/banners/
    """
    def get(self, request):
        banners = Banner.objects.values('image_url', 'news_id').annotate(
            news_title=F('news__title')
        ).filter(is_delete=False)[:constants.SHOW_BANNER_COUNT]
        data = {
            'banners': list(banners)
        }
        return json_response(data=data)

Define constants

# stay news Directory constants.py The following constants are defined
# banner Number of pages displayed
SHOW_BANNER_COUNT = 6

Route

# news Directory urls.py The following routing is defined:
from django.urls import path
from . import views
# url Namespace
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # Name this route index
    path('news/', views.NewsListView.as_view(), name='news_list'),
    path('news/banners/', views.NewsBannerView.as_view(), name='news_banner')
]

3. Front-end code

html code

<!-- modify templates/news/index.html in banner Part of the code is as follows -->
<!-- banner start -->
        <div class="banner">
            <ul class="pic">
                <!--Fade in and fade out banner-->

            </ul>
            <a href="javascript:void(0);" class="btn prev">
                <i class="PyWhich py-arrow-left"></i></a>
            <a href="javascript:void(0);" class="btn next">
                <i class="PyWhich py-arrow-right"></i></a>
            <ul class="tab">
                <!-- The number of buttons must be the same as the picture. -->

            </ul>
        </div>
      <!-- banner end -->

js code

<!-- static/js/news/index.js -->
    $(function () {
    // News list
    let $newNavLi = $('.news-nav ul li');   // Label li
    let iPage = 1;                          // Default page 1
    let iTotalPage = 1;                     // The default total number of pages is 1
    let iCurrentTagId = 0;                  // The default classification label is 0
    let bIsLoadData = true;                 // Are you loading data into the background?
    
    fn_load_content();

    // Click on the Classification Label
    $newNavLi.click(function () {
        // Click on the category label to add a label to the clicked label active Of class attribute
        // And remove the sibling elements active Of class attribute
        $(this).addClass('active').siblings('li').removeClass('active');
        // Get the binding in data-id Attribute tag_id
        let iClickTagId = $(this).children('a').attr('data-id');
        if (iClickTagId !== iCurrentTagId){
            iCurrentTagId = iClickTagId;  // Record current classification id
            // Reset Paging Parameters
            iPage = 1;
            iTotalPage = 1;
            fn_load_content()
        }

    });

    // Page scroll loading
    $(window).scroll(function () {
       // Browser window height
        let showHeigtht = $(window).height();
       // Whole page height
        let pageHeight = $(document).height();
        //How far a page can scroll
        let canScrollHeight = pageHeight - showHeigtht;
        // How many pages scroll, and the whole thing changes in real time as the pages scroll
        let nowScroll = $(document).scrollTop();
        if ((canScrollHeight - nowScroll) < 100){
            if(!bIsLoadData){
                bIsLoadData = true;
                //Judge the number of pages, update the news, load less than the total
                if(iPage < iTotalPage){
                    iPage += 1;
                    fn_load_content();

                }else {
                    message.showInfo('All loaded, no more content!');
                    $('a.btn-more').html('All loaded, no more content!')
                }

            }
        }
    });

    // Getting news list data from the back end
    function fn_load_content() {
        $.ajax({
            url: '/news/',
            type: 'GET',
            data:{
                tag: iCurrentTagId,
                page: iPage
            },
            dataType: 'json',
            success: function (res) {
                if(res.errno === '0'){
                    iTotalPage = res.data.total_pages;
                    if(iPage === 1){
                        // The first page is empty.
                        $('.news-list').html('')
                    }
                    res.data.news.forEach(function (one_news) {
                        let content = `                  <li class="news-item">
                      <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                         target="_blank">
                          <img src="${one_news.image_url}" alt="${one_news.title}"
                               title="${one_news.title}">
                      </a>
                      <div class="news-content">
                          <h4 class="news-title"><a
                                  href="#">${one_news.title}</a>
                          </h4>
                          <p class="news-details">${one_news.digest}</p>
                          <div class="news-other">
                              <span class="news-type">${one_news.tag_name}</span>
                              <span class="news-time">${one_news.update_time}</span>
                              <span class="news-author">${one_news.author}</span>
                          </div>
                      </div>
                  </li>`;
                        $('.news-list').append(content);
                    });
                  // $('.news-list').append($('<a href="javascript:void(0);" class="btn-more">Scroll Load More</a>'));
                  //When the data is loaded, set the data variable being loaded to false,Indicates that no data is currently loaded
                  bIsLoadData = false;
                  $('a.btn-more').html('Scroll Load More')
                }else {
                    // Load failure, print error message
                    message.showError(res.errmsg)
                }
            },
            error: function () {
                message.showError('Server timeout, please try again!')
            }
        })

    }

    // Function of News Rotary Map
    // 1.Loading Rotary Map Data function fn_load_banner()
    // 2.Click the navigation button to switch this Words must be written function()No use.()=>
    // 3.Previous page,next page
    // 4.Automatic switching
    // 5.Mouse slides into pause to play automatically
    
    fn_load_banner();                   // Load first banner

    let $banner = $('.banner');         // banner container div
    let $picLi = $('.banner .pic li');  // picture li Label
    let $pre = $('.banner .prev');      // Last one
    let $next = $('.banner .next');     // Next piece
    let $tabLi = $('.banner .tab li');  // Button
    let index = 0;                      // Current index

    // Navigation dot
    $tabLi.click(function () {
        index = $(this).index();
        $(this).addClass('active').siblings('li').removeClass('active');
        $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500);
    });
    
    // Click to switch to the previous one
    $pre.click(()=> {
        index --;
        if(index<0){
            index = $tabLi.length - 1       // Last piece
        }
        $tabLi.eq(index).addClass('active').siblings('li').removeClass('active');
        $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500);
    });

    // Click to switch to the next one
    $next.click(()=>{
        auto();
    });

    // The picture slides forward
    function auto() {
        index ++;
        index %= $tabLi.length;
        $tabLi.eq(index).addClass('active').siblings('li').removeClass('active');
        $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500)

    }
    // timer
    let timer = setInterval(auto, 2500);
    $banner.hover(
        ()=>{
            clearInterval(timer)
        },
        ()=>{
            timer = setInterval(auto, 2500);
        }
    );
    // Define Back-End Acquisition banner Of ajax data
    function fn_load_banner() {
        $
            .ajax({
                url: '/news/banners/',
                type: 'GET',
                async: false,               // Synchronized execution, the following code dependencies banner Loading
                dataType: "json",

            })
            .done( (res)=> {
                if(res.errno === '0'){
                    let content = '';
                    let tab_content = '';
                    res.data.banners.forEach( (one_banner, index) =>{
                        if(index === 0){        // Page 1 plus active attribute
                            content = `<li style="display:block;"><a href="/news/${one_banner.news_id}/">
                 <img src="${one_banner.image_url}" alt="${one_banner.news_title}"></a></li>`;
                            tab_content = '<li class="active"></li>';
                        }else {
                            content = `<li><a href="/news/${one_banner.news_id}/"><img src="${one_banner.image_url}" alt="${one_banner.news_title}"></a></li>`;
                            tab_content = '<li></li>';
                        }
                        $('.pic').append(content);
                        $('.tab').append(tab_content)
                    })
                    
                }else {
                    message.showError(res.errmsg)
                }
            })
            .fail(()=>{
                message.showError('Server timeout, please try again!')
            })
    }
});

Recommended News

1. Interface design

  1. Interface Description:

Category Explain
Request method GET
url definition /
Parameter format No parameter

2. Return content

Return to the news page and render directly in the template

2. Backend code

View code

# modify news/views.py Medium index view
def index(request):
    """
    //News Home View
    url: /
    :param request:
    :return:
    """

    tags = Tag.objects.only('id', 'name').filter(is_delete=False)
    
    hot_news = HotNews.objects.select_related('news').only('news__title', 'news__image_url', 'news_id').filter(
        is_delete=False
    ).order_by('priority', '-news__clicks')[:constants.SHOW_HOTNEWS_COUNT]
    return render(request, 'news/index.html',
                  context={
                      'tags': tags,
                      'hot_news': hot_news
                  })

Define constants

# stay news/constants.py Define the following constants
# Show the number of hot news items
SHOW_HOTNEWS_COUNT = 3

3. Front-end code

html code

<!-- modify templates/news/index.html -->
<ul class="recommend-news">
    {% for item in hot_news %}
    <li>
        <a href="https://www.shiguangkey.com/course/2432" target="_blank">
            <div class="recommend-thumbnail">
                <img src="{{ item.news.image_url }}" alt="title">
            </div>
            <p class="info">{{ item.news.title }}</p>
        </a>
    </li>
    {% endfor %}
</ul>
<!-- recommend-news end -->

Keywords: Python Django Database JSON

Added by stevenm on Wed, 18 Sep 2019 16:58:04 +0300