新闻轮播图功能
分析
- 请求方法:GET
- url定义:
/news/banners/
- 请求参数:前端无需传入参数
返回给前端
{
'errmsg':'',
'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序列于映射的解包操作'
}
]
},
'errno':'0'
}
后端代码实现
apps/news/views.py
import logging
from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator,EmptyPage #不存在的页码报错
from django.http import Http404
from apps.news import models
from apps.news import constants
from utils.json_fun import to_json_data
from utils.res_code import Code,error_map
logger = logging.getLogger('django')
# def index(request):
# return render(request,'news/index.html')
class IndexView(View):
"""
constants
"""
def get(self,request):
# tags = models.Tag.objects.filter(is_delete=False)
#通过表中的is_delete判断是否被删除
tags = models.Tag.objects.only('id','name').filter(is_delete=False) #only确定要查询的字段,其他的不查
#使用select_related关联表优化,一对一和一对多都可用,查询集,用切片获取对象
hot_news = models.HotNews.objects.select_related('news').only('news__title','news__image_url',\
'news_id').filter(is_delete=False).order_by('priority','-news__clicks')[0:constants.SHOW_HOTNEWS_COUNT]
# context = {
# 'tags':tags,
# }
#用模板上下文 将当前方法下的变量都传进locals,python内置函数
# return render(request,'news/index.html',context=context)
return render(request,'news/index.html',locals())
#ajax请求
#传参 tag_id page
#后台的返回 图片,标题,摘要,作者,标签,更新时间,文章id
#请求方式:GET
#/news/
#url查询字符串 ?tag_id=1&page=2 /news/?tag_id=1&page=2
class NewsListView(View):
"""
对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
select_related 返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。
它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询
"""
def get(self,request):
"""
# 1.获取参数
# 2.校验参数
# 3.从数据库拿数据
# 4.分页
# 5.序列化输出
#6.返回给前端
:param request:
:return:
"""
#1.获取参数,校验参数
#如果通过不正当手段导致int转换不成功时,返回最新资讯给前端
try:
# 如果没传或者格式不对得不到数据,为0,则把最新资讯id设置为0,当刚进入首页没进行选择标签时起作用,即默认为最新资讯
tag_id = int(request.GET.get('tag_id',0)) #get到的是str类型,存入数据库是int类型
except Exception as e:
logger.error('标签错误:\n{}'.format(e))
tag_id = 0
try:
page = int(request.GET.get('page',1))
except Exception as e:
logger.error('页码错误:\n{}'.format(e))
page = 1
#3.从数据库中拿数据
#title,digest(摘要),image_url,update_time
#select_related用于多表查询,是优化措施,参数是关联外键的字段,id为主键,无论怎样都会查到,所以不需要列出来
#News中的tag字段对应Tag标签中的name字段,News中的author字段对应Users中的username字段
news_queryset = models.News.objects.select_related('tag','author').only('title','digest',\
'image_url','update_time','tag__name','author__username')
#is_delete:是否被逻辑删除,tag_id=0在queryset中表示为空
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) #第一个参数是数据,第二个参数是每页的新闻个数
try:
news_info = paginator.page(page) #对应页的新闻
except EmptyPage:
logger.error('用户访问页数大于总页数')
news_info = paginator.page(paginator.num_pages) #展示最大页码的新闻
#5.序列化输出
news_info_list = []
for n in news_info:
news_info_list.append({
'id':n.id,
'title':n.title,
'digest':n.digest,
'image_url':n.image_url,
'update_time':n.update_time.strftime('%Y-%m-%d %H:%M:%S'),
'tag_name':n.tag.name, #tag的name属性
'author':n.author.username,
})
data = { #返回给前端的,名一定要和前端对应否则没作用,细心
'news':news_info_list,
'total_pages':paginator.num_pages #总页码数
}
#6.返回给前端
return to_json_data(data=data)
class NewsBanner(View):
"""
ajax
"""
#不传参,不改数据,用get
def get(self,request):
banners = models.Banner.objects.select_related('news').only('image_url','news_id','news__title').\
filter(is_delete=False).order_by('priority')[0:constants.SHOW_BANNER_COUNT]
#序列化输出
banners_info_list = []
for b in banners:
banners_info_list.append(
{
'image_url':b.image_url,
'news_id':b.news_id,
'news_title':b.news.title
}
)
data = {
'banners':banners_info_list,
}
return to_json_data(data=data) #返回给前端
#文章详情
#参数:news_id
#title author__username update_time tag_names content
class NewsDetailView(View):
"""
/news/<int:news_id>/
将文章id携带到url
"""
def get(self,request,news_id):
news = models.News.objects.select_related('tag','author').only('title','content','update_time',\
'tag__name','author__username').filter(is_delete=False,id=news_id).first()
if news:
return render(request,'news/news_detail.html',locals())
else:
raise Http404('新闻id不存在'.format(news_id))
apps/news/constants.py
#每页新闻数
PER_PAGE_NEWS_COUNT = 5
#显示热门新闻条数
SHOW_HOTNEWS_COUNT = 3
#轮播图每页新闻数
SHOW_BANNER_COUNT = 6
apps/news/urls.py
from django.urls import path, re_path
from apps.news import views
app_name = 'news'
urlpatterns = [
# path('',views.index,name='index'),
path('',views.IndexView.as_view(),name='index'),
path('news/',views.NewsListView.as_view(),name='news_list'),
path('news/banners/',views.NewsBanner.as_view(),name='news_banner'),
path('news/<int:news_id>/',views.NewsDetailView.as_view(),name='news_detail')
]
- 后台传给前端:context,ajax
- 前端传给后台:form表单
前端代码实现
templates/news/index.html
{% extends 'base/base.html' %}
{% load static %}
{% block title %}<title>IndexPage</title>{% endblock %}
{% block link %}
<link rel="stylesheet" href="{% static 'css/news/index.css' %}">
{% endblock %}
<!-- main-contain start -->
{% block contain %}
<div class="main-contain">
<!-- banner start -->
<div class="banner">
<ul class="pic">
<!--淡入淡出banner-->
{# <li><a href="javascript:void(0);"><img src="{% static 'img/ui.png' %}" alt="test"></a></li>#}
{# <li><a href="javascript:void(0);"><img src="{% static 'img/youxi.png' %}" alt="test"></a></li>#}
{# <li><a href="javascript:void(0);"><img src="{% static 'img/dianshang.png' %}" alt="test"></a></li>#}
{##}
{##}
{# <li><a href="javascript:void(0);"><img src="{% static 'img/zimeiti.png' %}" alt="test"></a></li>#}
{##}
{##}
{# <li><a href="javascript:void(0);"><img src="{% static 'img/python_gui.jpg' %}" alt="test"></a></li>#}
{##}
{##}
{# <li><a href="javascript:void(0);"><img src="{% static 'img/linux.jpg' %}" alt="test"></a></li>#}
</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">
<!-- 按钮数量必须和图片一致 -->
{# <li></li>#}
{##}
{##}
{# <li></li>#}
{##}
{##}
{# <li></li>#}
{##}
{##}
{# <li></li>#}
{##}
{##}
{# <li></li>#}
{##}
{##}
{# <li></li>#}
</ul>
</div>
<!-- banner end -->
<!-- content start -->
<div class="content">
<!-- recommend-news start -->
<ul class="recommend-news">
{% for n in hot_news %}
<li>
<a href="{% url 'news:news_detail' n.news.id %}" target="_blank">
<div class="recommend-thumbnail">
<img src="{{ n.news.image_url }}" alt="title">
</div>
<p class="info">{{ n.news.title }}</p>
</a>
</li>
{% endfor %}
{# <li>#}
{# <a href="https://www.shiguangkey.com/course/2432" target="_blank">#}
{# <div class="recommend-thumbnail">#}
{# <img src="{% static 'img/python_gui.jpg' %}" alt="title">#}
{# </div>#}
{# <p class="info">Python GUI 教程 25行代码写一个小闹钟</p>#}
{# </a>#}
{# </li>#}
{##}
{# <li>#}
{# <a href="https://www.shiguangkey.com/course/2432" target="_blank">#}
{# <div class="recommend-thumbnail">#}
{# <img src="{% static 'img/python_advanced.jpg' %}" alt="title">#}
{# </div>#}
{# <p class="info">python高性能编程方法一</p>#}
{# </a>#}
{# </li>#}
{##}
{# <li>#}
{# <a href="https://www.shiguangkey.com/course/2432" target="_blank">#}
{# <div class="recommend-thumbnail">#}
{# <img src="{% static 'img/jichujiaochen.jpeg' %}" alt="title">#}
{# </div>#}
{# <p class="info">python基础 split 和 join函数比较</p>#}
{# </a>#}
{# </li>#}
</ul>
<!-- recommend-news end -->
<!-- news-nav start-->
<nav class="news-nav">
<ul class="clearfix">
<li class="active"><a href="javascript:void(0)" data-id="0">最新资讯</a></li>
{% for tag in tags %}
<li><a href="javascript:void(0)" data-id="{{ tag.id }}">{{ tag.name }}</a></li>
{% endfor %}
{# <li><a href="javascript:void(0)" data-id="2">Python基础</a>#}
{# </li>#}
{##}
{# <li><a href="javascript:void(0)" data-id="3">Python高级</a>#}
{# </li>#}
{##}
{# <li><a href="javascript:void(0)" data-id="4">Python函数</a>#}
{# </li>#}
{##}
{# <li><a href="javascript:void(0)" data-id="5">PythonGUI</a>#}
{# </li>#}
{##}
{# <li><a href="javascript:void(0)" data-id="6">Linux教程</a>#}
{# </li>#}
</ul>
</nav>
<!-- news-nav end -->
<!-- news-contain start -->
<div class="news-contain">
<ul class="news-list">
{##}
{# <li class="news-item">#}
{# <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"#}
{# target="_blank">#}
{# <img src="{% static 'img/linux.jpg' %}" alt="linux查找文件或目录命令"#}
{# title="linux查找文件或目录命令">#}
{# </a>#}
{# <div class="news-content">#}
{# <h4 class="news-title"><a#}
{# href="#">linux查找文件或目录命令</a>#}
{# </h4>#}
{# <p class="news-details">linux查找文件或目录命令,前提:知道文件或者目录的具体名字,例如:sphinx.conffind 查找find / -name#}
{# dirname 查找目录find -name...</p>#}
{# <div class="news-other">#}
{# <span class="news-type">Linux教程</span>#}
{# <span class="news-time">11/11 18:24</span>#}
{# <span class="news-author">python</span>#}
{# </div>#}
{# </div>#}
{# </li>#}
{##}
{# <li class="news-item">#}
{# <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"#}
{# target="_blank">#}
{# <img src="{% static 'img/linux.jpg' %}" alt="linux下svn命令的使用"#}
{# title="linux下svn命令的使用">#}
{# </a>#}
{# <div class="news-content">#}
{# <h4 class="news-title"><a#}
{# href="https://www.shiguangkey.com/course/2432/887">linux下svn命令的使用</a>#}
{# </h4>#}
{# <p class="news-details">1、将文件checkout到本地目录svn checkout path(path是服务器上的目录) 例如:svn checkout#}
{# svn://192.168.1.1/pro/domain 简写:svn co2、往版本库中添加新的文件 svn addfile 例如:svn add te...</p>#}
{# <div class="news-other">#}
{# <span class="news-type">Linux教程</span>#}
{# <span class="news-time">11/11 18:24</span>#}
{# <span class="news-author">python</span>#}
{# </div>#}
{# </div>#}
{# </li>#}
{##}
{# <li class="news-item">#}
{# <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"#}
{# target="_blank">#}
{# <img src="{% static 'img/linux.jpg' %}" alt="实现linux和windows文件传输"#}
{# title="实现linux和windows文件传输">#}
{# </a>#}
{# <div class="news-content">#}
{# <h4 class="news-title"><a#}
{# href="https://www.shiguangkey.com/course/2432/886">实现linux和windows文件传输</a>#}
{# </h4>#}
{# <p class="news-details">#}
{# 其实这个题目有点大,这里介绍的只是linux和windows文件传输中的一种,但是这种方法却非常实用,那就是:ZModem协议具体是linux命令是:rz...</p>#}
{# <div class="news-other">#}
{# <span class="news-type">Linux教程</span>#}
{# <span class="news-time">11/11 18:24</span>#}
{# <span class="news-author">python</span>#}
{# </div>#}
{# </div>#}
{# </li>#}
{##}
{# <li class="news-item">#}
{# <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"#}
{# target="_blank">#}
{# <img src="{% static 'img/linux.jpg' %}" alt=".htaccess配置详解"#}
{# title=".htaccess配置详解">#}
{# </a>#}
{# <div class="news-content">#}
{# <h4 class="news-title"><a#}
{# href="https://www.shiguangkey.com/course/2432">.htaccess配置详解</a>#}
{# </h4>#}
{# <p class="news-details"> .htaccess文件设置基础教程 如果你设置好了比如常用的404页面 301重定向 页面还有500页面等会设置了#}
{# 无非对你的seo技术有很大帮助那么 .htaccess文件到底怎么设置呢 - .htaccess 文件(或者"分布式...</p>#}
{# <div class="news-other">#}
{# <span class="news-type">Linux教程</span>#}
{# <span class="news-time">11/11 18:24</span>#}
{# <span class="news-author">python</span>#}
{# </div>#}
{# </div>#}
{# </li>#}
{##}
{# <li class="news-item">#}
{# <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"#}
{# target="_blank">#}
{# <img src="{% static 'img/linux.jpg' %}" alt="使用nohup命令让linux程序后台运行"#}
{# title="使用nohup命令让linux程序后台运行">#}
{# </a>#}
{# <div class="news-content">#}
{# <h4 class="news-title"><a#}
{# href="https://www.shiguangkey.com/course/2432">使用nohup命令让linux程序后台运行</a>#}
{# </h4>#}
{# <p class="news-details">使用nohup让程序永远后台运行Unix/Linux下一般比如想让某个程序在后台运行,很多都是使用 &#}
{# 在程序结尾来让程序自动运行。比如我们要运行mysql在后台:/usr/local/mysql/bin/mysqld_safe --user=mysql &但是...</p>#}
{# <div class="news-other">#}
{# <span class="news-type">Linux教程</span>#}
{# <span class="news-time">11/11 18:24</span>#}
{# <span class="news-author">python</span>#}
{# </div>#}
{# </div>#}
{# </li>#}
</ul>
</div>
<!-- news-contain end -->
<!-- btn-more start -->
{# <a href="javascript:void(0);" class="btn-more">加载更多</a>#}
<!-- btn-more end -->
</div>
<!-- content end -->
</div>
{% endblock %}
<!-- main-contain end -->
<!-- main end -->
<!-- footer start -->
<!-- footer end -->
{% block domready %}
<script src="{% static 'js/commons.js' %}"></script>
<script src="{% static 'js/news/index.js' %}"></script>
{% endblock %}
static/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>'))
}
}
}
});
// 新闻轮播图功能
fn_load_banner();
/*=== bannerStart ===*/
let $banner = $('.banner');
let $picLi = $(".banner .pic li");
let $prev = $('.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);
});
// 点击切换上一张
$prev.click(function () {
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);
}).mousedown(function () {
return false
});
$next.click(function () {
auto();
}).mousedown(function () {
return false
});
// 图片向前滑动
function auto() {
index++;
index %= $tabLi.length;
$tabLi.eq(index).addClass('active').siblings('li').removeClass('active');
$picLi.eq(index).fadeIn(3000).siblings('li').fadeOut(3000);
}
// 定时器
let timer = setInterval(auto, 2000);
$banner.hover(function () {
clearInterval(timer)
}, function () {
auto();
});
/*=== bannerEnd ===*/
// 定义向后端获取新闻列表数据的请求
function fn_load_content() {
// let sCurrentTagId = $('.active a').attr('data-id');
// 创建请求参数
let sDataParams = {
"tag_id": sCurrentTagId,
"page": iPage
};
// 创建ajax请求
$.ajax({
// 请求地址
url: "/news/", // url尾部需要添加/
// 请求方式
type: "GET",
data: sDataParams,
// 响应数据的格式(后端返回给前端的格式)
dataType: "json",
})
.done(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="/news/${one_news.id}/" 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="/news/${one_news.id}">${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('服务器超时,请重试!');
});
}
function fn_load_banner() {
$.ajax({
// 请求地址
url: "/news/banners/", // url尾部需要添加/
// 请求方式
type: "GET",
async: false
})
.done(function (res) {
if (res.errno === "0") {
let content = ``;
let tab_content = ``;
res.data.banners.forEach(function (one_banner, index) {
if (index === 0){
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(function () {
message.showError('服务器超时,请重试!');
});
}
});
templates/news/news_detail.html
{% extends 'base/base.html' %}
{% load static %}
{% block meta %}
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
{% endblock %}
{% block title %}<title>news-detail</title>{% endblock %}
{% block link %}<link rel="stylesheet" href="{% static 'css/news/news-detail.css' %}">{% endblock %}
{% block contain %}
<div class="news-contain">
<h1 class="news-title">{{ news.title }}</h1>
<div class="news-info">
<div class="news-info-left">
<span class="news-author">{{ news.author.username }}</span>
<span class="news-pub-time">{{ news.update_time }}</span>
<span class="news-type">{{ news.tag.name }}</span>
</div>
</div>
<article class="news-content">
{# {{ news.content}}#}
{{ news.content|safe }}
</article>
<div class="comment-contain">
<div class="comment-pub clearfix">
<div class="new-comment">
文章评论(<span class="comment-count">0</span>)
</div>
<div class="comment-control please-login-comment" style="display:none;">
<input type="text" placeholder="请登录后参加评论">
</div>
<div class="comment-control logged-comment">
<input type="text" placeholder="请填写评论">
</div>
<button class="comment-btn">发表评论</button>
</div>
<ul class="comment-list">
<li class="comment-item">
<div class="comment-info clearfix">
<img src="{% static 'img/avatar.jpeg' %}" alt="avatar" class="comment-avatar">
<span class="comment-user">评论人</span>
<span class="comment-pub-time">1小时前</span>
</div>
<div class="comment-content">这是一条评论</div>
</li>
<li class="comment-item">
<div class="comment-info clearfix">
<img src="{% static 'img/avatar.jpeg' %}" alt="avatar" class="comment-avatar">
<span class="comment-user">评论人</span>
<span class="comment-pub-time">1小时前</span>
</div>
<div class="comment-content">这是一条评论</div>
</li>
</ul>
</div>
</div>
<!-- side end -->
{% endblock %}