玩转Django2.0---Django笔记建站基础十一(一)(音乐网站开发)
第十一章 音乐网站开发
本章以音乐网站项目为例,介绍Django在实际项目开发中的应用,该网站共分为6个功能模块分别是:网站首页、歌曲排行榜、歌曲播放、歌曲点评、歌曲搜索和用户管理。
11.1 网站需求与设计
当我们接到一个项目的时候,首先需要了解项目的具体需求,根据需求类型划分网站功能,并了解每个需求的业务流程。本节以音乐网站为例进行介绍,整个网站的功能分为:网站首页、歌曲排行榜、歌曲播放、歌曲搜索、歌曲点评和用户管理,各个功能说明如下:
1、网站首页是整个网站的主界面,主要显示网站最新的动态信息以及网站的功能导航。网站动态信息以歌曲的动态为主,如热门下载、热门搜索和新歌推荐等;网站的功能导航时将其他页面的链接展示在首页上,方便用户访问浏览。
2、歌曲排行榜是按照歌曲的播放量进行排序,用户还可以根据歌曲类型进行自定义筛选。
3、歌曲播放是为用户提供在线试听功能,此外还提供歌曲下载、歌曲点评和相关歌曲推荐。
4、歌曲点评是通过歌曲播放页面进入的,每条点评信息包含用户名、点评内容和点评时间。
5、歌曲搜索是根据用户提供的关键字进行歌曲或歌手匹配查询的,搜索结果以数据列表显示在网页上。
6、用户管理分为用户注册、登录和用户中心。用户中心包含用户信息、登录注销和歌曲播放记录。
我们根据需求对网站的开发进行设计,首先由UI设计师根据网站需求实现网页设计图,然后由前端工程师根据网页设计图实现HTML静态页面,最后由后端工程师根据HTML静态页面实现数据库构建和网站后台开发。根据上述网站需求,一个哦你设计了6个网站页面,其中网站页面,气宗网站首页如图所示:
网站首页
从网站首页的设计图可以看到,按照网站功能可以分为7个功能区,说明如下:
1、歌曲搜索:位于网页顶端,由文本输入框和搜索按钮组成,文本输入框下面是热门搜索的歌曲。
2、轮播图:以歌曲的封面进行轮播,单击图片可进入歌曲播放。
3、音乐分类:位于轮播图的左边,按照歌曲的类型进行分类。
4、热门歌曲:位于轮播图的右边,按照歌曲的播放量进行排序。
5、新歌推荐:按照歌曲的发行时间进行排序。
6、热门搜索:按照歌曲的搜索量进行排序。
7、热门下载:按照歌曲的下载量进行排序。
歌曲排行榜页面如下图:
歌曲排行榜
从歌曲排行榜的设计图可以看到,整个页面分为两部分:歌曲分类和歌曲列表,说明如下:
1、歌曲分类:根据歌曲类型进行歌曲筛选,筛选后的歌曲显示在歌曲列表中。
2、歌曲列表:歌曲信息以播放次数进行降序显示,若对歌曲进行类型筛选,则对同一类型的歌曲以播放次数进行降序显示。
歌曲播放页面如下图:
歌曲播放
从歌曲播放的设计图可以看到,整个页面共有4大功能:各个功能说明如下:
1、歌曲信息:包括歌名、歌手、所属专辑、语种、流派、发行时间、歌词、歌曲封面和歌曲文件等。
2、下载与歌曲点评:实现歌曲下载,每下载一次都会对歌曲的下载次数累加一次。单击"歌曲点评"可进入歌曲点评页面。
3、播放列表:记录当前用户的试听记录,每播放一次都会对歌曲的播放次数累加一次。
4、相关歌曲:根据当前歌曲的类型筛选出同一类型的其他歌曲信息。
如下图:
歌曲点评
歌曲点评主要分为两部分:歌曲点评和点评信息列表,两者说明如下:
1、歌曲点评:由文本输入框和发表按钮组成的表单,以POST的请求形式实现内容提交。
2、点评信息列表:列出当前歌曲的点评信息,并对点评信息设置分页功能。
歌曲搜索页面如下图:
歌曲搜索
歌曲搜索主要根据文本框的内容对歌名或歌手进行匹配查询,然后将搜索结果返回到搜索页面上,其说明如下:
1、若文本框的内容为空,则默认返回前50首最新发行的歌曲。
2、若文本框的内容不为空,则从歌曲的歌名或歌手进行匹配查询,查询结果以歌曲的发现时间进行排序。
3、每次搜索时,若文本框的内容与歌名完全相符,则相符的歌曲将其搜索次数累加一次。
用户中心页面如下图:
用户中心
用户中心需要用户登录后才能访问,该页面主要分为用户基本信息和歌曲播放记录,说明如下:
1、用户基本信息:显示当前用户的用户头像和用户名,并设有用户退出登录链接。
2、歌曲播放记录:播放记录来自于歌曲播放页面的播放列表,并对播放记录进行分页显示。
用户注册和登录页面如下图:
用户的注册和登录是由同一个页面实现两个不同的功能,注册和登录都是通过JavaScript脚本来控制显示的,其说明如下:
1、用户注册:填写用户名、手机号和用户密码,其中用户名和手机号码具有唯一性,而且不能为空。
2、用户登录:根据用户注册时所填写的手机号码或用户名实现用户登录。
11.2 数据库设计
从网站的需求与网站设计可以得知,歌曲信息是整个网站最为核心的数据。因此,设置网站的数据结构时,应以歌曲信息为核心数据,逐步向外扩展相关联的数据信息。
我们将歌曲信息的数据表名为song,歌曲信息不song的数据结构如表所示:
表字段 | 字段类型 | 含义 |
song_id | Int类型,长度为11 | 主键 |
song_name | Varchar类型,长度为50 | 歌曲名称 |
song_singer | Varchar类型,长度为50 | 歌曲的演唱歌手 |
song_time | Varchar类型,长度为10 | 歌曲的播放时长 |
song_album | Varchar类型,长度为50 | 歌曲所属专辑 |
song_languages | Varchar类型,长度为20 | 歌曲的语种 |
song_type | Varchar类型,长度为20 | 歌曲的风格类型 |
song_release | Varchar类型,长度为20 | 歌曲的发行时间 |
song_img | Varchar类型,长度为20 | 歌曲封面图片路径 |
song_lyrics | Varchar类型,长度为50 | 歌曲的歌词文件路径 |
song_file | Varchar类型,长度为50 | 歌曲的文件路径 |
label_id | Int类型,长度为11 | 外键,管理歌曲分类表 |
歌曲信息不song的数据结构
从表可以看到,歌曲信息表song的字段以Varchar类型定义,数据表记录了歌曲的基本信息,如歌名、歌手、时长、所属专辑、语种、流派、发现世界、歌词、歌曲封面和歌曲文件,其中歌曲封面、歌词和歌曲文件是以路径的形式记录在数据库中的。一般来说,如果网站中涉及文件的使用,数据库最好记录文件的访问路径。若将文件的内容直接写入数据库中,则会对数据库造成一定的压力,从而降低网站的响应速度。
在歌曲信息不song的字段label_id可以知道,歌曲信息不song关联歌曲分类表,我们将歌曲分类表命名为label,歌曲分类表主要实现网站首页的音乐分类,其数据结构如下表:
表字段 | 字段类型 | 含义 |
label_id | Int类型,长度为11 | 主键 |
label_name | Varchar类型,长度为10 | 歌曲的分类标签 |
歌曲分类表的数据结构
在网站需求中,还会涉及歌曲动态信息,因此延伸出歌曲动态表。歌曲动态表用于记录歌曲的播放次数、搜索次数和下载次数,并且与歌曲信息表song实现一对一的数据关系,也就是一首歌曲只有一条动态信息。将歌曲动态表命名为dynamic,其数据结构如表:
表字段 | 字段类型 | 含义 |
dynamic_id | Int类型,长度为11 | 主键 |
dynamic_ | Int类型,长度为11 | 歌曲的播放次数 |
dynamic_ | Int类型,长度为11 | 歌曲的搜索次数 |
dynamic_ | Int类型,长度为11 | 歌曲的下载次数 |
song_id | Int类型,长度为11 | 外键,关联歌曲信息表 |
歌曲动态表的数据结构
最后还有与歌曲信息表song相互关联的歌曲点评表,该表主要用于歌曲点评页面。从歌曲点评页面可以知道,一首歌可以有多条点评信息,说明歌曲信息表song和歌曲点评表存在一对多的数据关系。将歌曲点评表命名为comment,其数据结构如下图:
表字段 | 字段类型 | 含义 |
comment_id | Int类型,长度为11 | 主键 |
comment_ | Varchar类型,长度为500 | 歌曲的点评内容 |
comment_ | Varchar类型,长度为20 | 用户名 |
comment_ | Varchar类型,长度为50 | 点评日期 |
song_id | Int类型,长度为11 | 外键,管理歌曲信息表 |
歌曲点评表的数据结构
除此之外,还有网站的用户管理功能,用户管理功能由用户表myuser提供用户信息。用户表myuser由Django内置模型User扩展而成,其数据结构如表所示:
表字段 | 字段类型 | 含义 |
Id | Int类型,长度为11 | 主键 |
Password | Varchar类型,长度为128 | 用户密码 |
last_login | Datetime类型,长度为6 | 上次登录时间 |
is_superuser | Tinyint类型,长度为1 | 超级用户 |
Username | Varchar类型,长度为150 | 用户名 |
first_name | Varchar类型,长度为30 | 用户的名字 |
last_name | Varchar类型,长度为150 | 用户的姓氏 |
Varchar类型,长度为254 | 邮箱地址 | |
is_staff | Tinyint类型,长度为1 | 登录Admin权限 |
is_active | Tinyint类型,长度为1 | 用户的激活状态 |
date_joined | Datetime类型,长度为6 | 用户创建的时间 |
Varchar类型,长度为 | 用户的QQ号码 | |
Varchar类型,长度为 | 用户的微信 | |
Mobile | Varchar类型,长度为11 | 用户的手机号码 |
11.3 项目创建与配置
我们对音乐网站的需求与设计有了大概的了解,下一步将需求与设计落实到真正开发中。我们选择Python3.7+Django2.2+MySQL+PyCharm作为网站的开发工具,开发环境是Windows操作系统。
首先在CMD窗口下创建Django项目,项目命名为music,然后在项目music中分别创建项目应用index、ranking、play、comment、search和user,创建指令如下:
创建music项目应用 (py3_3) E:\test5\music>python manage.py startapp index (py3_3) E:\test5\music>python manage.py startapp ranking (py3_3) E:\test5\music>python manage.py startapp play (py3_3) E:\test5\music>python manage.py startapp comment (py3_3) E:\test5\music>python manage.py startapp search (py3_3) E:\test5\music>python manage.py startapp user
完成项目和项目应用的创建后,我们在项目music的根目录下创建文件夹templates和static,两者分别存放模板文件和静态资源文件。然后在文件夹templates中放置公用模板title_base.html,而在文件夹static目录下创建文件夹css、js、font、image、songFile、songLyric和songImg以及防止图片favicon.ico,文件夹static的目录说明如下:
1、css是存放全网站的CSS样式的文件。
2、js是存放全网站的JS脚本的文件。
3、font是存放网站字体的文件。
4、image是存放网站页面的图片。
5、songFile是存放歌曲的文件。
6、songLyric是存放歌词的文件。
7、favicon.ico是网站的LOGO图片。
项目music的目录结构是根据网站的需求与设计进行搭建的,不同的需求与设计都会导致项目的目录结构有所不同。我们打开PyCharm查看项目music的目录结构,如下图:
项目目录结构
项目目录结构搭建完成后,下一步是对项目进行相关的配置,配置信息主要在配置文件settings.py中完成。我们对项目music的属性INSTALLED_APPS、MIDDLEWARE、TEMPLATES和DATABASES进行相关配置,配置信息如下:
#添加新增的项目应用index、ranking、play、comment、search和user INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'index', 'ranking', 'user', 'play', 'search', 'comment', ] #添加中间件LocaleMiddleware MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # 使用中文 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] #设置模板路径,在每个App中分别创建模板文件夹templates TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'index/templates'), os.path.join(BASE_DIR, 'ranking/templates'), os.path.join(BASE_DIR, 'user/templates'), os.path.join(BASE_DIR, 'play/templates'), os.path.join(BASE_DIR, 'comment/templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] 设置数据库连接信息,项目使用的数据库为music_db DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'music_db', 'USER':'root', 'PASSWORD':'root', 'HOST':'192.168.10.100', 'PORT':'3306', } }
任何一个项目都需要对配置属性INSTALLED_APPS、MIDDLEWARE、TEMPLATES和DATABASES进行配置,这是一个项目的常规配置。完成项目配置后,我们接着对项目的URL进行配置。在项目的urls.py中分别对新建的App设置相应的URL地址,设置如下:
from django.contrib import admin from django.urls import path, include #配置URL地址信息 urlpatterns = [ path('admin/', admin.site.urls), path('', include('index.urls')), path('ranking.html', include('ranking.urls')), path('play/', include('play.urls')), path('comment/', include('comment.urls')), path('search/', include('search.urls')), path('user/', include('user.urls')), ]
至此,音乐网站的开发环境基本上已搭建完毕。在整个项目搭建过程中,我们总结出Django开发环境的搭建流程,其说明如下:
1、创建Django项目,可以在CMD窗口下输入创建指令或者在PyCharm下实现项目新建。
2、创建项目的App应用,创建方式也是在CMD窗口或者PyCharm下实现。
3、在项目的根目录下新建文件夹templates和static,分别存放模板文件和静态资源。
4、设置项目的配置信息,由settings.py实现,常规的配置属性有INSTALLED_APPS、MIDDLEWARE、TEMPLATES和DATABASES。
5、根据项目的App或项目的页面来设定网站的URL地址信息,由项目的urls.py实现。
11.4 网站首页
网站首页是整个网站的主界面,从网站的需求设计来看,首页共实现7个功能:歌曲搜索、轮播图、音乐分类、热门歌曲、新歌推荐、热门搜索和热门下载。在项目music中,首页由项目应用index实现,我们在index中创建模板文件夹templates,在文件夹中放置模板文件index.html,如下图:
index目录架构
首页的歌曲信息应该来自于数据库,除了Django内置的数据表之外,根据项目的数据库设计得知,网站一共定义了4张数据表,为了方便管理,我们将4张数据表所对应的模型都在index的models.py中进行定义,模型定义如下:
#index/models.py from django.db import models # 歌曲分类表label class Label(models.Model): label_id = models.AutoField('序号', primary_key=True) label_name = models.CharField('分类标签', max_length=10) def __str__(self): return self.label_name class Meta: # 设置Admin界面的显示内容 verbose_name = '歌曲分类' verbose_name_plural = '歌曲分类' # 歌曲信息表song class Song(models.Model): song_id = models.AutoField('序号', primary_key=True) song_name = models.CharField('歌名', max_length=50) song_singer = models.CharField('歌手', max_length=50) song_time = models.CharField('时长', max_length=10) song_album = models.CharField('专辑', max_length=50) song_languages = models.CharField('语种', max_length=20) song_type = models.CharField('类型', max_length=20) song_release = models.CharField('发行时间', max_length=20) song_img = models.CharField('歌曲图片', max_length=20) song_lyrics = models.CharField('歌词', max_length=50, default='暂无歌词') song_file = models.CharField('歌曲文件', max_length=50) label = models.ForeignKey(Label, on_delete=models.CASCADE,verbose_name='歌名分类') def __str__(self): return self.song_name class Meta: # 设置Admin界面的显示内容 verbose_name = '歌曲信息' verbose_name_plural = '歌曲信息' # 歌曲动态表dynamic class Dynamic(models.Model): dynamic_id = models.AutoField('序号', primary_key=True) song = models.ForeignKey(Song, on_delete=models.CASCADE, verbose_name='歌名') dynamic_plays = models.IntegerField('播放次数') dynamic_search = models.IntegerField('搜索次数') dynamic_down = models.IntegerField('下载次数') class Meta: # 设置Admin界面的显示内容 verbose_name = '歌曲动态' verbose_name_plural = '歌曲动态' # 歌曲点评表comment class Comment(models.Model): comment_id = models.AutoField('序号', primary_key=True) comment_text = models.CharField('内容', max_length=500) comment_user = models.CharField('用户', max_length=20) song = models.ForeignKey(Song, on_delete=models.CASCADE,verbose_name='歌名') comment_date = models.CharField('日期', max_length=50) class Meta: # 设置Admin界面的显示内容 verbose_name = '歌曲评论' verbose_name_plural = '歌曲评论'
上述代码定义了模型Label、Song、Dynamic和Comment,分别对应歌曲分类表label、歌曲信息表song、歌曲动态表dynamic和歌曲点评表comment。我们根据模型的定义在项目的数据库中创建相应的数据表,在PyCharm的Terminal模式下输入数据迁移指令:
(py3_3) E:\test4\music>python manage.py makemigration #创建数据表 (py3_3) E:\test4\music>python manage.py migrate
我们打开数据库music_db可以看到项目所有已定义的模型都能转换成相应的数据表,在数据表index_label、index_song和index_dynamic中分别添加网站开发所需的数据信息,如下图:
数据表index_label(左) 数据表index_dynamic(右)
数据表index_song
值得注意的是,数据表index_song的字段song_img、song_lyrics和song_file的数据分别代表静态文件夹songImg、songLyric和songFile里面的数据名。在实际的开发中,文件的存储都是采用文件服务器存放的,比如阿里云的云存储OSS和腾讯云的对象存储COS等。
至此,网站的数据模型和数据表的数据已经部署完毕,下一步是实现网站首页的开发。网站首页主要有index的路由配置urls.py、视图views.py和模板index.html共同实现,代码如下:
#index/urls.py from django.urls import path from . import views # 设置首页的URL地址信息 urlpatterns = [ path('', views.indexView, name='index'), ] #index/views.py from django.shortcuts import render from .models import * def indexView(request): # 热搜歌曲 search_song = Dynamic.objects.select_related('song').order_by('-dynamic_search').all()[:8] # 音乐分类 label_list = Label.objects.all() # 热门歌曲 play_hot_song = Dynamic.objects.select_related('song').order_by('-dynamic_plays').all()[:10] # 新歌推荐 daily_recommendation = Song.objects.order_by('-song_release').all()[:3] # 热门搜索、热门下载 search_ranking = search_song[:6] down_ranking = Dynamic.objects.select_related('song').order_by('-dynamic_down').all()[:6] all_ranking = [search_ranking, down_ranking] return render(request, 'index.html',locals())
上述代码将首页的响应处理交给视图函数indexViews执行,并且将首页的URL命名为index,URL的命名可以在模板上使用Django内置的url标签生成相应的URL地址。视图函数indexViews一共执行了5此数据查询,其说明如下:
1、search_song:通过歌曲的搜索次数进行降序查询,由Django内置的select_related方法实现模型Song和Dynamic的数据查询。
2、label_list:查询模型Label的全部数据,数据显示在首页轮播图左侧的音乐分类中。
3、play_host_song:由select_related方法实现模型Song和Dynamic的数据查询,查询结果以歌曲的播放次数进行降序排列,数据显示在首页轮播图右侧的热门歌曲中。
4、daily_recommendation:以歌曲发行时间的先后顺序查询前三首歌曲的信息,数据显示在首页的新歌推荐中。
5、all_ranking:由热门搜索和热门下载组成的列表。热门搜索的数据来自于search_song:热门下载用于获取下载次数排在前6行的歌曲信息。
最后在模板index.html中编写模板语法,将视图函数indexViews查询所得的数据对象通过遍历的方式呈现在网页上。由于模板index.html的代码较多,此处只列出首页的功能代码,完整的模板代码可在下载资源中查看。模板index.html代码如下:
#模板index.html的功能代码 #首页的搜索框,由HTML表单实现,{% url 'search' XXX %}是搜索页面的地址链接 <form id="searchForm" action="{% url 'search' 1 %}" method="post" target="_blank"> {% csrf_token %} <div class="search-keyword"> <input name="kword" type="text" class="keyword" maxlength="120" placeholder="音乐节" /> </div> <input id="subSerch" type="submit" class="search-button" value="搜 索" /> </form> #搜索框下面的热门搜索歌曲,{% url 'play' XXX %}是播放页面的地址链接 <div id="suggest" class="search-suggest"></div> <div class="search-hot-words"> {% for song in search_song %} <a target="play" href="{% url 'play' song.song.song_id %}" >{{ song.song.song_name }}</a> {% endfor %} </div> #网站导航栏 <ul class="nav clearfix"> <li><a href="/">首页</a></li> <li><a href="{% url 'ranking' %}" target="_blank">歌曲排行</a></li> <li><a href="{% url 'home' 1 %}" target="_blank">用户中心</a></li> </ul> #音乐分类,位于轮播图的左侧 <div class="category-nav-body"> <div id="J_CategoryItems" class="category-items"> {% for label in label_list %} <div class="item" data-index="1"><h3> <a href="javascript:;">{{ label.label_name }}</a></h3> </div> {% endfor %} </div> </div> #轮播图,{% url 'play' 12 %}是播放页面的地址链接 <div id="J_FocusSlider" class="focus"> <div id="bannerLeftBtn" class="banner_btn"></div> <ul class="focus-list f_w"> <li class="f_s"><a target="play" href="{% url 'play' 12 %}" class="layz_load" > <img data-src="{% static '/image/datu-1.jpg' %}" width="750" height="275"></a> </li> <li class="f_s"><a target="play" href="{% url 'play' 13 %}" class="layz_load" > <img data-src="{% static '/image/datu-2.jpg' %}" width="750" height="275"></a> </li> </ul> <div id="bannerRightBtn" class="banner_btn"></div> </div> #热门歌曲,位于轮播图的右侧。{{ forloop.counter }}用于显示当前循环次数 <div class="aside"> <h2>热门歌曲</h2> <ul> {% for song in play_hot_song %} <li><span>{{ forloop.counter }}</span> <a target="play" href="{% url 'play' song.song.song_id %}" >{{ song.song.song_name }}</a> </li> {% endfor %} </ul> </div> #新歌推荐 <div id="J_TodayRec" class="today-list"> <ul> {% for list in daily_recommendation %} {% if forloop.first %} <li class="first"> {% else %} <li> {% endif %} <a class="pic layz_load pic_po" target="play" href="{% url 'play' list.song_id %}" > <img data-src="{% static "songImg/" %}{{ list.song_img }}" ></a> <div class="name"> <h3><a target="play" href="{% url 'play' list.song_id %}" >{{ list.song_name }}</a></h3> <div class="singer"><span>{{ list.song_singer }}</span></div> <div class="times">发行时间:<span>{{ list.song_release }}</span></div> </div> <a target="play" href="{% url 'play' list.song_id %}" class="today-buy-button" >去听听></a> {% endfor %} </ul> </div> #热门搜索和热门下载 <div id="J_Tab_Con" class="tab-container-cell"> {% for list in all_ranking %} {% if forloop.first %} <ul class="product-list clearfix t_s current"> {% else %} <ul class="product-list clearfix t_s" style="display:none;"> {% endif %} {% for songs in list %} <li> <a target="play" href="{% url 'play' songs.song.song_id %}" class="pic layz_load pic_po" > <img data-src="{% static "songImg/" %}{{ songs.song.song_img }}" ></a> <h3><a target="play" href="{% url 'play' songs.song.song_id %}" >{{ songs.song.song_name }}</a></h3> <div class="singer"><span>{{ songs.song.song_singer }}</span></div> {% if all_ranking|first == list %} <div class="times">搜索次数:<span>{{ songs.dynamic_search }}</span></div> {% else %} <div class="times">下载次数:<span>{{ songs.dynamic_down }}</span></div> {% endif %} </li> {% endfor %} </ul> {% endfor %} </div>
从模板的功能代码可以看到,每个功能都是通过遍历的方式将视图函数传递的变量进行输出,还有部分功能在数据列举的过程中,通过判断当前循环次数来控制HTML标签的样式。为了检验首页是否正常运行,启动music项目,在浏览器上访问