从本讲开始,我们开始视频管理功能的开发,视频管理包括视频上传、视频列表、视频编辑、视频删除。另外还有视频分类的功能,会一同讲解。这一讲非常重要,因为你将学习到一些之前没有学过的技术,比如大文件上传技术。
个人博客:mypython.me
我们先来实现视频的上传,视频的上传采用的是分块上传的策略,并用了分块上传类库: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地址的演示来一步步学习。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。