Views, Templates, and Static Files

目前我们已经有一个名为 home“Hello, World!”的视图。在我们应用程序的主页中。

myproject/urls.py

from django.conf.urls import url
from django.contrib import admin

from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^admin/', admin.site.urls),
]

boards/views.py

from django.http import HttpResponse

def home(request):
    return HttpResponse('Hello, World!')

我们可以以此为起点。如果您还记得我们的线框,图 5显示了主页应该是什么样子。我们想要做的是在表格中显示板列表以及其他一些信息。

首先要做的是导入boards models并列出所有现有的boards:

boards/views.py

from django.http import HttpResponse
from .models import Board

def home(request):
    boards = Board.objects.all()
    boards_names = list()

    for board in boards:
        boards_names.append(board.name)

    response_html = '<br>'.join(boards_names)

    return HttpResponse(response_html)

结果将是这个简单的 HTML 页面:

板主页 HttpResponse

但让我们就此打住。我们不会像这样渲染 HTML。对于这个简单的视图,我们只需要一个板列表;那么渲染部分是Django Template Engine 的工作

Django templates引擎设置

在board和mysite文件夹旁边创建一个名为templates 的新文件夹:

myproject/
|-- myproject/
| |-- boards/
| |-- myproject/
| |-- templates/ <-- here!
| +-- manage.py
+-- venv/

现在在templates 文件夹中,创建一个名为home.html 的 HTML 文件:

*templates/home.html*

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Boards</title>
  </head>
  <body>
    <h1>Boards</h1>

    {% for board in boards %}
      {{ board.name }} <br>
    {% endfor %}

  </body>
</html>

在上面的示例中,我们将原始 HTML 与一些特殊标签和 . 它们是 Django 模板语言的一部分。上面的例子展示了如何使用. 该呈现在HTML模板基板的名称,生成动态HTML文档。<span class="p">{</span><span class="err">%</span><span class="w"><span> </span></span><span class="err">for</span><span class="w"><span> </span></span><span class="err">...</span><span class="w"><span> </span></span><span class="err">in</span><span class="w"><span> </span></span><span class="err">...</span><span class="w"><span> </span></span><span class="err">%</span><span class="p">}</span>`<span class="p">{</span><span class="err">{</span><span class="w"><span> </span></span><span class="err">variable</span><span class="w"><span> </span></span><span class="p">}</span><span class="err">}</span>for<span class="p">{</span><span class="err">{</span><span class="w"><span> </span></span><span class="err">board.name</span><span class="w"><span> </span></span><span class="p">}</span><span class="err">}</span>`

在我们可以使用这个 HTML 页面之前,我们必须告诉 Django 在哪里可以找到我们应用程序的模板。

打开settings.py 里面的myproject 目录和搜索 TEMPLATES变量,并设置 DIRS 关键 os.path.join(BASE_DIR, 'templates')

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, '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',
            ],
        },
    },
]

基本上,这一行的作用是找到项目目录的完整路径并在其后附加“/templates”。

我们可以使用 Python shell 调试它:

python manage.py shell

from django.conf import settings

settings.BASE_DIR
'/Users/vitorfs/Development/myproject'

import os

os.path.join(settings.BASE_DIR, 'templates')
'/Users/vitorfs/Development/myproject/templates'

看?它只是指向我们在前面步骤中创建的templates文件夹。

现在我们可以更新我们的home视图:

boards/views.py

from django.shortcuts import render
from .models import Board

def home(request):
    boards = Board.objects.all()
    return render(request, 'home.html', {'boards': boards})

The resulting HTML:

Boards Homepage render

We can improve the HTML template to use a table instead:

templates/home.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Boards</title>
  </head>
  <body>
    <h1>Boards</h1>

    <table border="1">
      <thead>
        <tr>
          <th>Board</th>
          <th>Posts</th>
          <th>Topics</th>
          <th>Last Post</th>
        </tr>
      </thead>
      <tbody>
        {% for board in boards %}
          <tr>
            <td>
              {{ board.name }}<br>
              <small style="color: #888">{{ board.description }}</small>
            </td>
            <td>0</td>
            <td>0</td>
            <td></td>
          </tr>
        {% endfor %}
      </tbody>
    </table>
  </body>
</html>

Boards Homepage render

Testing the Homepage

Testing Comic

这将是一个反复出现的主题,我们将在整个教程系列中共同探索不同的概念和策略。

让我们编写我们的第一个测试。现在,我们将在board应用程序中的tests.py 文件中工作:

boards/tests.py

from django.core.urlresolvers import reverse
from django.test import TestCase

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

这是一个非常简单的测试用例,但非常有用。我们正在测试响应的状态代码 。状态码 200 表示成功

我们可以在控制台查看响应的状态码:

响应 200

如果有未捕获的异常、语法错误或其他任何事情,Django 将返回状态代码500 ,这意味着Internal Server Error 。现在,假设我们的应用程序有 100 个视图。如果我们为所有视图编写这个简单的测试,只用一个命令,我们将能够测试所有视图是否都返回成功代码,因此用户不会在任何地方看到任何错误消息。如果没有自动化测试,我们将需要逐页检查。

执行 Django 的测试套件:

python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.041s

OK
Destroying test database for alias 'default'...

现在我们可以测试 Django 是否为请求的 URL 返回了正确的视图函数。这也是一个有用的测试,因为随着开发的进行,您将看到urls.py 模块变得非常庞大和复杂。URL conf 都是关于解析正则表达式的。在某些情况下,我们有一个非常宽松的 URL,因此 Django 最终可能会返回错误的视图函数。

这是我们如何做到的:

boards/tests.py

from django.core.urlresolvers import reverse
from django.urls import resolve
from django.test import TestCase
from .views import home

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

    def test_home_url_resolves_home_view(self):
        view = resolve('/')
        self.assertEquals(view.func, home)

在第二个测试中,我们正在使用该 resolve功能。Django 使用它来将请求的 URL 与urls.py 模块中列出的 URL 列表进行匹配。此测试将确保作为 /根 URL 的 URL 返回主视图。

再测试一下:

python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.027s

OK
Destroying test database for alias 'default'...

要查看有关测试执行的更多详细信息,请将详细程度 设置为更高级别:

python manage.py test --verbosity=2
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Operations to perform:
  Synchronize unmigrated apps: messages, staticfiles
  Apply all migrations: admin, auth, boards, contenttypes, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying boards.0001_initial... OK
  Applying sessions.0001_initial... OK
System check identified no issues (0 silenced).
test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok
test_home_view_status_code (boards.tests.HomeTests) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.017s

OK
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

Verbosity 确定将打印到控制台的通知和调试信息的数量;0 是无输出,1 是正常输出,2 是详细输出。

最后修改:2021 年 06 月 14 日
如果觉得我的文章对你有用,请随意赞赏