欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Django项目: 5.新闻主页

程序员文章站 2022-04-14 15:30:54
一、功能需求分析 1.功能 轮播图 推荐文章列表 文章标签导航 文章列表 分页 二、模型设计 根据功能分析,我们需要如下表 1.表和字段分析 文章分类表 文章表 文章评论表 推荐文章表 轮播图表 2.模型定义 定义一个基类模型,抽取公共字段 定义其他模型 三、文章标签导航功能 1.接口设计 接口说明 ......

一、功能需求分析

1.功能

  1. 轮播图

  2. 推荐文章列表

  3. 文章标签导航

  4. 文章列表

  5. 分页

二、模型设计

根据功能分析,我们需要如下表

1.表和字段分析

  1. 文章分类表

  2. 文章表

  3. 文章评论表

  4. 推荐文章表

  5. 轮播图表

2.模型定义

定义一个基类模型,抽取公共字段

# 在utils目录下,创建一个models.py文件,在其中定义一个基类模型
from django.db import models


class basemodel(models.model):
    """
    基类,公共字段
    """
    create_time = models.datetimefield('创建时间', auto_now_add=true)
    update_time = models.datetimefield('更新时间', auto_now=true)
    is_delete = models.booleanfield('逻辑删除', default=false)

    class meta:
        # 抽象类,用于继承,迁移时不会创建
        abstract = true

定义其他模型

# 在news目录下的models.py文件中定义如下数据模型
from django.db import models
from utils.models import basemodel


class tag(basemodel):
    """
    文章分类标签模型
    """
    name = models.charfield('标签名', max_length=64, help_text='标签名')

    class meta:
        ordering = ['-update_time', '-id']      # 排序
        db_table = "tb_tag"                     # 指明数据库表名
        verbose_name = "文章标签"                # 在admin站点中显示的名称
        verbose_name_plural = verbose_name      # 显示的复数名称

    def __str__(self):
        return self.name


class news(basemodel):
    """
    文章模型
    """
    title = models.charfield('标题', max_length=150, help_text='标题')
    digest = models.charfield('摘要', max_length=200, help_text='摘要')
    content = models.textfield('内容', help_text='内容')
    clicks = models.integerfield('点击量', default=0, help_text='点击量')
    image_url = models.urlfield('图片url', default='', help_text='图片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']  # 排序
        db_table = "tb_news"  # 指明数据库表名
        verbose_name = "新闻"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return self.title


class comments(basemodel):
    """
    评论模型
    """
    content = models.textfield('内容', help_text='内容')
    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']  # 排序
        db_table = "tb_comments"  # 指明数据库表名
        verbose_name = "评论"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

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


class hotnews(basemodel):
    """
    推荐文章表
    """
    news = models.onetoonefield('news', on_delete=models.cascade)
    priority = models.integerfield('优先级', help_text='优先级')

    class meta:
        ordering = ['-update_time', '-id']  # 排序
        db_table = "tb_hotnews"  # 指明数据库表名
        verbose_name = "热门新闻"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<热门新闻{}>'.format(self.id)


class banner(basemodel):
    """
    轮播图
    """
    image_url = models.urlfield('轮播图url', help_text='轮播图url')
    priority = models.integerfield('优先级', help_text='优先级')

    news = models.onetoonefield('news', on_delete=models.cascade)

    class meta:
        ordering = ['priority', '-update_time', '-id']  # 排序
        db_table = "tb_banner"  # 指明数据库表名
        verbose_name = "轮播图"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<轮播图{}>'.format(self.id)

三、文章标签导航功能

1.接口设计

  1. 接口说明:

    类目 说明
    请求方法 get
    url定义 /
    参数格式 无参数
  2. 返回结果

    返回新闻页面,直接在模板渲染

2.后端代码

# 在news/views.py文件中定义如下视图
from django.shortcuts import render

from .models import tag


def index(request):
    """
    新闻首页视图
    :param request:
    :return:
    """

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

导入tag测试数据,或者直接用navicat软件在tb_tag表中添加数据(数据见下方代码中引号里的内容,共6个),,,因为前段设置的原因,必须要跟我的内容一样,后面会用到

# insert news tag data

insert into tb_tag(name, create_time, update_time, is_delete) values
('python基础', now(), now(), 0),
('python高级', now(), now(), 0),
('python函数', now(), now(), 0),
('pythongui', now(), now(), 0),
('linux教程', now(), now(), 0),
('python框架', now(), now(), 0);

3.前端代码

# 修改templates/news/index.html中 news-nav部分代码如:
        <!--  news-nav start-->
          <nav class="news-nav">
              <ul class="clearfix">
                  <li class="active"><a href="javascript:void(0)">最新资讯</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 -->

四、新闻列表功能

1.业务流程分析

  1. 判断前端传递标签分类id是否为空,是否为整数,是否超过范围

  2. 判断前端传递当前文章页数是否为空,是否为整数,是否超过范围

2.接口设计

接口说明:

类目 说明
请求方法 get
url定义 /news/
参数格式 查询参数

参数说明:

参数名 类型 是否必须 描述
tag 整数 标签分类id
page 整数 当前文章页数

返回结果:

{
    "errno": "0", 
     "errmsg": "", 
    "data": {
        "total_pages": 61,
        "news": [
            {
                'id': 'xxx',
                "digest": "在python用import或者from...import或者from...import...as...来导入相应的模块,作用和使用方法与c语言的include头文件类似。其实就是引入...",
                "title": "import方法引入模块详解",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "python基础",
                "update_time": "2018年12月17日 14:48"
            },
            {
                'id': 'xxx'
                "digest": "如果你原来是一个php程序员,你对于php函数非常了解(ps:站长原来就是一个php程序员),但是现在由于工作或者其他原因要学习python,但是p...",
                "title": "给曾经是phper的程序员推荐个学习网站",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "python基础",
                "update_time": "2018年12月17日 14:48"
            }
        ]
    }
}

3.后端代码

在项目根目录下创建一个media文件夹,用于存放新闻图片以及用户上传文件。

# 在settings.py文件中添加
# 媒体文件配置
media_url = '/media/'
media_root = os.path.join(base_dir, 'media')

django在调试模式下提供静态文件服务,为了能够返回media中的媒体文件还需在根urls.py中做如下配置

# 在根urls.py中加上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)

导入测试数据,为了测试数据的导入,请确保表名一致

# 在xshell中导入测试数据,在xshell中通过rz命令,将tb_news_20181217.sql文件上传到虚拟机

mysql -u 用户名 -p -d 数据库名 < tb_news_20181217.sql

 文件我已经放在我的文件栏里(名字是sql数据包.rar,解压后有4个文件,分别是标签表,新闻表,热门新闻表和轮播图表)

 

视图代码

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):
    """
    新闻首页视图
    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):
    """
    新闻列表视图
    url: /news/
    args: tag, page
    """
    def get(self, request):
        # 1.获取参数
        try:
            tag_id = int(request.get.get('tag', 0))
        except exception as e:
            logger.error('标签错误:\n{}'.format(e))
            tag_id = 0

        try:
            page = int(request.get.get('page', 0))
        except exception as e:
            logger.error('页码错误:\n{}'.format(e))
            page = 1
        # 使用only返回的是对象,所以传递到前端时需要迭代处理
        # news_queryset = news.objects.select_related('tag', 'author').only(
        #     'title', 'digest', 'image_url', 'update_time', 'tag__name', 'author__username')
        # 2.获取查询集 (values返回的是字典,only返回的是对象)
        ############## 重要 start #################
        # queryset:惰性 不会去数据库 
        # 构造,切片,过滤,传递的时候通常查询集不真正的去数据库查询
        # 什么时候,去数据库查呢:
            # 1.迭代
            # 2.切片,只要不是跳着去查就不会到数据库中查询,只有中间隔着数据切片的时候才会去数据库中查询,切出一个元素的时候
            # 3.照顾会在控制台打印的时候
            # 4.序列化缓存,把查询集的内容存到redis,内存
            # 5.使用len()方法获取长度,count
            # 6.使用list()方法转换类型
            # 7.bool,去判断是否为空的时候
        # queryset的缓存
        # 什么时候不缓存
            # 只执行查询集的一部分
            # 简单的打印不换车:q = news.objects.all()        print(q[1]) # 不会被缓存
            
           
        ############## 重要 end #################
        news_queryset = news.objects.values('id', 'title', 'digest', 'image_url', 'update_time').annotate(
            tag_name=f('tag__name'), author=f('author__username'))

        # 3.过滤
        # 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.分页
        paginator = paginator(news, constants.per_page_news_count) # 见下文讲解
        # 获取当前页数据 get_page 可以容错
        news_info = paginator.get_page(page)
        # 5.返回数据
        data = {
            'total_pages': paginator.num_pages, # 分了多少页
            'news':list(news_info)
        }
        return json_response(data=data)

paginator(分页器)方法:

paginator.get_page(number)此方法为2.0新方法。

返回page具有给定的从1开始的索引对象,同时还处理超出范围和无效页码,如果页码不是数字,则返回第一页。如果页码为负数或大于页数,则返回最后一页。

# 2.分页解析
        paginator = paginator(docs, 5) #  每5份内容分页一次
        # 2.2 拿到前段发送过来的page这个查询参数的值
        page = paginator.get_page(request.get.get('page',1))
        # request.get.get('page',1) 获取url参数,127.0.0.1:8000/?page=<value>
        # 非法数值则返回1 数值为空也返回1 如 127.0.0.1:8000/?page=asdsa
        # 获取当前(页码)所需要的文章列表 相当于一个容器

 

因为json默认不支持datetime类型数据,所以自定义json编码器(既让新闻的时间序列化)

# 在utils/json_res.py文件中添加自定义的json编码器以便能够序列化datetime数据类型
import json
import datetime
from django.http import jsonresponse

from .res_code import code


# json编码器
# 自定义序列化器1,处理时间字段(老一点的版本这个就会报错)
#class myjsonencoder(json.jsonencoder):
#    def default(self, o):
#        if isinstance(o, datetime.datetime):
#            return o.astimezone().strftime('%y-%m-%d %h:%m:%s')  # 转换为本地时间
        
# 自定义序列化器2,处理时间字段(2.1.10django版本 djangojsonencoder能够帮我们自动的实现)
class myjsonencoder(djangojsonencoder):
    def default(self, o):
        if isinstance(o, datetime.datetime):
            return o.astimezone().strftime('%y-%m-%d %h:%m:%s')  # 转换为本地时间

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)

定义常量

# 在news目录下constants.py中定义如下常量:

# 每页新闻数
per_page_news_count = 5

路由配置

from django.urls import path
from . import views
# url的命名空间
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # 将这条路由命名为index
    path('news/', views.newslistview.as_view(), name='news_list')
]

4.前端代码

前端html页面代码修改

<!-- news-contain start 清空 ul中的内容 -->
<div class="news-contain">
    <ul class="news-list">



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

js代码

// static/js/news/index.js
$(function () {
    // 新闻列表
    let $newnavli = $('.news-nav ul li');   // 标签li
    let ipage = 1;                          // 默认第一页
    let itotalpage = 1;                     // 默认总页数为1
    let icurrenttagid = 0;                  // 默认分类标签为0
    let bisloaddata = true;                 // 是否正在向后台加载数据
    
    fn_load_content();

    // 点击分类标签
    $newnavli.click(function () {
        // 点击分类标签,则为点击的标签加上一个active的class属性
        // 并移除其他兄弟元素上的active的class属性
        $(this).addclass('active').siblings('li').removeclass('active');
        // 获取绑定在data-id属性上的tag_id
        let iclicktagid = $(this).children('a').attr('data-id');
        if (iclicktagid !== icurrenttagid){
            icurrenttagid = iclicktagid;  // 记录当前分类id
            // 重置分页参数
            ipage = 1;
            itotalpage = 1;
            fn_load_content()
        }

    });

    // 页面滚动加载
    $(window).scroll(function () {
       // 浏览器窗口高度
        let showheigtht = $(window).height();
       // 整个网页高度
        let pageheight = $(document).height();
        //页面可以滚动的距离
        let canscrollheight = pageheight - showheigtht;
        // 页面滚动了多少, 整个是随着页面滚动实时变化的
        let nowscroll = $(document).scrolltop();
        if ((canscrollheight - nowscroll) < 100){
            if(!bisloaddata){
                bisloaddata = true;
                //判断页数,去更新新闻,小于总数才加载
                if(ipage < itotalpage){
                    ipage += 1;
                    fn_load_content();

                }else {
                    message.showinfo('已全部加载,没有更多内容!');
                    $('a.btn-more').html('已全部加载,没有更多内容!')
                }

            }
        }
    });

    // 向后端获取新闻列表数据
    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){
                        // 第一页清空内容
                        $('.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">滚动加载更多</a>'));
                  //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据
                  bisloaddata = false;
                  $('a.btn-more').html('滚动加载更多')
                }else {
                    // 加载失败,打印错误信息
                    message.showerror(res.errmsg)
                }
            },
            error: function () {
                message.showerror('服务器超时,请重试!')
            }
        });
    } 
});

五、轮播图功能

1. 接口设计

  1. 接口说明:

类目 说明
请求方法 get
url定义 /news/banners/
参数格式 无参数

返回结果:

{
    "errno": "0", 
     "errmsg": "ok", 
     "data": {
        "banners": [
            {
                'image_url': '/media/jichujiaochen.jpeg',
                'news_id': 221,
                'news_title': "python 算法快速排序"
            },
            {
                "image_url": "/media/python_advanced.jpg",
                "news_id": 707,
                "news_title": "python 序列与映射的解包操作"
            }
        ]
    }
}

2.后端代码

视图代码

# 在news目录下views.py中创建如下视图
class newsbannerview(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)

定义常量

# 在news目录下constants.py中定义如下常量
# banner页展示数量
show_banner_count = 6

路由

# news目录下urls.py中定义如下路由:
from django.urls import path
from . import views
# url的命名空间
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # 将这条路由命名为index
    path('news/', views.newslistview.as_view(), name='news_list'),
    path('news/banners/', views.newsbannerview.as_view(), name='news_banner')
]

3.前端代码

html代码

<!-- 修改templates/news/index.html中banner部分的代码如下 -->
<!-- banner start -->
        <div class="banner">
            <ul class="pic">
                <!--淡入淡出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">
                <!-- 按钮数量必须和图片一致 -->

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

js代码

<!-- static/js/news/index.js -->
    $(function () {
    // 新闻列表
    let $newnavli = $('.news-nav ul li');   // 标签li
    let ipage = 1;                          // 默认第一页
    let itotalpage = 1;                     // 默认总页数为1
    let icurrenttagid = 0;                  // 默认分类标签为0
    let bisloaddata = true;                 // 是否正在向后台加载数据
    
    fn_load_content();

    // 点击分类标签
    $newnavli.click(function () {
        // 点击分类标签,则为点击的标签加上一个active的class属性
        // 并移除其他兄弟元素上的active的class属性
        $(this).addclass('active').siblings('li').removeclass('active');
        // 获取绑定在data-id属性上的tag_id
        let iclicktagid = $(this).children('a').attr('data-id');
        if (iclicktagid !== icurrenttagid){
            icurrenttagid = iclicktagid;  // 记录当前分类id
            // 重置分页参数
            ipage = 1;
            itotalpage = 1;
            fn_load_content()
        }

    });

    // 页面滚动加载
    $(window).scroll(function () {
       // 浏览器窗口高度
        let showheigtht = $(window).height();
       // 整个网页高度
        let pageheight = $(document).height();
        //页面可以滚动的距离
        let canscrollheight = pageheight - showheigtht;
        // 页面滚动了多少, 整个是随着页面滚动实时变化的
        let nowscroll = $(document).scrolltop();
        if ((canscrollheight - nowscroll) < 100){
            if(!bisloaddata){
                bisloaddata = true;
                //判断页数,去更新新闻,小于总数才加载
                if(ipage < itotalpage){
                    ipage += 1;
                    fn_load_content();

                }else {
                    message.showinfo('已全部加载,没有更多内容!');
                    $('a.btn-more').html('已全部加载,没有更多内容!')
                }

            }
        }
    });

    // 向后端获取新闻列表数据
    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){
                        // 第一页清空内容
                        $('.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">滚动加载更多</a>'));
                  //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据
                  bisloaddata = false;
                  $('a.btn-more').html('滚动加载更多')
                }else {
                    // 加载失败,打印错误信息
                    message.showerror(res.errmsg)
                }
            },
            error: function () {
                message.showerror('服务器超时,请重试!')
            }
        })

    }

    // 新闻轮播图功能
    // 1.加载轮播图数据 function fn_load_banner()
    // 2.点击导航按钮切换 用this的话必须写function()了不能用()=>
    // 3.上一页,下一页
    // 4.自动切换
    // 5.鼠标滑入暂停自动播放
    
    fn_load_banner();                   // 先加载banner

    let $banner = $('.banner');         // banner容器div
    let $picli = $('.banner .pic li');  // 图片li标签
    let $pre = $('.banner .prev');      // 上一张
    let $next = $('.banner .next');     // 下一张
    let $tabli = $('.banner .tab li');  // 按钮
    let index = 0;                      // 当前索引

    // 导航小圆点
    $tabli.click(function () {
        index = $(this).index();
        $(this).addclass('active').siblings('li').removeclass('active');
        $picli.eq(index).fadein(1500).siblings('li').fadeout(1500);
    });
    
    // 点击切换上一张
    $pre.click(()=> {
        index --;
        if(index<0){
            index = $tabli.length - 1       // 最后一张
        }
        $tabli.eq(index).addclass('active').siblings('li').removeclass('active');
        $picli.eq(index).fadein(1500).siblings('li').fadeout(1500);
    });

    // 点击切换下一张
    $next.click(()=>{
        auto();
    });

    // 图片向前滑动
    function auto() {
        index ++;
        index %= $tabli.length;
        $tabli.eq(index).addclass('active').siblings('li').removeclass('active');
        $picli.eq(index).fadein(1500).siblings('li').fadeout(1500)

    }
    // 定时器
    let timer = setinterval(auto, 2500);
    $banner.hover(
        ()=>{
            clearinterval(timer)
        },
        ()=>{
            timer = setinterval(auto, 2500);
        }
    );
    // 定义向后端获取banner的ajax数据
    function fn_load_banner() {
        $
            .ajax({
                url: '/news/banners/',
                type: 'get',
                async: false,               // 同步执行,下面的代码依赖banner的加载
                datatype: "json",

            })
            .done( (res)=> {
                if(res.errno === '0'){
                    let content = '';
                    let tab_content = '';
                    res.data.banners.foreach( (one_banner, index) =>{
                        if(index === 0){        // 第一页 加active属性
                            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('服务器超时,请重试!')
            })
    }
});

六、推荐新闻

1. 接口设计

  1. 接口说明:

类目 说明
请求方法 get
url定义 /
参数格式 无参数

2.返回内容

返回新闻页面,直接在模板渲染

2.后端代码

视图代码

# 修改news/views.py中的index视图
def index(request):
    """
    新闻首页视图
    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
                  })

定义常量

# 在news/constants.py中定义下面的常量
# 显示热门新闻条数
show_hotnews_count = 3

3.前端代码

html代码

<!-- 修改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 -->