新闻首页实现 -- 3. 文章列表页功能实现

1.分析

业务处理流程:

  • 判断前端传的标签分类id是否为空,是否为整数、是否超过范围
  • 判断前端传的当前文章页数是否为空,是否为整数、是否超过范围

请求方法GET

url定义/news/list/

传参方式:url字符串传参

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

向前端返回的数据格式为json格式,返回实例如下:

{
    "data": {
        "total_pages": 61,
        "news": [
            {
                "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"
            },
            {
                "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"
            }
        ]
    },
    "errno": "200",
    "errmsg": ""
}

2.后端视图实现

首先在项目根目录下创建一个media文件夹,用于存放新闻图片以及用户上传的文件(仅供测试使用)
正常会存储在七牛云等平台

# 在settings.py文件中添加如下配置:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 在全局urls.py文件中添加如下配置:
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include

urlpatterns = [
    ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# 在xshell中导入测试数据,在xshell中通过rz命令,将tb_news_20181217.sql文件上传到虚拟机

mysql -u 用户名 -p -D 数据库名 < tb_news_20181217.sql
# 在news目录下views.py文件中创建如下类视图:

import logging

from django.views import View
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage

from . import models
from . import constants
from utils.res_code.rescode import Code, error_map
from utils.json_translate.json_fun import to_json_data


logger = logging.getLogger('django')


# 新闻列表及分页视图
class NewsListView(View):
    """
    1.创建一个类视图 NewsListView
    2.明确请求方式
        -- 请求类型 ajax get
        -- 传参方式 url字符串传参
        -- url定义 '/news/list/'  ?tag_id=1&?page=1
    3.获取前端参数:
        -- tag_id = request.GET.get('tag_id')
    4.业务处理:
        -- 是否需要校验    需要  自定义校验
        -- 是否需要储存以及储存方式     不需要
        -- 其他:
            -- 数据库读取数据
            -- 分页处理
    5.返回前端数据
        -- to_json_data(errno= , errmsg=, data=)
    """
    def get(self, request):

        # tag_id 参数校验
        try:
            tag_id = int(request.GET.get('tag_id', 0))
        except Exception as e:
            tag_id = 0
            logger.error("传入tag_id参数错误:\n{}".format(e))

        # page 参数校验
        try:
            page = int(request.GET.get('page', 1))
        except Exception as e:
            page = 1
            logger.error("传入page参数错误:\n{}".format(e))

        # 若page为0,会返回最后一页内容,用户体验不好
        if page == 0:
            page = 1

        # 获取新闻
        news_queryset = models.News.objects.select_related('tag', 'author').\
            only('title', 'digest', 'image_url', 'update_time', 'tag__name', 'author__username')

        news = news_queryset.filter(is_delete=False, tag_id=tag_id) or \
            news_queryset.filter(is_delete=False)

        # 分页处理
        paginator = Paginator(news, constants.PER_PAGE_NEWS_COUNT)

        try:
            news_info = paginator.page(page)
        except EmptyPage:
            # 若用户访问的页数大于实际页数,则返回最后一页数据
            logger.info('用户访问页数大于总页数')
            news_info = paginator.page(paginator.num_pages)

        # 序列化输出
        news_info_list = []
        for n in news_info:
            news_info_list.append({
                'title':n.title,
                'digest':n.digest,
                'image_url':n.image_url,
                'tag_name':n.tag.name,
                'author':n.author.username,
                'update_time':n.update_time.strftime('%Y年%m月%d日 %H:%M')
            })

        # 返回前端数据
        data = {
            'total_pages':paginator.num_pages,
            'news':news_info_list,
        }

        return to_json_data(data=data)

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

# 每页新闻数
PER_PAGE_NEWS_COUNT = 5
# 在news目录下urls.py中定义如下路由:

urlpatterns = [
      path('list/', views.NewsListView.as_view(), name='news_list'),
]

3.前端代码实现

<!-- 在templates/news/index.html中 -->

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

{% block script %}
  <script src="{% static 'js/news/index.js' %}"></script>
{% endblock %}
// js/news/index.js
$(function () {
  // 新闻列表功能
  let $newsLi = $(".news-nav ul li");
  let iPage = 1;  //默认第1页
  let iTotalPage = 1; //默认总页数为1
  let sCurrentTagId = 0; //默认分类标签为0
  let bIsLoadData = true;   // 是否正在向后台加载数据


  fn_load_content();

  $newsLi.click(function () {
    // 点击分类标签,则为点击的标签加上一个class属性为active
    // 并移除其它兄弟元素的上的,值为active的class属性
    $(this).addClass('active').siblings('li').removeClass('active');
    // 获取绑定在当前选中分类上的data-id属性值
    let sClickTagId = $(this).children('a').attr('data-id');

    if (sClickTagId !== sCurrentTagId) {
            sCurrentTagId = sClickTagId;  // 记录当前分类id
            // 重置分页参数
            iPage = 1;
            iTotalPage = 1;
            fn_load_content()
        }
  });

  //页面滚动加载相关
  $(window).scroll(function () {
    // 浏览器窗口高度
    let showHeight = $(window).height();

    // 整个网页的高度
    let pageHeight = $(document).height();

    // 页面可以滚动的距离
    let canScrollHeight = pageHeight - showHeight;

    // 页面滚动了多少,这个是随着页面滚动实时变化的
    let nowScroll = $(document).scrollTop();

    if ((canScrollHeight - nowScroll) < 100) {
      // 判断页数,去更新新闻数据
      if (!bIsLoadData) {
        bIsLoadData = true;
        // 如果当前页数据如果小于总页数,那么才去加载数据
        if (iPage < iTotalPage) {
          iPage += 1;
          $(".btn-more").remove();  // 删除标签
          // 去加载数据
          fn_load_content()
        } else {
          message.showInfo('已全部加载,没有更多内容!');
          $(".btn-more").remove();  // 删除标签
          $(".news-list").append($('<a href="javascript:void(0);" class="btn-more">已全部加载,没有更多内容!</a>'))
        }
      }
    }
  });


  // 定义向后端获取新闻列表数据的请求
  function fn_load_content() {
    // let sCurrentTagId = $('.active a').attr('data-id');

    // 创建请求参数
    let sDataParams = {
      "tag_id": sCurrentTagId,
      "page": iPage
    };

    // 创建ajax请求
    $.ajax({
      // 请求地址
      url: "/news/list/",  // url尾部需要添加/
      // 请求方式
      type: "GET",
      data: sDataParams,
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
      // 若不加异步:false 后面调用这个函数时,会出现 ""/define
    })
      .done(function (res) {
        if (res.errno === "200") {
          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;

        } else {
          // 登录失败,打印错误信息
          message.showError(res.errmsg);
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });
  }

});
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容