從本講開始,我們開始視訊管理功能的開發,視訊管理包括視訊上傳、視訊清單、視訊編輯、視訊删除。另外還有視訊分類的功能,會一同講解。這一講非常重要,因為你将學習到一些之前沒有學過的技術,比如大檔案上傳技術。
視訊上傳
我們先來實作視訊的上傳,視訊的上傳采用的是分塊上傳的政策,并用了分塊上傳類庫: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位址的示範來一步步學習。