网络角色视图教程

在进行本教程之前,您可能想先阅读更改网页教程中的介绍。

在本教程中,我们将创建一个网页,用于显示游戏角色的统计信息。为此,以及我们希望制作的所有其他特定于游戏的页面,我们需要创建自己的 Django 应用程序。我们将把应用程序命名为 character,因为它将处理角色信息。从您的游戏目录中运行以下命令:

evennia startapp character

这将在 mygame 中创建一个名为 character 的新目录。为了保持整洁,让我们将其移动到 web/ 子目录中。

mv character web  # (linux/mac)
move character web # (windows)

我们将其放在 web/ 中以保持整洁,但您可以将其放在您喜欢的任何位置。它包含 Django 应用程序所需的所有基本文件。

请注意,我们不会编辑此新目录中的所有文件,许多生成的文件超出了本教程的范围。

为了让 Django 找到我们的新 Web 应用程序,我们需要将其添加到 INSTALLED_APPS 设置中。Evennia 的默认安装应用程序已经设置,因此在 server/conf/settings.py 中,我们只需扩展它们:

INSTALLED_APPS += ('web.character',)

注意:末尾的逗号很重要。它确保 Python 将添加项解释为元组,而不是字符串。

我们首先需要创建一个 视图 和一个 URL 模式 来指向该视图。视图是生成访问者想要查看的网页的函数,而 URL 模式则让 Django 知道哪个 URL 应该触发该视图。该模式也可能提供一些自己的信息,正如我们将看到的那样。

这是我们的 character/urls.py 文件(注意:如果未为您生成空白文件,您可能需要创建此文件):

# URL patterns for the character app

from django.urls import path
from web.character.views import sheet

urlpatterns = [
    path("sheet/<int:object_id>", sheet, name="sheet")
]

此文件包含应用程序的所有 URL 模式。urlpatterns 列表中的 url 函数有三个参数。第一个参数是一个模式字符串,用于识别哪些 URL 是有效的。模式以 正则表达式 形式指定。正则表达式用于匹配字符串,并以一种特殊的、非常紧凑的语法编写。关于正则表达式的详细描述超出了本教程的范围,但您可以在这里了解更多。目前,只需接受这个正则表达式要求访问者的 URL 看起来像这样:

sheet/123/

sheet/ 后跟一个数字,而不是其他可能的 URL 模式。我们将把这个数字解释为对象 ID。由于正则表达式的格式,模式识别器将该数字存储在名为 object_id 的变量中。这个变量将传递给视图(见下文)。我们在第二个参数中添加导入的视图函数(sheet)。我们还添加了 name 关键字以识别 URL 模式本身。您应该始终为 URL 模式命名,这样在使用 {% url %} 标签在 HTML 模板中引用时会很方便(但我们将在本教程中不进一步讨论)。

安全注意:通常,用户无法看到游戏中的对象 ID(仅限超级用户)。公开游戏的对象 ID 使公众能够执行所谓的 账户枚举攻击,以试图劫持您的超级用户账户。考虑这一点:在每个 Evennia 安装中,我们可以 始终 期望存在两个对象,并且具有相同的对象 ID—— Limbo (#2) 和您在开始时创建的超级用户 (#1)。因此,破坏者仅通过导航到 sheet/1 就可以获得劫持管理员账户所需信息的 50%。

接下来,我们创建 views.py,即 urls.py 引用的视图文件。

# Views for our character app

from django.http import Http404
from django.shortcuts import render
from django.conf import settings

from evennia.utils.search import object_search
from evennia.utils.utils import inherits_from

def sheet(request, object_id):
    object_id = '#' + str(object_id)
    try:
        character = object_search(object_id)[0]
    except IndexError:
        raise Http404("I couldn't find a character with that ID.")
    if not inherits_from(character, settings.BASE_CHARACTER_TYPECLASS):
        raise Http404("I couldn't find a character with that ID. "
                      "Found something else instead.")
    return render(request, 'character/sheet.html', {'character': character})

正如前面所解释的,urls.py 中的 URL 模式解析器解析 URL 并将 object_id 传递给我们的视图函数 sheet。我们使用这个数字对对象进行数据库搜索。我们还确保这样的对象存在,并且它实际上是一个角色。视图函数还会传递一个 request 对象。这为我们提供了关于请求的信息,例如是否有登录用户在查看它——尽管我们在这里不会使用该信息,但记住这一点是很好的。

在最后一行中,我们调用 render 函数。除了 request 对象,render 函数接受一个 HTML 模板的路径和一个字典,您希望将额外数据传递给该模板。作为额外数据,我们传递了刚找到的角色对象。在模板中,它将作为变量 “character” 可用。

HTML 模板被创建为 templates/character/sheet.html,应放在您的 character 应用程序文件夹下。您可能需要手动创建 template 及其子文件夹 character。以下是要创建的模板:

{% extends "base.html" %}
{% block content %}

    <h1>{{ character.name }}</h1>

    <p>{{ character.db.desc }}</p>

    <h2>Stats</h2>
    <table>
      <thead>
        <tr>
          <th>Stat</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Strength</td>
          <td>{{ character.db.str }}</td>
        </tr>
        <tr>
          <td>Intelligence</td>
          <td>{{ character.db.int }}</td>
        </tr>
        <tr>
          <td>Speed</td>
          <td>{{ character.db.spd }}</td>
        </tr>
      </tbody>
    </table>

    <h2>Skills</h2>
    <ul>
      {% for skill in character.db.skills %}
        <li>{{ skill }}</li>
      {% empty %}
        <li>This character has no skills yet.</li>
      {% endfor %}
    </ul>

    {% if character.db.approved %}
      <p class="success">This character has been approved!</p>
    {% else %}
      <p class="warning">This character has not yet been approved!</p>
    {% endif %}
{% endblock %}

在 Django 模板中,{% ... %} 表示 Django 理解的特殊 “函数”。{{ ... }} 块充当 “插槽”。它们会被代码块内部返回的任何值替换。

第一行 {% extends "base.html" %} 告诉 Django 此模板扩展了 Evennia 使用的基础模板。基础模板由主题提供。Evennia 附带了开源的第三方主题 prosimii。您可以在 evennia/web/templates/prosimii 中找到它及其 base.html 文件。像其他模板一样,这些模板可以被覆盖。

接下来的行是 {% block content %}base.html 文件具有 block,这是模板可以扩展的占位符。主要的块(也是我们使用的)被命名为 content

在模板中,我们可以在任何地方访问 character 变量,因为我们在 views.pyrender 调用中传递了它。 这意味着我们同样可以访问角色的 db 属性,就像在普通的 Python 代码中一样。您无法在模板中调用带参数的函数——实际上,如果您需要执行任何复杂的逻辑,应该在 views.py 中进行,然后将结果作为更多变量传递给模板。但您仍然在展示数据时有相当大的灵活性。

我们也可以在这里进行一些小逻辑。我们使用 {% for %} ... {% endfor %}{% if %} ... {% else %} ... {% endif %} 结构来更改模板的渲染方式,具体取决于用户的技能数量,或者用户是否获得批准(假设您的游戏有一个审批系统)。

最后,我们需要编辑的文件是主 URL 文件。这是为了将来自您新 character 应用程序的 URLs 平滑集成到 Evennia 的现有页面的 URLs 中。找到文件 web/website/urls.py 并更新其 patterns 列表如下:

# web/website/urls.py

urlpatterns = [
    # ...
    path("character/", include('web.character.urls'))
]

现在,通过运行 evennia reload 重新加载服务器,并在浏览器中访问该页面。如果您没有更改默认设置,您应该能够在 http://localhost:4001/character/sheet/1/ 找到角色 #1 的详情。

尝试在游戏中更新统计数据,并在浏览器中刷新页面。结果应该立即显示。

作为可选的最后一步,您还可以更改角色类型类,使其拥有一个名为 get_absolute_url 的方法。

# typeclasses/characters.py

# 在 Character 内部
def get_absolute_url(self):
    from django.urls import reverse
    return reverse('character:sheet', kwargs={'object_id': self.id})

这样将在 Django 管理对象更改页面的右上角给您一个“在网站上查看”按钮,该按钮链接到您的新角色表单,并允许您通过在任何具有给定对象的模板中使用 {{ object.get_absolute_url }} 来获取角色页面的链接。

现在您已经使用 Django 创建了一个基本页面和应用程序,您可能希望阅读完整的 Django 教程,以更好地了解它的功能。您可以在这里找到 Django 的教程.