基于django的视频点播网站开发-step5-详情页功能

本文涉及的产品
视频点播 VOD,流量+存储+转码
简介: 在本讲中,我们开始详情页功能的开发,详情页就是对单个视频进行播放并展示视频的相关信息,比如视频标题、描述、评论信息、相关推荐等。我们将会学习到通用视图类DetailView的使用、评论动态加载、以及如何通过ajax实现喜欢和收藏功能,并通过一段段很酷的代码来说明这些功能。

在本讲中,我们开始详情页功能的开发,详情页就是对单个视频进行播放并展示视频的相关信息,比如视频标题、描述、评论信息、相关推荐等。我们将会学习到通用视图类DetailView的使用、评论动态加载、以及如何通过ajax实现喜欢和收藏功能,并通过一段段很酷的代码来说明这些功能。

效果展示

整体功能

大家可先通过 网站演示地址 浏览一下网站效果。点击某个视频即可浏览详情页。详情页实现了是对单个视频进行展示,用户可看到视频的一些元信息,包括标题、描述、观看次数、喜欢数、收藏数等等。另外,网站还实现了评论功能,通过上拉网页即可分页加载评论列表,用户还能添加评论。网页侧栏是推荐视频列表,这里使用的推荐逻辑比较简单,就是推荐观看次数最多的视频。

我们把详情页分为4个小的业务模块来开发,分别是:视频详情显示、喜欢和收藏功能、评论功能、推荐功能。下面我们分别对这四个功能模块进行开发讲解。

视频详情显示

因为在上一讲中,我们已经建立了video模型,所以不必再新建模型,我们就在video模型的基础上进行扩展。上一讲,我们创建的字段有title、desc、classification、file、cover、status、create_time。这些字段目前是不够用的,我们再加几个字段,需要加观察次数喜欢的用户收藏的用户。video模型扩展后如下

class Video(models.Model):
    STATUS_CHOICES = (
        ('0', '发布中'),
        ('1', '未发布'),
    )
    title = models.CharField(max_length=100,blank=True, null=True)
    desc = models.CharField(max_length=255,blank=True, null=True)
    classification = models.ForeignKey(Classification, on_delete=models.CASCADE, null=True)
    file = models.FileField(max_length=255)
    cover = models.ImageField(upload_to='cover/',blank=True, null=True)
    status = models.CharField(max_length=1 ,choices=STATUS_CHOICES, blank=True, null=True)
    view_count = models.IntegerField(default=0, blank=True)
    liked = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                   blank=True, related_name="liked_videos")
    collected = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                   blank=True, related_name="collected_videos")
    create_time = models.DateTimeField(auto_now_add=True, blank=True, max_length=20)

新增了3个字段

  • view_count 观看次数。数据类型是IntegerField,默认是0
  • liked 喜欢的用户。数据类型是ManyToManyField,这是一种多对多的关系,表示一个视频可以被多个用户喜欢,一个用户也可以喜欢多个视频。记得设置用户表为settings.AUTH_USER_MODEL
  • collected 收藏的用户。数据类型是ManyToManyField,这是一种多对多的关系,表示一个视频可以被多个用户收藏,一个用户也可以收藏多个视频。设置用户表为settings.AUTH_USER_MODEL

更多关于ManyToManyField的使用介绍,可以查询django官网的介绍。

下面就是详情展示阶段,我们先配置好详情页的路由信息,在video/urls.py中追加detail的路由信息。


app_name = 'video'
urlpatterns = [
    path('index', views.IndexView.as_view(), name='index'),
    path('search/', views.SearchListView.as_view(), name='search'),
    path('detail/<int:pk>/', views.VideoDetailView.as_view(), name='detail'),
]

path('detail/<int:pk>/', views.VideoDetailView.as_view(), name='detail')即表示详情信息,注意每条视频都是有自己的主键的,所以设置路径匹配为detail/<int:pk>/,其中<int:pk>表示主键,这是django中表示主键的一种方法。这样我们就可以在浏览器输入127.0.0.1:8000/video/detail/xxx来访问详情了。

怎么显示详情呢,聪明的django为我们提供了DetailView。urls.py中设置的视图类是VideoDetailView,我们让VideoDetailView继承DetailView即可。

class VideoDetailView(generic.DetailView):
    model = Video
    template_name = 'video/detail.html' 

看起来超级简单,django就是如此的酷,只需要我们配置几行代码,就能实现很强大的功能。这里我们配置model为Video模型,模板为video/detail.html,其它的工作都不用管,全都交给django去干,oh,这棒极了。

模板文件位于templates/video/detail.html,它的代码比较简单,这里就不贴了。

从效果图上我们看到还有个观看次数的展示,这里的观看次数本质上就是数据库里的一个自增字段,每次观看的时候,view_count自动加1。对于这个小需求,我们需要做两件事情,首先这video模型里面,添加一个次数自增函数,命名为increase_view_count,这很简单,如下所示:

    def increase_view_count(self):
        self.view_count += 1
        self.save(update_fields=['view_count'])

然后,还需要我们在VideoDetailView视图类里面调用到这个函数。这个时候get_object()派上用场了。因为每次调用DetailView的时候,django都会回调get_object()这个函数。因此我们可以把increase_view_count()放到get_object()里面执行。完美的代码如下

class VideoDetailView(generic.DetailView):
    model = Video
    template_name = 'video/detail.html'

    def get_object(self, queryset=None):
        obj = super().get_object()
        obj.increase_view_count()  # 调用自增函数
        return obj

目前为止,我们就能在详情页看到标题、描述、观看次数、收藏次数、喜欢次数。预览如下

虽然可以显示收藏人数、喜欢人数。但是目前还没实现点击喜欢/收藏的功能。下面我们来实现。

收藏和喜欢功能

收藏和喜欢是一组动作,因此可以用ajax来实现:用户点击后调用后端接口,接口返回json数据,前端显示结果。

既然需要接口,那我们先添加喜欢/收藏接口的路由,在video/urls.py追加代码如下

path('like/', views.like, name='like'),
path('collect/', views.collect, name='collect'),

由于喜欢和收藏的功能实现非常类似,限于篇幅,我们只实现喜欢功能。

我们先写like函数:

@ajax_required
@require_http_methods(["POST"])
def like(request):
    if not request.user.is_authenticated:
        return JsonResponse({"code": 1, "msg": "请先登录"})
    video_id = request.POST['video_id']
    video = Video.objects.get(pk=video_id)
    user = request.user
    video.switch_like(user)
    return JsonResponse({"code": 0, "likes": video.count_likers(), "user_liked": video.user_liked(user)})

首先判断用户是否登录,如果登录了则调用switch_like(user)来实现喜欢或不喜欢功能,最后返回json。注意这里添加了两个注解@ajax_required@require_http_methods(["POST"]),分别验证request必须是ajax和post请求。

switch_like()函数则写在了video/model.py里面

    def switch_like(self, user):
        if user in self.liked.all():
            self.liked.remove(user)
        else:
            self.liked.add(user)

所有的后端工作都准备好了,我们再把视线转向前端。前端主要是写ajax代码。

由于ajax代码量较大,我们封装到一个单独的js文件中 ==> static/js/detail.js

在detail.js中,我们先实现喜欢的ajax调用:

$(function () {

    // 写入csrf
    $.getScript("/static/js/csrftoken.js");

    // 喜欢
    $("#like").click(function(){
      var video_id = $("#like").attr("video-id");
      $.ajax({
            url: '/video/like/',
            data: {
                video_id: video_id,
                'csrf_token': csrftoken
            },
            type: 'POST',
            dataType: 'json',
            success: function (data) {
                var code = data.code
                if(code == 0){
                    var likes = data.likes
                    var user_liked = data.user_liked
                    $('#like-count').text(likes)
                    if(user_liked == 0){
                        $('#like').removeClass("grey").addClass("red")
                    }else{
                        $('#like').removeClass("red").addClass("grey")
                    }
                }else{
                    var msg = data.msg
                    alert(msg)
                }

            },
            error: function(data){
              alert("点赞失败")
            }
        });
    });

上述代码中,关键代码是$.ajax()函数,我们传入了参数:video_idcsrftoken。其中csrftoken可通过/static/js/csrftoken.js生成。在success回调中,通过判断user_liked的值来确定自己是否喜欢过,然后改变模板中相应的css。

推荐功能

每个网站都有自己的推荐功能,且都有自己的推荐逻辑。我们视点的推荐逻辑是根据访问次数最高的n个视频来降序排序,然后推荐给用户的。

实现起来非常容易,我们知道详情页实现用的是VideoDetailView,我们可以在get_context_data()中把推荐内容传递给前端模板。只需要我们改写VideoDetailView的get_context_date()函数。

    def get_context_data(self, **kwargs):
        context = super(VideoDetailView, self).get_context_data(**kwargs)
        form = CommentForm()
        recommend_list = Video.objects.get_recommend_list()
        context['form'] = form
        context['recommend_list'] = recommend_list
        return context

改写后,我们添加了一行

recommend_list = Video.objects.get_recommend_list()

我们把获取推荐列表的函数get_recommend_list()封装到了Video模型里面。在Video/models.py里面
我们追加代码:

class VideoQuerySet(models.query.QuerySet):
    def get_recommend_list(self):
        return self.filter(status=0).order_by('-view_count')[:4]

关键是self.filter(status=0).order_by('-view_count')[:4],通过order_by把view_count降序排序,并选取前4条数据。

注意此处我们用了VideoQuerySet查询器,需要我们在Video下面添加一行依赖。表示用VideoQuerySet作为Video的查询管理器。

objects = VideoQuerySet.as_manager()

当模板拿到数据后,即可渲染显示。这里我们将推荐侧栏的代码封装到templates/video/recommend.html里面。

# templates/video/recommend.html
{% load thumbnail %}
<span class="video-side-title">推荐列表</span>
<div class="ui unstackable divided items">
    {% for item in recommend_list %}
    <div class="item">
        <div class="ui tiny image">
            {% thumbnail item.cover "300x200" crop="center" as im %}
            <img class="ui image" src="{{ im.url }}">
            {% empty %}
            {% endthumbnail %}
        </div>
        <div class="middle aligned content">
            <a class=" header-title" href="{% url 'video:detail' item.pk %}">{{ item.title }}</a>
            <div class="meta">
                <span class="description">{{ item.view_count }}次观看</span>
            </div>
        </div>
    </div>
    {% empty %}
    <h3>暂无推荐</h3>
    {% endfor %}

</div>

并在detail.html中将它包含进来

{% include "video/recommend.html" %}

评论功能

评论区位于详情页下侧,显示效果如下。共分为两个部分:评论form和评论列表。

评论功能是一个独立的模块,该功能通用性较高,在其他很多网站中都有评论功能,为了避免以后开发其他网站时重复造轮子,我们建立一个新的应用,命名为comment

python3 manage.py startapp comment

接下来,我们创建comment模型

# 位于comment/models.py

class Comment(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    nickname = models.CharField(max_length=30,blank=True, null=True)
    avatar = models.CharField(max_length=100,blank=True, null=True)
    video = models.ForeignKey(Video, on_delete=models.CASCADE)
    content = models.CharField(max_length=100)
    timestamp = models.DateTimeField(auto_now_add=True) 

    class Meta:
        db_table = "v_comment"
  • user 用户。数据类型是ForeignKey,外键是settings.AUTH_USER_MODEL,并设置为级联删除on_delete=models.CASCADE
  • nickname 用户昵称。数据类型是CharField。
  • avatar 头像。数据类型是CharField。
  • video 对应的视频。数据类型是ForeignKey,对应Video模型,级联删除 on_delete=models.CASCADE
  • content 评论内容。 数据类型是CharField。
  • timestamp 评论时间。 数据类型是DateTimeField。

有了模型之后,我们就可以专心写业务代码了,首先在comment下建立路由文件urls.py。并写入代码:

from django.urls import path
from . import views

app_name = 'comment'
urlpatterns = [
    path('submit_comment/<int:pk>',views.submit_comment, name='submit_comment'),
    path('get_comments/', views.get_comments, name='get_comments'),
]

我们配置了两条路由信息:评论提交 和 获取评论。

提交评论,需要一个form,我们把form放到video/forms.py

from django import forms
from comment.models import Comment

class CommentForm(forms.ModelForm):
    content = forms.CharField(error_messages={'required': '不能为空',},
        widget=forms.Textarea(attrs = {'placeholder': '请输入评论内容' })
    )

    class Meta:
        model = Comment
        fields = ['content']

然后在video/views.py的VideoDetailView下添加form的相关代码。

class VideoDetailView(generic.DetailView):
    model = Video
    template_name = 'video/detail.html'

    def get_object(self, queryset=None):
        obj = super().get_object()
        obj.increase_view_count()
        return obj

    def get_context_data(self, **kwargs):
        context = super(VideoDetailView, self).get_context_data(**kwargs)
        form = CommentForm() 
        context['form'] = form 
        return context

在get_context_data()函数里面,我们把form传递给模板。

同样的,提交评论也是异步的,我们用ajax实现,我们打开static/js/detail.js,写入

    // 提交评论
    var frm = $('#comment_form')
    frm.submit(function () {
        $.ajax({
            type: frm.attr('method'),
            url: frm.attr('action'),
            dataType:'json',
            data: frm.serialize(),
            success: function (data) {
                var code = data.code
                var msg = data.msg
                if(code == 0){
                    $('#id_content').val("")
                    $('.comment-list').prepend(data.html);
                    $('#comment-result').text("评论成功")
                    $('.info').show().delay(2000).fadeOut(800)
                }else{
                    $('#comment-result').text(msg)
                    $('.info').show().delay(2000).fadeOut(800);
                }
            },
            error: function(data) {
            }
        });
        return false;
    });

评论通过ajax提交后,我们在submit_comment()中就能接收到这个请求。处理如下

def submit_comment(request,pk):
    video = get_object_or_404(Video, pk = pk)
    form = CommentForm(data=request.POST)

    if form.is_valid():
        new_comment = form.save(commit=False)
        new_comment.user = request.user
        new_comment.nickname = request.user.nickname
        new_comment.avatar = request.user.avatar
        new_comment.video = video
        new_comment.save()

        data = dict()
        data['nickname'] = request.user.nickname
        data['avatar'] = request.user.avatar
        data['timestamp'] = datetime.fromtimestamp(datetime.now().timestamp())
        data['content'] = new_comment.content

        comments = list()
        comments.append(data)

        html = render_to_string(
            "comment/comment_single.html", {"comments": comments})

        return JsonResponse({"code":0,"html": html})
    return JsonResponse({"code":1,'msg':'评论失败!'})

在接收函数中,通过form自带的验证函数来保存记录,然后将这条记录返回到前端模板。

下面我们开始评论列表的开发。

评论列表部分,我们使用了的是上拉动态加载的方案,即当页面拉到最下侧时,js加载代码会自动的获取下一页的数据并显示出来。前端部分,我们使用了一种基于js的开源加载插件。基于这个插件,可以很容易实现网页的上拉动态加载效果。它使用超级简单,仅需要调用$('.comments').dropload({})即可。我们把调用的代码封装在static/js/load_comments.js里面。

完整的调用代码如下:

$(function(){
    // 页数
    var page = 0;
    // 每页展示15个
    var page_size = 15;

    // dropload
    $('.comments').dropload({
        scrollArea : window,
        loadDownFn : function(me){
            page++;

            $.ajax({
                type: 'GET',
                url: comments_url,
                data:{
                     video_id: video_id,
                     page: page,
                     page_size: page_size
                },
                dataType: 'json',
                success: function(data){
                    var code = data.code
                    var count = data.comment_count
                    if(code == 0){
                        $('#id_comment_label').text(count + "条评论");
                        $('.comment-list').append(data.html);
                        me.resetload();
                    }else{
                        me.lock();
                        me.noData();
                        me.resetload();
                    }
                },
                error: function(xhr, type){
                    me.resetload();
                }
            });
        }
    });
});

不用过多的解释,这段代码已经非常非常清晰了,本质还是ajax的接口请求调用,调用后返回结果更新前端网页内容。

我们看到ajax调用的接口是get_comments,我们继续来实现它,它位于comment/views.py中。代码如下所示,这段代码也很简单,没有什么复杂的技术。当获取到page和page_size后,使用paginator对象来实现分页。最后通过render_to_string将html传递给模板。

def get_comments(request):
    if not request.is_ajax():
        return HttpResponseBadRequest()
    page = request.GET.get('page')
    page_size = request.GET.get('page_size')
    video_id = request.GET.get('video_id')
    video = get_object_or_404(Video, pk=video_id)
    comments = video.comment_set.order_by('-timestamp').all()
    comment_count = len(comments)

    paginator = Paginator(comments, page_size)
    try:
        rows = paginator.page(page)
    except PageNotAnInteger:
        rows = paginator.page(1)
    except EmptyPage:
        rows = []

    if len(rows) > 0:
        code = 0
        html = render_to_string(
            "comment/comment_single.html", {"comments": rows})
    else:
        code = 1
        html = ""

    return JsonResponse({
        "code":code,
        "html": html,
        "comment_count": comment_count
    })
目录
相关文章
|
6月前
|
存储 数据库 文件存储
掌握Django文件处理:一步步构建上传功能
文件上传算是一种很常见的需求,几乎构建很多项目系统,以及插件都需要用到。 通过上面例子可以看到django通过forms表单的形式灵活定义文件上传的页面,字段,以及数据库存储。 在实际项目中我们还要考虑到安全性,包括检查文件大小(可通过FileField的max_length属性设置)、防止路径遍历攻击。 还需注意服务器端的验证,比如检查文件类型、内容安全等。
|
2月前
|
前端开发 JavaScript UED
探索Python Django中的WebSocket集成:为前后端分离应用添加实时通信功能
通过在Django项目中集成Channels和WebSocket,我们能够为前后端分离的应用添加实时通信功能,实现诸如在线聊天、实时数据更新等交互式场景。这不仅增强了应用的功能性,也提升了用户体验。随着实时Web应用的日益普及,掌握Django Channels和WebSocket的集成将为开发者开启新的可能性,推动Web应用的发展迈向更高层次的实时性和交互性。
92 1
|
1月前
|
程序员 API 数据库
Django/Flask深度揭秘:揭秘那些让程序员爱不释手的神奇功能!
在Web开发领域,Django与Flask凭借其独特魅力和强大功能深受程序员喜爱。Django作为全能型框架,以其ORM、模板引擎和丰富的内置功能著称;Flask则以轻量级、灵活的路由系统和强大的扩展生态见长。两者各具特色,为开发者提供了高效、灵活的开发工具。
40 4
|
3月前
|
前端开发 搜索推荐 算法
中草药管理与推荐系统Python+Django网页界面+推荐算法+计算机课设系统+网站开发
中草药管理与推荐系统。本系统使用Python作为主要开发语言,前端使用HTML,CSS,BootStrap等技术和框架搭建前端界面,后端使用Django框架处理应用请求,使用Ajax等技术实现前后端的数据通信。实现了一个综合性的中草药管理与推荐平台。具体功能如下: - 系统分为普通用户和管理员两个角色 - 普通用户可以登录,注册、查看物品信息、收藏物品、发布评论、编辑个人信息、柱状图饼状图可视化物品信息、并依据用户注册时选择的标签进行推荐 和 根据用户对物品的评分 使用协同过滤推荐算法进行推荐 - 管理员可以在后台对用户和物品信息进行管理编辑
93 12
中草药管理与推荐系统Python+Django网页界面+推荐算法+计算机课设系统+网站开发
|
2月前
|
存储 Python
使用django构建一个多级评论功能
使用django构建一个多级评论功能
25 0
|
4月前
|
存储 关系型数据库 MySQL
基于python django 医院管理系统,多用户功能,包括管理员、用户、医生,数据库MySQL
本文介绍了一个基于Python Django框架开发的医院管理系统,该系统设计了管理员、用户和医生三个角色,具备多用户功能,并使用MySQL数据库进行数据存储和管理。
178 4
基于python django 医院管理系统,多用户功能,包括管理员、用户、医生,数据库MySQL
|
5月前
|
存储 NoSQL 中间件
【Django+Vue3 线上教育平台项目实战】登录功能模块之短信登录与钉钉三方登录
在当今的数字化时代,用户认证是任何在线服务安全性的基石。本文将简明扼要地介绍登录注册流程中的核心概念:HTTP无状态性、Session、Token与JWT,并详细阐述两种实用登录方式—— 手机号登录验证(借助容联云/云通讯服务) 与钉钉第三方登录。我们将探讨这些概念的基本原理,并深入解析两种登录方式的实现流程,旨在帮助开发者提升用户认证的安全性与便捷性。
【Django+Vue3 线上教育平台项目实战】登录功能模块之短信登录与钉钉三方登录
|
5月前
|
前端开发 JavaScript API
【Django+Vue3 线上教育平台项目实战】构建课程详情页与集成视频播放功能
随着数字化教育的兴起,构建一个高效、用户友好的线上教育平台至关重要。本文将探讨如何使用Django与Vue.js 3结合,实现一个包含课程列表和课程详情页(含视频播放功能)的线上教育平台部分。本文主要介绍了如何设计数据库模型、处理数据查询、构建动态前端界面,并集成视频播放功能,为用户带来流畅的学习体验。
【Django+Vue3 线上教育平台项目实战】构建课程详情页与集成视频播放功能
|
4月前
|
数据可视化 安全 前端开发
基于Django的美团药品数据分析与可视化系统,有多用户功能,可增删改查数据
本文介绍了一个基于Django框架开发的美团药品数据分析与可视化系统,该系统具备多用户功能,支持数据的增删改查操作,并采用MySQL、pandas、echarts和bootstrap技术栈,为用户提供了一个高效、安全且实用的药品数据管理和分析平台。
基于Django的美团药品数据分析与可视化系统,有多用户功能,可增删改查数据
|
5月前
|
存储 数据库 文件存储
掌握Django文件处理:一步步构建上传功能
掌握Django文件处理:一步步构建上传功能
67 3