《Tango with Django》-第6章 模型、模板和视图

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 />
  • 效果如下
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

小结

  • 太多的失败让人沮丧,我们使网站先运作起来,慢慢培养兴趣。

猜你喜欢

转载自blog.csdn.net/m0_46629123/article/details/112296399
今日推荐