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

栏目: Python · 发布时间: 5年前

内容简介:在本讲中,我们开始详情页功能的开发,详情页就是对单个视频进行播放并展示视频的相关信息,比如视频标题、描述、评论信息、相关推荐等。我们将会学习到通用视图类DetailView的使用、评论动态加载、以及如何通过ajax实现喜欢和收藏功能,并通过一段段很酷的代码来说明这些功能。大家可先通过网站演示地址 浏览一下网站效果。点击某个视频即可浏览详情页。详情页实现了是对单个视频进行展示,用户可看到视频的一些元信息,包括标题、描述、观看次数、喜欢数、收藏数等等。另外,网站还实现了评论功能,通过上拉网页即可分页加载评论列表

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

效果展示

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

整体功能

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

我们把详情页分为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
复制代码

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

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

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

收藏和喜欢功能

收藏和喜欢是一组动作,因此可以用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和评论列表。

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

评论功能是一个独立的模块,该功能通用性较高,在其他很多网站中都有评论功能,为了避免以后开发其他网站时重复造轮子,我们建立一个新的应用,命名为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
    })
复制代码

以上所述就是小编给大家介绍的《基于django的视频点播网站开发-step5-详情页功能》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Blockchain Basics

Blockchain Basics

Daniel Drescher / Apress / 2017-3-16 / USD 20.99

In 25 concise steps, you will learn the basics of blockchain technology. No mathematical formulas, program code, or computer science jargon are used. No previous knowledge in computer science, mathema......一起来看看 《Blockchain Basics》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换