6.1 创建数据驱动页面的流程
在 Django 中创建数据驱动页面主要分为 5 步:
➊ 在 views.py 文件中导入想使用的模型。
➋ 在视图函数中查询模型,获取想呈现的数据。
➌ 把从模型获取的数据传给模板上下文。
➍ 创建或修改模板,显示上下文中的数据。
➎ 把 URL 映射到视图上(如果还未做的话)。
以上就是在 Django 框架中把模型、视图和模板连接在一起的步骤。
6.2 在首页显示分类
导入所需的模型
打开 rango/views.py 文件,在顶部其他导入语句之后从 Rango 应用的 models.py
文件中导入 Category 模型:
from rango.models import Category
修改 index 视图
在 rango/views.py 文件中,我们要修改主页的视图函数,即 index()。根据下述代码修改。
def index(request):
# 查询数据库,获取目前存储的所有分类
# 按点赞次数倒序排列分类
# 获取前 5 个分类(如果分类数少于 5 个,那就获取全部)
# 把分类列表放入 context_dict 字典
# 稍后传给模板引擎
category_list = Category.objects.order_by('-likes')[:5]
context_dict = {
'categories':category_list}
return render(request,'rango/index.html',context = context_dict)
最终rango/views.py 文件如下:
from django.shortcuts import render
from django.http import HttpResponse
from rango.models import Category
def index(request):
category_list = Category.objects.order_by('-likes')[:5]
context_dict = {
'categories':category_list}
return render(request,'rango/index.html',context = context_dict)
修改 index 模板
<!DOCTYPE html>
{
% load static %} <!-- 新增 -->
<html>
<head>
<title>Rango</title>
<!-- CSS -->
<!-- JavaScript -->
</head>
<body>
<h1>Rango says...</h1>
<div>
hey there partner! <br /></div>
<div>
{
% if categories %}
<ul>
{
% for category in categories %}
<li>{
{
category.name}}</li>
{
% endfor %}
</ul>
{
% else %}
<strong>There are no categories present.</strong>>
{
% endif %}
</div>
<div>
<a href="/rango/about/">About</a><br />
<img src="{% static 'images/rango.jpg'%}"
alt="Picture of Rango"/><!--新增-->>
</div>
</body>
</html>
现在访问 Rango 的主页应该会看到页面标题下方显示着分类列表。
6.3 创建详情页面
URL 设计和映射
更好的方法是在 URL 中使用分类的名称。例如,可以通过 URL /rango/category/python/ 访问与Python 相关的网页列表。这样的 URL 简单、可读性高,而且具有一定的语义。为此,我们要使用 Django 提供的 slugify 函数。
为分类添加 slug 字段
为了得到可读性高的 URL,我们要为 Category 模型添加一个别名(slug)字段。然后使用 Django提供的 slugify 函数把空白替换为连字符,例如 “how do i create a slug in django” 将变成"how-do-i-create-a-slug-in-django"。
此外,还要覆盖 Category 模型的 save() 方法,调用 slugify() 函数更新 slug 字段。注意,只要分类名称变了,别名就随之改变。参照下述代码片段更新模型,别忘了导入 slugify() 函数。
修改rango/models.py 文件:
from django.template.defaultfilters import slugify
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField()
def save(self,*args,**kwargs):
self.slug = slugify(self.name)
super(Category,self).save(*args,**kwargs)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
更新模型之后,接下来要把变动应用到数据库上。然而,数据库中已经有数据了,因此我们必须考虑改动产生的影响。其实我们想做的很简单,就是从分类名称中得到别名(此项操作在初次保存记录时执行)。通过迁移工具能把 slug 字段添加到数据库中,而且可以为该字段指定默认值。可是,每个分类的别名应该是不同的。因此,我们将先执行迁移,然后重新运行填充脚本。之所以这么做,是因为填充脚本会在分类上调用 save() 方法,从而触发上面实现的 save() 方法,更新各分类的别名。
- 第一次跑
执行下述命令,执行迁移:
我们没有为 slug 字段指定默认值,而且数据库中有数据,因此 migrate 命令会给你两个选择。选择提供默认值的选项,输入一个空字符串(两个引号,即 ‘’)。然后运行填充脚本,更新 slug字段。
你会发现跑不出来。我们用的方法是,更新模型,把 slug 字段设为允许空值:slug = models.SlugField(blank=True)
slug = models.SlugField(blank=True)
rango/models.py完整的Category class如下:
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(blank=True)
def save(self,*args,**kwargs):
self.slug = slugify(self.name)
super(Category,self).save(*args,**kwargs)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
- 修改Category class后第二次跑
创建分类页面的步骤
为了实现可通过 /rango/category/< category-name-slug >/ 访问的分类页面,我们要做几处修改。基本步骤如下:
➊ 把 Page 模型导入 rango/views.py 模块。
➋ 在 rango/views.py 模块中定义一个新视图,命名为 show_category()。这个视图有个额外的参数,category_name_slug,用于传入编码后的分类名称。为了编码和解码category_name_slug,要定义两个辅助函数。
➌ 创建一个模板,templates/rango/category.html。
➍ 更新 rango/urls.py. 中的 urlpatterns,把这个新视图映射到 URL 模式上。
此外,还要更新 index() 视图和 index.html 模板,添加指向分类页面的链接。
分类视图
在 rango/views.py 中,首先导入 Page 模型,即把下述导入语句添加到文件顶部。然后定义视图 show_category()。
from rango.models import Page
def show_category(request,category_name_slug):
context_dict = {
}
try:
category = Category.objects.get(slug=category_name_slug)
pages = Page.objects.filter(category=category)
context_dict['pages']=pages
context_dict['category']=category
except Category.DoesNotExist:
context_dict['category'] = None
context_dict['pages'] = None
return render(request,'rango/category.html',context_dict)
分类模板
下面为这个新视图创建模板。在 < workspace>/tango_with_django_project/templates/rango/ 目录中新建 category.html 文件,写入下述代码。
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<div>
{
% if category %}
<h1>{
{
category.name }}</h1>>
{
% if pages %}
<ul>
{
% for page in pages %}
<li><a href="{
{page.url}}">{
{
page.title}}</a>></li>>
{
% endfor %}
</ul>>
{
% else %}
<strong>No pages currently in category.</strong>>
{
% endif %}
{
% else %}
The specified category does not exist!
{
% endif %}
</div>>
</body>
</html>
上述 HTML 代码再次展示了如何通过 {
{ }} 标签使用模板上下文中的数据。我们访问了 category和 pages 对象,以及它们的字段,例如 category.name 和 page.url。
如果 category 存在,再检查分类下有没有网页。如果有,使用模板标签 {% for page in pages %}迭代网页,显示 pages 列表中各网页的 title 和 url 属性。网页的信息在一个 HTML 无序列表(< ul> 标签)中显示。
带参数的 URL 映射
下面看看 category_name_slug 参数的值是如何传给 show_category() 视图函数的。打开 Rango 应用的 urls.py 文件,把 urlpatterns 改成下面这样:
from django.conf.urls import url
from rango import views
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.show_category, name='show_category'),
]
新增的 URL 模型匹配 rango/category/ 和末尾的 / 之间的数字字母和连字符序列。匹配的序列存储在参数 category_name_slug 中,传给 views.show_category() 函数。例如,对 rango/category/python-books/ 这个 URL 来说,category_name_slug 参数的值是 python-books。
Django 应用中的视图函数必须至少有一个参数。这个参数通常命名为 request,通过它获取与HTTP 请求有关的信息。如果 URL 中带有参数,必须为对应的视图函数声明额外的具名参数。鉴于此,show_category() 视图才定义为 def show_category(request, category_name_slug)。
修改 index 模板
我们要修改首页的模板,为列出的分类添加链接,指向分类页面。更新 index.html 模板,加入指向分类页面的链接。
<!DOCTYPE html>
{
% load static %} <!-- 新增 -->
<html>
<head>
<title>Rango</title>
<!-- CSS -->
<!-- JavaScript -->
</head>
<body>
<h1>Rango says...</h1>
<div>
hey there partner! <br /></div>
<div>
{
% if categories %}
<ul>
{
% for category in categories %}
<!-- 修改下面这一行,添加链接 -->
<li>
<a href="/rango/category/{
{category.slug}}">{
{
category.name}}</a>
</li>
{
% endfor %}
</ul>
{
% else %}
<strong>There are no categories present.</strong>>
{
% endif %}
</div>
<div>
<a href="/rango/about/">About Rango</a><br />
<img src="{% static 'images/rango.jpg'%}" alt="Picture of Rango"/><!--新增-->>
</div>
</body>
</html>
检验结果
- 进入主页
- 进入Python Category
- 进入Official Python Tutorial
练习
- 更新视图和模型rango/views.py
def index(request):
category_list = Category.objects.order_by('-likes')[:5]
page_list = Page.objects.order_by('-views')[:5]
context_dict = {
'categories': category_list, 'pages': page_list}
return render(request,'rango/index.html',context = context_dict)
- 修改模板templates/rango/index.html
<!DOCTYPE html>
{
% load static %} <!-- 新增 -->
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>Rango says...</h1>
<div>
hey there partner! <br /></div>
<div>
<div><h3>Most Liked Categories</h3> </div>
{
% if categories %}
<ul>
{
% for category in categories %}
<li>
<a href="/rango/category/{
{category.slug}}">{
{
category.name}}</a>
</li>
{
% endfor %}
</ul>
{
% else %}
<strong>There are no categories present.</strong>>
{
% endif %}
</div>
<div>
<div><h3>Most Viewed Pages</h3></div>
{
% if pages %}
<ul>
{
% for page in pages %}
<li><a href="{
{ page.url }}">{
{
page.title }}</a></li>
{
% endfor %}
</ul>
{
% else %}
<strong>There are no categories present.</strong>
{
% endif %}
</div>
<div>
<a href="/rango/about/">About Rango</a><br />
<img src="{% static 'images/rango.jpg'%}" alt="Picture of Rango"/><!--新增-->>
</div>
</body>
</html>
- 更新填充数据populate_rango.py
def populate():
python_pages = [
{
"title": "Official Python Tutorial","url":"http://docs.python.org/2/tutorial/","views":32},
{
"title":"How to Think like a Computer Scientist","url":"http://www.greenteapress.com/thinkpython/","views":16},
{
"title":"Learn Python in 10 Minutes","url":"http://www.korokithakis.net/tutorials/python/","views":8}]
django_pages = [
{
"title":"Official Django Tutorial","url":"https://docs.djangoproject.com/en/1.9/intro/tutorial01/","views":32},
{
"title":"Django Rocks","url":"http://www.djangorocks.com/","views":16},
{
"title":"How to Tango with Django","url":"http://www.tangowithdjango.com/","views":8}]
other_pages = [
{
"title":"Bottle","url":"http://bottlepy.org/docs/dev/","views":32},
{
"title":"Flask","url":"http://flask.pocoo.org","views":16}]
# 下述代码迭代 cats 字典,添加各分类,并把相关的网页添加到分类中
for cat,cat_data in cats.items():
c = add_cat(cat,cat_data['views'],cat_data['likes'])
for p in cat_data["pages"]:
add_page(c,p["title"],p["url"],p["views"])
- 修改模板templates/rango/page_list.html
{
% if pages %}
<ul>
{
% for page in pages %}
<li><a href="{
{page.url}}">{
{
page.title}}{
{
page.views}}</a>></li>>
{
% endfor %}
</ul>
{
% else %}
<strong>No pages currently in category.</strong>
{
% endif %}
- 修改模板template/rango/category.html
<a href="/rango/">Index</a><br />
- 效果如下
小结
- 太多的失败让人沮丧,我们使网站先运作起来,慢慢培养兴趣。