内容简介:在本讲中,我们开始首页功能的开发,在开发过程中,大家将会学习到Django中的通用视图类、分页对象paginator以及foreignKey外键的使用。大家可先通过网站演示地址 浏览一下首页的效果。我们首页呢,比较简洁大方,让人一目了然。我这样设计的目的呢,是让大家把精力放到学习django上面来,不必过度关注花哨的页面效果。我们把首页拆解为4个小的业务模块来开发,分别是:列表显示、分页功能、搜索功能、分类功能。下面我们分别对这四个功能模块进行开发讲解。
在本讲中,我们开始首页功能的开发,在开发过程中,大家将会学习到Django中的通用视图类、分页对象paginator以及foreignKey外键的使用。
效果演示
整体功能
大家可先通过网站演示地址 浏览一下首页的效果。我们首页呢,比较简洁大方,让人一目了然。我这样设计的目的呢,是让大家把精力放到学习django上面来,不必过度关注花哨的页面效果。
我们把首页拆解为4个小的业务模块来开发,分别是:列表显示、分页功能、搜索功能、分类功能。下面我们分别对这四个功能模块进行开发讲解。
开发思路
开发一个功能的基本思路是:先新建应用,然后分析功能涉及到哪些业务,从而分析出需要的数据库字段,然后编写模型,之后就是展示阶段,通过url路由配置视图函数,来将模型里面的数据显示出来。
ok,我们通过命令建立应用,命名为video。执行后,django将为我们新建video文件夹。
python3 manage.py startapp video 复制代码
下面的功能模块开发都在该应用(video)下进行。
建模型
此处,我们需要建立两个模型,分别是分类表(classification)和视频表(video)。他们是多对一的关系(一个分类对应多个视频,一个视频对应一个分类)。
首先编写Classification表,在model.py下面,我们键入如下代码。 字段有title(分类名称)和status(是否启用)
class Classification(models.Model):
list_display = ("title",)
title = models.CharField(max_length=100,blank=True, null=True)
status = models.BooleanField(default=True)
class Meta:
db_table = "v_classification"
复制代码
字段说明
- title 分类名称。数据类型是CharField,最大长度为max_length=100,允许为空null=True
- status 是否启用。数据类型是BooleanField,默认为default=True
- db_table 表名
然后编写Video模型,根据网站业务,我们设置了title(标题)、 desc(描述)、 classification(分类)、file(视频文件)、cover(封面)、status(发布状态)等字段。其中classification是一个ForeignKey外键字段,表示一个分类对应多个视频,一个视频对应一个分类(多对一)
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)
create_time = models.DateTimeField(auto_now_add=True, blank=True, max_length=20)
复制代码
字段说明
- title 视频标题。数据类型是charField,最大长度为max_length=100,允许为空null=True
- desc 视频描述。数据类型是charField,最大长度为max_length=255,允许为空null=True
- file 视频文件地址。数据类型是fileField。其中存的是视频文件的地址,在之后的视频管理中我们将会对视频的上传进行具体的讲解。
- cover 视频封面。数据类型是ImageField。存储目录为upload_to='cover/',允许为空null=True
- status 视频状态。是一个选择状态,用choices设置多选元祖。
- create_time 创建时间。数据类型是DateTimeField 。设置自动生成时间auto_now_add=True
ForeignKey 表明一种一对多的关联关系。比如这里我们的视频和分类的关系,一个视频只能对应一个分类,而一个分类下可以有多个视频。 更多关于ForeinkKey的说明,可以参看ForeignKey官方介绍
列表显示
要想访问到首页,必须先配置好路由。在video下建立urls.py文件,写入如下代码
from django.urls import path
from . import views
app_name = 'video'
urlpatterns = [
path('index', views.IndexView.as_view(), name='index'),
]
复制代码
一条path语句就代表一条路由信息。这样我们就可以在浏览器输入127.0.0.1:8000/video/index来访问首页了。
显示列表数据非常简单,我们使用django中内置的视图模版类ListView来显示,首先在view.py中编写IndexView类,用它来显示列表数据。键入如下代码
class IndexView(generic.ListView):
model = Video
template_name = 'video/index.html'
context_object_name = 'video_list'
复制代码
此处,我们使用了django提供的通用视图类ListView, ListView使用很简单,只需要我们简单的配置几行代码,即可将数据库里面的数据渲染到前端。比如上述代码中,我们配置了
- model = Video, 作用于Video模型
- template_name = 'video/index.html' ,告诉ListView要使用我们已经创建的模版文件。
- context_object_name = 'video_list' ,上下文变量名,告诉ListView,在前端模版文件中,可以使用该变量名来展现数据。
之后,我们在templates文件夹下,建立video目录,用来存放视频相关的模板文件,首先我们创建首页文件index.html。并将刚才获取到的数据显示出来。
<div class="ui grid">
{% for item in video_list %}
<div class="four wide column">
<div class="ui card">
<a class="image">
{% thumbnail item.cover "300x200" crop="center" as im %}
<img class="ui image" src="{{ im.url }}">
{% empty %}
{% endthumbnail %}
<i class="large play icon v-play-icon"></i>
</a>
<div class="content">
<a class="header">{{ item.title }}</a>
<div class="meta">
<span class="date">发布于{{ item.create_time|time_since}}</span>
</div>
<div class="description">
{{ item.view_count}}次观看
</div>
</div>
</div>
</div>
{% empty %}
<h3>暂无数据</h3>
{% endfor %}
</div>
复制代码
通过for循环,将video_list渲染到前端。这里我们使用到了django中的内置标签,比如for语句、empty语句。这些都是django中非常常用的语句。在之后的教程中我们会经常遇到。
另外,还使用了thumbnail标签来显示图片,thumbnail是一个很常用的 python 库,常常被用来做图片显示。
显示结果如下
分类功能
在写分类功能之前,我们先学习一个回调函数 get_context_data() 这是ListView视图类中的一个函数,在 get_context_data() 函数中,可以传一些 额外内容 到模板。因此我们可以使用该函数来传递分类数据。
要使用它,很简单。
只需要在IndexView类下面,追加get_context_data()的实现即可。
class IndexView(generic.ListView):
model = Video
template_name = 'video/index.html'
context_object_name = 'video_list'
def get_context_data(self, *, object_list=None, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
classification_list = Classification.objects.filter(status=True).values()
context['classification_list'] = classification_list
return context
复制代码
在上述代码中,我们将分类数据通过Classification.objects.filter(status=True).values()从数据库里面过滤出来,然后赋给classification_list,最后放到context字典里面。
在前端模板(templates/video/index.html)中,就可以通过classification_list来取数据。添加代码
<div class="classification">
<a class="ui red label" href="">全部</a>
{% for item in classification_list %}
<a class="ui label" href="">{{ item.title }}</a>
{% endfor %}
</div>
复制代码
显示效果如下
当然现在只是实现了分类展示效果,我们还需要继续实现点击效果,即点击不同的分类,显示不同的视频列表。
我们先给每个分类按钮加上href链接
<div class="classification">
<a class="ui red label" href="{% url 'home' %}">全部</a>
{% for item in classification_list %}
<a class="ui label" href="?c={{ item.id }}">{{ item.title }}</a>
{% endfor %}
</div>
复制代码
通过添加?c={{item.id }} 这里用c代表分类的id,点击后,会传到视图类中,在视图类中,我们使用 get_queryset() 函数,将get数据取出来。通过self.request.GET.get("c", None) 赋给c,判断c是否为None,如果为None,就响应全部,如果有值,就通过get_object_or_404(Classification, pk=self.c)先获取当前类,然后classification.video_set获取外键数据。
def get_queryset(self):
self.c = self.request.GET.get("c", None)
if self.c:
classification = get_object_or_404(Classification, pk=self.c)
return classification.video_set.all().order_by('-create_time')
else:
return Video.objects.filter(status=0).order_by('-create_time')
复制代码
更多关于ForeignKey的使用方法,可参考这里
分页功能
在Django中,有现成的分页解决方案,我们开发者省了不少事情。如果是简单的分页,只需要配置一下paginate_by即可实现。
class IndexView(generic.ListView):
model = Video
template_name = 'video/index.html'
context_object_name = 'video_list'
paginate_by = 12
c = None
复制代码
- painate_by = 12每页显示12条
这样每页的分页数据就能正确的显示出来来,现在来完善底部的页码条。
页码列表需要视图类和模板共同来完成,我们先来写视图类。在前面我们已经写过get_context_data了,该函数的主要功能就是传递额外的数据给模板。这里,我们就利用get_context_data来传递页码数据。
我们先定义一个 工具 函数,叫get_page_list。 在项目根目录下,新建一个文件helpers.py该文件当作一个全局的工具类,用来存放各种工具函数。把get_page_list放到helpers.py里面 该函数用来生产页码列表,不但这里可以使用,以后在其他地方也可以调用该函数。
def get_page_list(paginator, page):
page_list = []
if paginator.num_pages > 10:
if page.number <= 5:
start_page = 1
elif page.number > paginator.num_pages - 5:
start_page = paginator.num_pages - 9
else:
start_page = page.number - 5
for i in range(start_page, start_page + 10):
page_list.append(i)
else:
for i in range(1, paginator.num_pages + 1):
page_list.append(i)
return page_list
复制代码
分页逻辑:
if 页数>=10:
当前页<=5时,起始页为1
当前页>(总页数-5)时,起始页为(总页数-9)
其他情况 起始页为(当前页-5)
复制代码
举例:
假设一共16页 情况1: 当前页==5 则页码列表为[1,2,3,4,5,6,7,8,9,10] 情况2: 当前页==8 则页码列表为[3,4,5,6,7,8,9,10,11,12] 情况3: 当前页==15 则页码列表为[7,8,9,10,11,12,13,14,15,16] 复制代码
当然你看到这个逻辑会有点乱,建议大家读着代码,多试验几遍。
当拿到页码列表,我们继续改写get_context_data()函数。 将获取到的classification_list追加到context字典中。
def get_context_data(self, *, object_list=None, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
paginator = context.get('paginator')
page = context.get('page_obj')
page_list = get_page_list(paginator, page)
classification_list = Classification.objects.filter(status=True).values()
context['c'] = self.c
context['classification_list'] = classification_list
context['page_list'] = page_list
return context
复制代码
你或许对 paginator = context.get('paginator') page = context.get('page_obj')这两行代码感到陌生,我们只需要知道context.get('page_obj')返回的是当前页码,context.get('paginator')返回的是分页对象,就够了。更加详细的介绍,可参考官方。
当数据传递给模板之后,模板就负责显示出来就行了。
因为分页功能比较常用,所以需要把它单独拿出来封装到一个单独的文件中,我们新建templates/base/page_nav.html文件。然后在index.html里面我们将该文件include进来。
{% include "base/page_nav.html" %}
复制代码
打开page_nav.html,写入代码
{% if is_paginated %}
<div class="video-page">
<div class="ui circular labels">
{% if page_obj.has_previous %}
<a class="ui circular label" href="?page={{ page_obj.previous_page_number }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}"><</a>
{% endif %}
{% for i in page_list %}
{% if page_obj.number == i %}
<a class="ui red circular label">{{ i }}</a>
{% else %}
<a class="ui circular label" href="?page={{ i }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}">{{ i }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="ui circular label" href="?page={{ page_obj.next_page_number }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}">></a>
{% endif %}
</div>
</div>
{% endif %}
复制代码
上面代码中,我们用到了page_obj对象的几个属性:has_previous、previous_page_number、next_page_number。通过这几个属性,即可实现复杂的页码显示效果。其中我们还这href里面加了
{% if c %}&c={{c}}
复制代码
代表分类的id。
搜索功能
要实现搜索,我们需要一个搜索框
因为搜索框是很多页面都需要的,所以我们把代码写到templates/base/header.html文件里面。
<div class="ui small icon input v-video-search">
<input class="prompt" value="{{ q }}" type="text" placeholder="搜索视频" id="v-search">
<i id="search" class="search icon" style="cursor:pointer;"></i>
</div>
复制代码
点击搜索或回车的代码写在了static/js/header.js里面。
我们还需要配置一下路由,添加一行搜索的路由。
app_name = 'video'
urlpatterns = [
path('index', views.IndexView.as_view(), name='index'),
path('search/', views.SearchListView.as_view(), name='search'),
]
复制代码
搜索路由指向的视图类为SearchListView
下面我们来写SearchListView的代码
class SearchListView(generic.ListView):
model = Video
template_name = 'video/search.html'
context_object_name = 'video_list'
paginate_by = 8
q = ''
def get_queryset(self):
self.q = self.request.GET.get("q","")
return Video.objects.filter(title__contains=self.q).filter(status=0)
def get_context_data(self, *, object_list=None, **kwargs):
context = super(SearchListView, 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
复制代码
关键代码就是Video.objects.filter(title__contains=self.q).filter(status=0) title__contains是包含的意思,表示查询title包含q的记录。利用filter将数据过滤出来。这里写了两层过滤,第一层过滤搜索关键词,第二层过滤status已发布的视频。
另外,这里也用到了get_context_data来存放额外的数据,包括分页数据、q关键词。
配置模板文件是templates/video/search.html
因此模板代码写在search.html里面
<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" %}
复制代码
搜索功能效果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 基于django的视频点播网站开发-step5-详情页功能
- 干货 | 如何搭建小型视频点播网站
- 【腾讯课堂】视频点播上云实践
- 权利的游戏、破冰行动都烂尾了,那就来讨论一下视频点播吧
- MeEdu 0.1.4 发布,基于 Laravel 开发的在线视频点播系统
- 爬虫+网站开发实例:电影票比价网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Writing Apache Modules with Perl and C
Lincoln Stein、Doug MacEachern / O'Reilly Media, Inc. / 1999-03 / USD 39.95
Apache is the most popular Web server on the Internet because it is free, reliable, and extensible. The availability of the source code and the modular design of Apache makes it possible to extend Web......一起来看看 《Writing Apache Modules with Perl and C》 这本书的介绍吧!