基於django的視頻點播網站開發-step9-後臺視頻管理功能

從本講開始,咱們開始視頻管理功能的開發,視頻管理包括視頻上傳、視頻列表、視頻編輯、視頻刪除。另外還有視頻分類的功能,會一同講解。這一講很是重要,由於你將學習到一些以前沒有學過的技術,好比大文件上傳技術。javascript

視頻上傳

咱們先來實現視頻的上傳,視頻的上傳採用的是分塊上傳的策略,並用了分塊上傳類庫:django_chunked_upload,使用該類庫,再配合前端上傳js庫(jquery.fileupload.js),便可完美的實現文件的分塊上傳功能。html

照例先編寫添加視頻的路由前端

添加視頻,固然須要上傳視頻的頁面,咱們的頁面是video_add路由來顯示,經過urls .py中指定java

path('video_add/', views.AddVideoView.as_view(), name='video_add'),
複製代碼

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

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

只是繼承了TemplateView來顯示myadmin/video_add.htmljquery

myadmin/video_add.html中實現了上傳視頻的全過程,視頻的上傳採用的是分塊上傳的策略,前端使用的是js上傳庫(jquery.fileupload.js),後端使用的是django_chunked_upload,上傳的邏輯是這樣的:前端先選擇一個文件,經過jquery.fileupload.js中的$.fileupload()方法來上傳文件,後端接收到後分批返回已上傳塊的進度,前端根據進度來更新界面。因爲上傳前須要作一些校驗的操做,代碼較複雜,因此咱們把上傳的代碼封裝到了一個js中:static/js/myadmin/video_upload.js,主要的代碼以下:ajax

$("#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(),該方法表示上傳完畢,前端可在裏面作一些額外的事情。django

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

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地址的演示來一步步學習。

相關文章
相關標籤/搜索