首页 文章 精选 留言 我的

精选列表

搜索[云点播],共10003篇文章
优秀的个人博客,低调大师

基于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地址的演示来一步步学习。

优秀的个人博客,低调大师

基于django的视频点播网站开发-step6-个人中心功能

从本讲起,我们开始个人中心功能的开发。个人中心里面包括个人资料、修改密码、订阅设置、意见反馈这四部分。通过这部分的开发,我们将会接触到更多django的用法。 感兴趣的伙伴可通过网站演示预览网站,登录后点击右上角头像即可弹出个人中心相关菜单。 整体功能 个人中心模块是对用户的信息进行展示并可以编辑。其中个人资料、修改密码、订阅设置是对用户信息的编辑,反馈建议是属于创建新数据。 个人资料 这里主要是对个人资料进行编辑,先显示用户原有的信息,然后用户即可对其进行修改并保存,对于编辑功能,django有自己的解决方案,即通过通用视图类UpdateView对模型进行更改。关于Update的介绍,同学们可查阅官网介绍 因为前面已经建立过user模型,所以这里就不用再次建立了,我们直接使用之前的user模型即可。 需要我们做的就是在users/urls.py中添加个人资料的路由, path('profile/<int:pk>/', views.ProfileView.as_view(), name='profile'), 可以看到,这里我们需要传一个int参数做为主键,并传递给视图类ProfileView。 下面我们转向ProfileView,它的写法超级简单 class ProfileView(LoginRequiredMixin,AuthorRequiredMixin, generic.UpdateView): model = User form_class = ProfileForm template_name = 'users/profile.html' def get_success_url(self): messages.success(self.request, "保存成功") return reverse('users:profile', kwargs={'pk': self.request.user.pk}) 简单的几行代码,就实现了个人资料的更新,再次彰显了django框架的强大。我们这里继承了UpdateView来实现更新操作,和DetailView类似,我们这里也设置了model和template_name 还有form_class。当更新成功后,django会回调get_success_url来将结果告诉模板,因此我们可以在get_success_url里面做一些定制的工作,我们可以传一些自己的参数。 可以看到我们还继承了LoginRequiredMixin和AuthorRequiredMixin两个类,这两个类属于公共类,其中LoginRequiredMixin的用途是:只允许登录的用户访问该视图类,AuthorRequiredMixin的用途是:只允许用户自己查看自己的个人资料,别人是无法查看的。其中AuthorRequiredMixin的代码位于videoproject/helpers.py。 修改密码 同样的,修改密码也是属于更新操作。 模型当然是用user模型,不必再建。 我们先添加路由 path('change_password/', views.change_password, name='change_password'), 修改密码比较特殊,需要对密码进行特殊处理,因此我们通过视图函数change_password来手写代码 def change_password(request): if request.method == 'POST': form = ChangePwdForm(request.user, request.POST) if form.is_valid(): user = form.save(commit=False) if not user.is_staff and not user.is_superuser: user.save() update_session_auth_hash(request, user) # 更新session 非常重要! messages.success(request, '修改成功') return redirect('users:change_password') else: messages.warning(request, '无权修改管理员密码') return redirect('users:change_password') else: print(form.errors) else: form = ChangePwdForm(request.user) return render(request, 'registration/change_password.html', { 'form': form }) 代码中,当拿到form之后,通过验证form合法性,然后调用user.save()来保存修改。然后通过update_session_auth_hash来更新session。 这样就实现了修改密码功能。 订阅设置 很多网站都有订阅设置功能,当用户订阅了网站内容之后,网站有了新内容,即向订阅用户推送相关内容。有利于让用户获取网站内容的最新更新。 我们先在users/urls.py下添加订阅功能的路由 path('subscribe/<int:pk>/', views.SubscribeView.as_view(), name='subscribe'), 我们设置的订阅视图类为SubscribeView,因为订阅的功能和修改个人资料功能类似,也是属于更新操作,所以同样是使用UpdateView来更新。 class SubscribeView(LoginRequiredMixin,AuthorRequiredMixin, generic.UpdateView): model = User form_class = SubscribeForm template_name = 'users/subscribe.html' def get_success_url(self): messages.success(self.request, "保存成功") return reverse('users:subscribe', kwargs={'pk': self.request.user.pk}) 反馈与建议 这里我们需要在users/models.py下新建一个反馈表,命名为Feedback, class Feedback(models.Model): contact = models.CharField(blank=True, null=True, max_length=20) content = models.CharField(blank=True, null=True, max_length=200) timestamp = models.DateTimeField(auto_now_add=True, null=True) class Meta: db_table = "v_feedback" 该表一共有3个字段,分别是 contact 联系方式 content 内容 timestamp 时间 写完model之后,我们就能写业务代码了。 先添加路由 path('feedback/', views.FeedbackView.as_view(), name='feedback'), 我们设置路由指向FeedbackView视图类。 我们直接贴出FeedbackView的代码 class FeedbackView(LoginRequiredMixin, generic.CreateView): model = Feedback form_class = FeedbackForm template_name = 'users/feedback.html' @ratelimit(key='ip', rate='2/m') def post(self, request, *args, **kwargs): was_limited = getattr(request, 'limited', False) if was_limited: messages.warning(self.request, "操作太频繁了,请1分钟后再试") return render(request, 'users/feedback.html', {'form': FeedbackForm()}) return super().post(request, *args, **kwargs) def get_success_url(self): messages.success(self.request, "提交成功") return reverse('users:feedback') 我们看到这个地方继承的是CreateView类,该类属于新建通用视图类。只要我们配置好model、form_class、template_name,django就自动为我们创建记录。 另外,我们还使用了一种限流量的技术:ratelimit。这是一个第三方类库,通过使用他,可以防止恶意提交数据。它使用超级简单,只需要配置好key和rate即可,key代表业务,rate代表速率,这里我们设置key为ip,即限制ip地址,rate为'2/m',表示每分钟限制请求2次。超过2次就提示用户操作频繁。 这样我们就完美的实现了用户反馈。 下面是效果展示 个人资料:修改密码:订阅设置:反馈建议:

优秀的个人博客,低调大师

基于django的视频点播网站开发-step7-我的收藏喜欢功能

从本讲起,我们会讲到个人菜单中另外两个比较重要的功能,“我的收藏”与“我的喜欢”。感兴趣的同学可以点击网站的右上角,即可弹出个人菜单,你就能看到“我的收藏”与“我的喜欢”了。通过学习这两个功能,我们会加深对django中通用视图类的理解与应用。 Demo预览 我的收藏 我们先来讨论模型,我们分析一下,我收藏的视频与我喜欢的视频,其中的视频都是属于video模型,因此我们应该在video模型上进行修改。因为是我的收藏、我的喜欢,都是与我关联,又因为,我可以收藏多个视频,视频也可以被多个用户收藏,所以用户与视频是属于多对多的关系。所以我们在video模型上添加两个字段liked和collected,分别对应我喜欢和我收藏。代码如下 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) 可以看出liked和collected字段都是属于ManyToManyField类型,表示视频与用户是多对多的关系。并分别设置它们的别名为"liked_videos"和"collected_videos",有django基础的同学应该明白,通过别名也可以访问到数据。 下面我们来添加两者的路由,添加在users/urls.py下面。 path('<int:pk>/collect_videos/', views.CollectListView.as_view(), name='collect_videos'), path('<int:pk>/like_videos/', views.LikeListView.as_view(), name='like_videos'), 其中,我的收藏的视图类是CollectListView,我的喜欢的视图类是LikeListView。我们先来实现CollectListView class CollectListView(generic.ListView): model = User template_name = 'users/collect_videos.html' context_object_name = 'video_list' paginate_by = 10 def get_context_data(self, *, object_list=None, **kwargs): context = super(CollectListView, 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 return context def get_queryset(self): user = get_object_or_404(User, pk=self.kwargs.get('pk')) videos = user.collected_videos.all() return videos 与首页展示的功能类似,这里同样继承了ListView通用视图类。并使用了公共函数get_page_list对数据进行分页。在获取收藏数据列表时,我们用的是user.collected_videos.all(),其中collected_videos就是前面定义的别名。 并通过配置template_name将数据传递给模板文件users/collect_videos.html。 模板文件关键代码 <h3 class="ui header">我的收藏</h3> <div class="ui unstackable items"> {% for item in video_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" href="{% url 'video:detail' item.pk %}">{{ item.title }}</a> </div> </div> {% empty %} <h3>暂无数据</h3> {% endfor %} </div> {% include "base/page_nav.html" %} 最终展示效果 我的喜欢 下面来开发我的喜欢功能 该功能与我的收藏功能类似。因为前面已经添加了like_videos路由,我们直接写LikeListView的代码 class LikeListView(generic.ListView): model = User template_name = 'users/like_videos.html' context_object_name = 'video_list' paginate_by = 10 def get_context_data(self, *, object_list=None, **kwargs): context = super(LikeListView, 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 return context def get_queryset(self): user = get_object_or_404(User, pk=self.kwargs.get('pk')) videos = user.liked_videos.all() return videos 与我的收藏的模式一模一样,同样是继承ListView并设置相关model与template_name变量。最终通过users/like_videos.html来渲染。 like_videos.html关键代码 <h3 class="ui header">我的喜欢</h3> <div class="ui unstackable items"> {% for item in video_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" href="{% url 'video:detail' item.pk %}">{{ item.title }}</a> </div> </div> {% empty %} <h3>暂无数据</h3> {% endfor %} </div> {% include "base/page_nav.html" %} 最终展示效果

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册