天天看點

基于django的視訊點播網站開發-step9-背景視訊管理功能

從本講開始,我們開始視訊管理功能的開發,視訊管理包括視訊上傳、視訊清單、視訊編輯、視訊删除。另外還有視訊分類的功能,會一同講解。這一講非常重要,因為你将學習到一些之前沒有學過的技術,比如大檔案上傳技術。

視訊上傳

我們先來實作視訊的上傳,視訊的上傳采用的是分塊上傳的政策,并用了分塊上傳類庫:django_chunked_upload,使用該類庫,再配合前端上傳js庫(jquery.fileupload.js),即可完美的實作檔案的分塊上傳功能。

照例先編寫添加視訊的路由

添加視訊,當然需要上傳視訊的頁面,我們的頁面是video_add路由來顯示,通過urls .py中指定

path('video_add/', views.AddVideoView.as_view(), name='video_add'),           

AddViewView僅僅用來顯示上傳頁面,它的代碼很簡單

class AddVideoView(SuperUserRequiredMixin, TemplateView):
    template_name = 'myadmin/video_add.html'           

隻是繼承了TemplateView來顯示myadmin/video_add.html

myadmin/video_add.html中實作了上傳視訊的全過程,視訊的上傳采用的是分塊上傳的政策,前端使用的是js上傳庫(jquery.fileupload.js),後端使用的是django_chunked_upload,上傳的邏輯是這樣的:前端先選擇一個檔案,通過jquery.fileupload.js中的$.fileupload()方法來上傳檔案,後端接收到後分批傳回已上傳塊的進度,前端根據進度來更新界面。由于上傳前需要做一些校驗的操作,代碼較複雜,是以我們把上傳的代碼封裝到了一個js中:static/js/myadmin/video_upload.js,主要的代碼如下:

$("#chunked_upload").fileupload({
  url: api_chunked_uplad,
  dataType: "json",
  maxChunkSize: 100000, // Chunks of 100 kB
  formData: form_data,
  add: function(e, data) { // Called before starting upload
    var fileSize = data.originalFiles[0]['size'];
    var type = data.originalFiles[0]['type']; 

    if(fileSize > 100000000){
        alert('檔案太大了,請上傳100M以内的檔案');
        return;
    }

    if(!type.startsWith("video/")){
        alert('視訊格式不正确');
        return;
    }
 
    form_data.splice(1);
    calculate_md5(data.files[0], 100000);  // Again, chunks of 100 kB
    data.submit();

    $('#progress_label').on('click', false);
    $('#progress_layout').show()

  },
  chunkdone: function (e, data) { // Called after uploading each chunk
    if (form_data.length < 2) {
      form_data.push(
        {"name": "upload_id", "value": data.result.upload_id}
      );
    }
    var progress = parseInt(data.loaded / data.total * 100.0, 10);
    console.log(progress);
    if(progress > lastprogress){
        lastprogress = progress
        $('#upload_progress').progress({
            percent: progress
        });
    }
  },
  done: function (e, data) { // Called when the file has completely uploaded
    $.ajax({
      type: "POST",
      url: api_chunked_upload_complete,
      data: {
        csrfmiddlewaretoken: csrf,
        upload_id: data.result.upload_id,
        md5: md5
      },
      dataType: "json",
      success: function(data) {
        console.log(data)
        $('#upload_label').text('上傳成功');
        $('#upload_progress').progress({
            percent: 100
        });
        $('#next_layout').show();
        $('#next').click(function(){
            window.location = '/myadmin/video_publish/' + data.video_id
        });
      }
    });
  },
});           

在$.fileupload()方法中,有一個回調方法chunkdone(),該方法是用來更新進度的,告訴前端已經上傳了多少位元組。另外還有一個回調方法done(),該方法表示上傳完畢,前端可在裡面做一些額外的事情。

上傳完畢後,調用了一個接口api_chunked_upload_complete,來給後端發送一個回執:我已上傳完畢。

api_chunked_upload和api_chunked_upload_complete的路由是

path('chunked_upload/',  views.MyChunkedUploadView.as_view(), name='api_chunked_upload'),
path('chunked_upload_complete/', views.MyChunkedUploadCompleteView.as_view(),name='api_chunked_upload_complete'),           

在MyChunkedUploadCompleteView中,我們在利用Video模型建立了這條視訊

class MyChunkedUploadCompleteView(ChunkedUploadCompleteView):
    model = MyChunkedUpload
 
    def get_response_data(self, chunked_upload, request):
        video = Video.objects.create(file=chunked_upload.file)
        return {'code': 0, 'video_id': video.id, 'msg': 'success'}
           

上傳完畢效果如下

然後使用者點選下一步,進入video_publish頁面,開始釋出前的資料填寫

video_publish的路由是

path('video_publish/<int:pk>/', views.VideoPublishView.as_view(), name='video_publish'),           

video_publish的視圖類是VideoPublishView,它的代碼如下

class VideoPublishView(SuperUserRequiredMixin, generic.UpdateView):
    model = Video
    form_class = VideoPublishForm
    template_name = 'myadmin/video_publish.html'

    def get_context_data(self, **kwargs):
        context = super(VideoPublishView, self).get_context_data(**kwargs)
        clf_list = Classification.objects.all().values()
        clf_data = {'clf_list':clf_list}
        context.update(clf_data)
        return context

    def get_success_url(self):
        return reverse('myadmin:video_publish_success')           

對應的頁面是myadmin/video_publish.html

就是下面這個頁面

要填寫的視訊資料有視訊标題、描述、分類、封面,

其中分類是通過get_context_data()帶過來的,

填寫後,點選釋出,django将通過UpdateView自動為你更新視訊資訊。并通過get_success_url跳轉到成功頁面myadmin:video_publish_success,它的路由是

path('video_publish_success/', views.VideoPublishSuccessView.as_view(), name='video_publish_success'),           

對應VideoPublishSuccessView是

class VideoPublishSuccessView(generic.TemplateView):
    template_name = 'myadmin/video_publish_success.html'           

如下

我們點選視訊清單即可檢視視訊

視訊清單

視訊清單的路由是

path('video_list/', views.VideoListView.as_view(), name='video_list'),           

對應的視圖類是VideoListView

class VideoListView(AdminUserRequiredMixin, generic.ListView):
    model = Video
    template_name = 'myadmin/video_list.html'
    context_object_name = 'video_list'
    paginate_by = 10
    q = ''

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(VideoListView, self).get_context_data(**kwargs)
        paginator = context.get('paginator')
        page = context.get('page_obj')
        page_list = get_page_list(paginator, page)
        context['page_list'] = page_list
        context['q'] = self.q
        return context

    def get_queryset(self):
        self.q = self.request.GET.get("q", "")
        return Video.objects.get_search_list(self.q)
           

這裡繼承了ListView來顯示視訊清單,并通過get_queryset實作了搜尋功能,通過get_context_data()實作了分頁功能。

最後展示效果如下

你可能會發現,頁面中還有編輯和删除的功能。編輯呢,是對單個視訊對資料進行更新,删除即删除本條視訊和視訊檔案。

視訊編輯

我們先實作編輯功能,路由是

path('video_edit/<int:pk>/', views.VideoEditView.as_view(), name='video_edit'),           

對應對視圖類是VideoEditView,這個視圖類是需要傳遞主鍵的。

class VideoEditView(SuperUserRequiredMixin, generic.UpdateView):
    model = Video
    form_class = VideoEditForm
    template_name = 'myadmin/video_edit.html'

    def get_context_data(self, **kwargs):
        context = super(VideoEditView, self).get_context_data(**kwargs)
        clf_list = Classification.objects.all().values()
        clf_data = {'clf_list':clf_list}
        context.update(clf_data)
        return context

    def get_success_url(self):
        messages.success(self.request, "儲存成功")
        return reverse('myadmin:video_edit', kwargs={'pk': self.kwargs['pk']})           

其實編輯頁面和釋出頁面很相似,都是繼承UpdateView視圖類,并在get_context_data()裡面傳遞分類資訊。最終成功後通過

messages.success(self.request, "儲存成功")

消息告之前端。

視訊删除

删除功能就更加簡單了。路由是

path('video_delete/', views.video_delete, name='video_delete'),           

這裡通過video_delete函數來實作,前端通過ajax(ajax代碼位于static/js/myadmin/video_list.js)調用這個函數。

@ajax_required
@require_http_methods(["POST"])
def video_delete(request): 
    video_id = request.POST['video_id']
    instance = Video.objects.get(id=video_id)
    instance.delete()
    return JsonResponse({"code": 0, "msg": "success"})           

擷取該視訊,然後instance.delete()删除之。

視訊分類

分類管理功能包括分類的增删改查。

增删改查的路由是

path('classification_add/', views.ClassificationAddView.as_view(), name='classification_add'),
path('classification_list/', views.ClassificationListView.as_view(), name='classification_list'),
path('classification_edit/<int:pk>/', views.ClassificationEditView.as_view(), name='classification_edit'),
path('classification_delete/', views.classification_delete, name='classification_delete'),           

先來看分類添加的功能

分類添加是通過ClassificationAddView視圖類來實作的,代碼如下

class ClassificationAddView(SuperUserRequiredMixin, generic.View):
    def get(self, request):
        form = ClassificationAddForm()
        return render(self.request, 'myadmin/classification_add.html', {'form': form})

    def post(self, request):
        form = ClassificationAddForm(data=request.POST)
        if form.is_valid():
            form.save(commit=True)
            return render(self.request, 'myadmin/classification_add_success.html')
        return render(self.request, 'myadmin/classification_add.html', {'form': form})           

此處是通過get和post一同來實作的,get()負責展示界面,post()負責邏輯判斷。在post()中,直接調用form.save來儲存記錄,然後跳轉到成功頁myadmin/classification_add_success.html。

分類添加

添加成功

然後點選視訊清單,即可檢視清單,視訊清單的視圖類是ClassificationListView,即

class ClassificationListView(AdminUserRequiredMixin, generic.ListView):
    model = Classification
    template_name = 'myadmin/classification_list.html'
    context_object_name = 'classification_list'
    paginate_by = 10
    q = ''

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(ClassificationListView, self).get_context_data(**kwargs)
        paginator = context.get('paginator')
        page = context.get('page_obj')
        page_list = get_page_list(paginator, page)
        context['page_list'] = page_list
        context['q'] = self.q
        return context

    def get_queryset(self):
        self.q = self.request.GET.get("q", "")
        return Classification.objects.filter(title__contains=self.q)           

繼承ListView來顯示清單,通過get_queryset()來實作搜尋功能,通過get_context_data()來實作分頁功能,通過template_name來指定模闆

效果如下

接着來實作編輯和删除功能。

編輯對應的視圖類是ClassificationEditView,它的實作超級簡單,繼承UpdateView即可。

class ClassificationEditView(SuperUserRequiredMixin, generic.UpdateView):
    model = Classification
    form_class = ClassificationEditForm
    template_name = 'myadmin/classification_edit.html'

    def get_success_url(self):
        messages.success(self.request, "儲存成功")
        return reverse('myadmin:classification_edit', kwargs={'pk': self.kwargs['pk']})           

編輯頁面和添加頁面很相似,這裡就不貼圖了。

最後是删除功能,是通過ajax來實作的,ajax代碼位于static/js/myadmin/classification_list.js,在ajax中,通過調用删除接口classification_delete來實作删除功能,

接口classification_delete的代碼:

@ajax_required
@require_http_methods(["POST"])
def classification_delete(request):
    classification_id = request.POST['classification_id']
    instance = Classification.objects.get(id=classification_id)
    instance.delete()
    return JsonResponse({"code": 0, "msg": "success"})           

功能略多,同學們可根據自身情況,根據

背景demo位址

的示範來一步步學習。