第七章 模型、模板和视图

来源:互联网 发布:rpg网游 知乎 编辑:程序博客网 时间:2024/06/05 15:49

       现在我们已经模型建立和填充了一些数据,我们现在可以放一些东西在一起。我们会弄清楚如何在视图中访问数据模型,以及如何通过模板呈现这些数据。

基本工作流程:数据驱动页面

你必须承担建立在Django数据驱动的网页五个主要步骤。

  1. 首先,把您希望使用的模型导入到应用程序的`views.py`文件。
  2. 在你希望使用的视图中,查询模型来得到你想要呈现的数据。
  3. 从你的模型传递结果到模板的上下文中。
  4. 设置你的模板将数据呈现给你希望的任何方式的用户.
  5. 如果你没有这么做,将URL映射到视图。

这些措施强调了Django的框架如何分离模型,视图和模板之间的关系。

在Rango的主页显示Categories

一个关于在主页面的要求是为了显示前五名rango’ed类别。

导入需求模型

       我们了满足需求,我们需要完成上面的每一步。首先,打开rango/views.py并在Rango的models.py文件中导入Category模型。

# Import the Category modelfrom rango.models import Category

修改索引视图

       做完第一步,我们想修改index()函数。我们回顾一下,我们应该记得index()函数负责主要的页面视图。修改函数看起来像下面的例子。

def index(request):    # Query the database for a list of ALL categories currently stored.    # Order the categories by no. likes in descending order.    # Retrieve the top 5 only - or all if less than 5.    # Place the list in our context_dict dictionary which will be passed to the template engine.    category_list = Category.objects.order_by('-likes')[:5]    context_dict = {'categories': category_list}    # Render the response and send it back!    return render(request, 'rango/index.html', context_dict)

       在这里,我们已经一气呵成完成第二步和第三步。首先,我们查询category模型去检索前五个类别。在这里我们使用了order_by()方法通过likes的数量降序排序-因此包含-。我们现在这个列表的前五个Category对象列。
       查询完成后,我们传递引用列表(存储为变量category_list)到数据字典,context_dict。这本字典随后被传递的上下文在render()调用模板引擎的一部分。

警告
注意,分类模型包含likes字段等。对于这个工作你需要完成前一章的练习,例类别模型需要更新包括likes字段。

修改索引模板

       视图更新,为我们剩下要做的就是更新模板rango/index.html。位于你项目的templates目录。修改文件的HTML代码,以便它看起来像如下所示的例子。

<!DOCTYPE html><html>    <head>        <title>Rango</title>    </head>    <body>        <h1>Rango says...hello world!</h1>        {% if categories %}            <ul>                {% for category in categories %}                <li>{{ category.name }}</li>                {% endfor %}            </ul>        {% else %}            <strong>There are no categories present.</strong>        {% endif %}        <a href="/rango/about/">About</a>    </body></html>

       在这里,我们使用Django的模板语言呈现数据,使用iffor控制状态。在页面的<body>中,我们测试看是否是categories-上下文变量包含我们的列表中的名称,实际上包含任何类别({{% if categories %}})。
       如果是这样,我们继续构建一个无序的HTML列表(使用<ul>)。for 循环{% for category in categories %}遍历结果列表,一系列<li>标签打印出每个分类的名字({{ category.name }})来显示一个列表元素。
如果没有类别存在,显示一条相反的指示消息。
如示例所示在Django模板语言,所有命令中附上标签{%%},而变量中引用在{{}}块。
如果你访问Rango的主页 http://127.0.0.1:8000/rango/,你应该看到下面三个类别的列表页面标题就像如图1所示。
图1
Figure 1: The Rango homepage - now dynamically generated - showing a list of categories. How exciting!

创建一个详细页

       根据Rango的说明书,我们还需要出示的与每个类别相关联的网页列表。我们这里有许多挑战需要克服。必须创建一个新视图,应进行参数设置。我们还需要创建URL模式和URL字符串编码类别名称。

URL设计和映射

       让我们首先考虑URL的问题。我们可以处理这个问题的一个方法是使用URL中的每个类别的惟一ID。例如,我们可以创建/rango/category/1//rango/category/2/,在类别对应的数字1和2分别具有独特的id。然而,这些url并不容易被人理解。虽然我们可能推断出数字与类别相关,用户知道如何分类与相关唯一的id 1或2 ?用户没有尝试就不知道。
       反而,我们可以使用类别名称作为URL的一部分。/rango/category/Python/应该给我们的有关Python的类别页面的列表。如果我们用这种方法,我们必须处理类别具有多个单词,如 ‘Other Frameworks’,等等。

注释
设计干净的URL是网页设计的一个重要方面。查看维基百科上简洁URLs文章了解更多信息。

更新Category表中的Slug字段

       为了使用简洁URLs我们将包括一个slug字段包含在Category模型中。首先,我们需要从Django中导入函数slugify,用连接符替代空白块。例如,“how do i create a slug in django”转变成“how-do-i-create-a-slug-in-django”

警告
当你在URLs中使用空格,它被认为是不安全的使用它们。查看IETF Memo on URLs了解更多信息。

我们需要重写Category模型中的save方法,我们会通过它调用slugify方法和更新slug字段。注意每次Category名字改变,这个slug都需要改变。更新你的模型,如下所示,添加导入。

from django.template.defaultfilters import slugifyclass Category(models.Model):        name = models.CharField(max_length=128, unique=True)        views = models.IntegerField(default=0)        likes = models.IntegerField(default=0)        slug = models.SlugField(unique=True)        def save(self, *args, **kwargs):                self.slug = slugify(self.name)                super(Category, self).save(*args, **kwargs)        def __unicode__(self):                return self.name

你现在已经完成模型的更新,则需要执行迁移。

$ python manage.py makemigrations rango$ python manage.py migrate

       由于我们没有提供slug的默认值,而且我们已经有了现有的数据模型,那么迁移命令会给你两个选择。选择选项,以提供一个默认,并输入“”。不要担心这将很快得到更新。现在重新运行你的填充脚本。由于save方法被每个分类调用,重写save方法会被执行,更新slug字段。运行服务器,通过管理界面检查模型中的数据。
       在管理界面中,您可能希望它会自动预填充slug字段作为类别名称的类型。要做到这一点,你可以更新rango/admin.py和下面的代码:

from django.contrib import adminfrom rango.models import Category, Page# Add in this class to customized the Admin Interfaceclass CategoryAdmin(admin.ModelAdmin):    prepopulated_fields = {'slug':('name',)}# Update the registeration to include this customised interfaceadmin.site.register(Category, CategoryAdmin)admin.site.register(Page)

       尝试管理接口,并添加一个新的类别。很酷,嘿嘿!现在,我们已经添加在slug字段域,我们现在可以使用它们作为简洁的URLs:-)。

类别页面工作流

随着我们的URLs设计选择,让我们开始吧。我们将采取以下措施。

  1. rango/views.py中导入页面模型
  2. rango/views.py创建一个新的视图-叫category-这个category视图会获取一个附加参数,category_name_url将存储在编码的类型名称。
    • 我们需要一些帮助函数来编码和解码category_name_url

  3. 创建一个新模板,templates/rango/category.html
  4. rango/urls.py更新Rango的urlpatterns映射到新category视图的一个URL模式。

我们也需要更新index()视图和index.html模板去提供一个链接到分类页面视图。

分类视图

rango/views.py里,我们首先需要导入一个Page模型。这意味着我们必须在文件的顶部添加下面的import语句。

from rango.models import Page

接下来,我们添加我们新的视图,category()

def category(request, category_name_slug):    # Create a context dictionary which we can pass to the template rendering engine.    context_dict = {}    try:        # Can we find a category name slug with the given name?        # If we can't, the .get() method raises a DoesNotExist exception.        # So the .get() method returns one model instance or raises an exception.        category = Category.objects.get(slug=category_name_slug)        context_dict['category_name'] = category.name        # Retrieve all of the associated pages.        # Note that filter returns >= 1 model instance.        pages = Page.objects.filter(category=category)        # Adds our results list to the template context under name pages.        context_dict['pages'] = pages        # We also add the category object from the database to the context dictionary.        # We'll use this in the template to verify that the category exists.        context_dict['category'] = category    except Category.DoesNotExist:        # We get here if we didn't find the specified category.        # Don't do anything - the template displays the "no category" message for us.        pass    # Go render the response and return it to the client.    return render(request, 'rango/category.html', context_dict)

       我们新的视图遵循相同的基本步骤作为我们的index()视图。我们首先定义一个上下文字典,然后我们尝试从模型中提取数据,并将相关数据添加到上下文字典。我们确定哪些类别使用值作为参数传递category_name_slugcategory()视图函数。如果在类别模型中找到类别,我们就可以抽出相关的网页,并且将其添加到上下文字典,context_dict

类别模板

       现在为我们的新视图创建模板。在<workspace>/tango_with_django_project/templates/rango/目录在,创建category.html。在新的文件中,添加以下代码。

<!DOCTYPE html><html>    <head>        <title>Rango</title>    </head>    <body>        <h1>{{ category_name }}</h1>        {% if category %}            {% 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 {{ category_name }} does not exist!        {% endif %}    </body></html>

       上面HTML代码示例再次证明我们如何利用数据通过其上下文传递给模板。我们使用category_name变量和我们的categorypages对象。如果category没有在我们模板上下文环境中定义,category不会在数据库中找到,一个友好的错误讯息,说明了这一事实。如果正好相反,然后我们继续检查pages。如果pages没有定义或不包含元素,我们显示一条消息说明没有页面。否则,分类中的页面呈现在HTML的列表。对于每个页面在pages列表中,呈现它们的titleurl属性。

注释
Django模板条件标签-{% if %}-是一个非常简洁的方式确定一个对象存在在模板的上下文。试着养成这样的习惯进行这些检查以减少潜在的异常范围内可能会提高你的代码质量。
在你的模板放置条件检查-像{% if category %}在上面示例-也是情理之中的语义。条件检查的结果直接影响渲染页面呈现给用户的方式,并且呈现面应该封装在Django应用程序的模板。

参数化URL映射

       现在,让我们来看看我们实际是如何将category_name_url参数的值传递给category()函数。为此,我们需要修改Rango的urls.py文件并更新urlpatterns元组如下所示。

urlpatterns = patterns('',    url(r'^$', views.index, name='index'),    url(r'^about/$', views.about, name='about'),    url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),)  # New!

正如你所看到的,我们增加了一个相当复杂的条目会调用view.category()当与正则表达式r'^(?P<category_name_slug>\w+)/$'匹配。我们建立了我们的正则表达式查找任意序列的字母数字字符(a-z,A-Z,0-9)和连字符(-)结尾的URL斜线之前。然后将这个值传递给视图views.category()作为参数category_name_slug,唯一的参数后强制request参数。

注释
当你希望参数化URLs,重要的是要确保你的URL模式匹配的参数,需要在相应的视图。为了进一步说明,在我们上面添加的实例。模式添加如下。
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category')
从这里我们可以推断出的字符(字母数字和下划线)在category/和匹配URL的结尾/之间传递views.category()作为命名参数category_name_slug。例如,URLcategory/python-books/将产生一个python-bookscategory_name_slug
你应该记住,所有视图函数定义为Django项目的一部分必须至少有一个参数。这通常称为request - 并提供访问与由用户进行的给定的HTTP请求信息。当参数化URLs时,你提供额外的命名参数为给定的view签名。使用相同的例子,我们的category视图签名改变,使得它现在看起来像下面这样。
def category(request, category_name_slug):
# ... code here ...

这不是附加参数位置的问题,它的名称必须匹配任何定义的URL模式。注意如何在URL模式category_name_slug定义与为我们示例所定义的category_name_slug参数定义相匹配。在我们视图中使用category_name_slug获取python-books,或任何值提供作为URL的一部分。

注释
正则表达式刚开始可能看起来可怕的和令人困惑,但网上有大量的资源来帮助你。这Cheat sheet是固定的正则表达式的问题一个很好的资源。

修改索引模板

       我们的新视图已经配置和准备就绪-但是我们需要做更多的事。我们的索引页面模板需要更新为用户提供一种方法来查看列出的类别页面。我们可以更新index.html模板现在包括一个通过slug链接到分类页。

<!DOCTYPE html><html>    <head>        <title>Rango</title>    </head>    <body>        <h1>Rango says..hello world!</h1>        {% if categories %}            <ul>                {% for category in categories %}                <!-- Following line changed to add an HTML hyperlink -->                <li><a href="/rango/category/{{ category.slug }}">{{ category.name }}</a></li>                {% endfor %}            </ul>       {% else %}            <strong>There are no categories present.</strong>       {% endif %}    </body></html>

       在这里,我们已经更新了每个列表元素(<li>)加入HTML超链接(<a>)。 超链接的href属性,我们使用它来指定目标URL定义为{{ category.slug }}。 

Demo

       现在让我们尝试通过访问Rango的主页显示输出。你可以看到主页列表的所有分类。这些分类可以点击链接。点击Python然后跳转到python的详细分类视图,如图2所示。如果你看到一系列链接像Official Python Tutorial,这时你成功设置新的视图。尝试导航一个不存在的分类,像/rango/category/computers。您应该看到一条消息告诉你,没有页面中存在在分类中。
图2
Figure 2: What your link structure should now look like. Starting with the Rango homepage, you are then presented with the category detail page. Clicking on a page link takes you to the linked website.

练习

通过尝试下面的练习加强你在本章所学的知识。

  • 修改索引页面,也包括页面浏览最多的前5位。
  • 承接Django官方教程的第三部分,如果你还没有这样做的话,进一步巩固你学到的知识。

提示

为了帮助你完成上面的练习,下面的提示可能对你有用。 祝好运!

  • 更新填充脚本加入一些值到视图去记录每一页面。

原文:http://www.tangowithdjango.com/book17/chapter/models_templates.html

0 0