OverIQ 中文系列教程(十四)

原文:OverIQ Tutorials

协议:CC BY-NC-SA 4.0

创建 Django 项目

原文:https://overiq.com/django-1-11/creating-django-project/

最后更新于 2020 年 7 月 27 日


创建项目

要创建新的 Django 项目,请确保虚拟环境处于活动状态,并且您当前的工作目录设置为djangobin,然后发出以下命令:

$ django-admin startproject django_project

该命令将在djangobin目录内创建一个名为django_project的新目录。django_project目录也称为 Django 项目根目录或简称项目根目录。它的目录结构应该如下所示:

django_project/
├── django_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

1 directory, 5 files

在顶层,我们新创建的django_project目录包含以下两项。

  • 与项目根目录同名的目录,即django_project
  • 一个叫做manage.py的 Python 脚本。

目录django_project/django_project被称为项目配置目录。在其中,您会发现以下四个 python 文件:

文件名 描述
__init__.py 一个特殊的空文件,告诉 Python 当前目录应该被视为 Python 包。
settings.py 这个文件包含了 Django 项目的所有设置和配置。
urls.py 一个 Python 脚本,用于存储 Django 项目的 URL 模式。
wsgi.py 运行开发服务器并将项目部署到生产环境的 Python 脚本。

manage.py -一个命令行工具,用于与您的 Django 项目进行交互或维护。我们可以通过简单地执行它来查看该文件提供的各种参数(或子命令)。

$ ./django_project/manage.py 

Type 'manage.py help <subcommand>' for help on a specific subcommand.

Available subcommands:

[auth]
    changepassword
    createsuperuser

[contenttypes]
    remove_stale_contenttypes

[django]
    check
    compilemessages
    createcachetable
    ...    
    startapp
    startproject
    test
    testserver

[sessions]
    clearsessions

[staticfiles]
    collectstatic
    findstatic
    runserver

如果您在 Windows 上,请始终在 Python 文件前面加上python命令:

C:\Users\overiq\djangobin> python django_project/manage.py

最基本的子命令之一是runserver(列表中最后一个),用于运行 Django 开发 web 服务器。让我们看看它是如何完成的。

首先使用cd命令将当前工作目录更改为manage.py所在的目录,然后执行runserver子命令,如下所示:

$ cd django_project
$ ./manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

March 19, 2018 - 08:23:19
Django version 1.11, using settings 'django_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

这里有几点需要注意。第一行:

System check identified no issues (0 silenced).

表示在您的 Django 项目中没有发现错误。如果有错误,那么./manage.py runserver将无法启动服务器。这里需要注意的第二点如下:

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

这条线告诉我们项目中有一些未应用的迁移。在 Django,我们使用迁移来改变数据库的状态。不要太担心什么是迁移,我们将在后面的章节中深入讨论。

第三点也是目前最重要的一点是 Django 开发服务器的地址,即http://127.0.0.1:8000/。打开你喜欢的浏览器,访问http://127.0.0.1:8000/。您应该会看到这样的页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

停止开发服务器点击CTRL+C

默认情况下manage.py总是在端口 8000 启动开发服务器,但是您可以使用以下命令将其更改为其他端口。

$ ./manage.py runserver <port>

例如./manage.py runserver 3000将在 3000 号港口启动 Django 开发。如果您想打开特定 IP 地址的端口,可以使用以下命令轻松实现:

./manage.py runserver <ip-address>:<port>

例如,要在端口 4444 上的 localhost 运行 Django 开发服务器,请发出以下命令。

$ ./manage.py runserver 127.0.0.1:4444

每次修改 Python 代码后,Django 开发服务器都会自动重启。因此,您不需要每次都手动重新启动服务器来使更改生效。但是,有些操作,如添加文件,不会触发重启。在这种情况下,您必须手动重新启动服务器。

设置数据库

Django 可以与几乎所有流行的数据库一起使用,如 MySQL、Oracle、PostgreSQL(这是 Django 开发人员的首选)、SQLite 等。由于这是一个初学者教程,我们将使用 SQLite 数据库。但是为什么是 SQLite 呢?因为安装和配置 SQLite 数据库不需要额外的步骤。SQLite 已经与 Python 捆绑在一起,因此您不必配置任何东西来使用它。

第一次执行runserver命令时,Django 会在项目根目录(即djangobin/django_project)中自动创建一个名为db.sqlite3的 SQLite 数据库。我们已经执行了一次runserver命令,所以此时,您的项目根目录(djangobin/django_project)中应该有一个名为db.sqlite3的 SQLite 数据库文件。

django_project/
├── db.sqlite3   <-- SQLite database
├── django_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

如果我们没有使用 SQLite 数据库,那么我们可以很容易地在位于项目配置目录(djangobin/django_project/django_project)中的settings.py文件中指定数据库配置。如前所述,settings.py文件包含与我们的 Django 项目相关的所有设置。

打开settings.py文件,滚动到一半,直到找到
DATABASES设置。DATABASES变量包含所有数据库特定的设置。DATABASES变量的默认值应该是这样的:

djangobin/django _ project/django _ project/settings . py

#...

DATABASES = {
    
    
    'default': {
    
    
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

#...

ENGINE指定 Django 正在使用的数据库后端的类型。在我们的例子中,我们使用的是 SQLite,这就是为什么它被设置为django.db.backends.sqlite3。以下是一些其他后端可供选择:

数据库ˌ资料库 发动机
关系型数据库 django.db.backends.mysql
神谕 django.db.backends.oracle
一种数据库系统 django.db.backends.postgresql

NAME指定我们正在使用的数据库的名称。由于 SQLite 数据库由单个文件组成,所以它当前指向db.sqlite3文件的绝对路径。这就是我们使用 SQLite 数据库所需要的。

要使用像 MySQL 或 PostgreSQL 这样的数据库,我们需要添加一些额外的参数,如USERPASSWORDHOSTPORT。例如,下面是如何配置 Django 来使用 MySQL 数据库。

DATABASES = {
    
    
    'default': {
    
    
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'database_name',
        'USER': 'username',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Django 应用

在 Django,项目和应用意味着不同的东西。根据 Django 术语,Django 项目是配置和应用的集合,这些配置和应用共同组成了整个网络应用。换句话说,一个项目是一个完整的网络应用,而一个应用只是一个特性。例如,应用可以是博客、评论系统、论坛、聊天室,甚至是联系人表单。所有这些小应用和配置共同构成了一个 Django 项目。

Django 内置应用

Django 已经捆绑了几个内置应用。要查看这些内置应用,请查看settings.py文件顶部的INSTALLED_APPS设置。

djangobin/django _ project/django _ project/settings . py

#...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

#...

如您所见,默认安装了 6 个应用,它们都随 Django 一起提供。下面是每个应用的概要。

  • django.contrib.admin-一个管理站点。
  • django.contrib.auth——用户管理和认证的框架。
  • django.contrib.contenttypes——内容类型的框架。
  • django.contrib.sessions-一个会话框架。
  • django.contrib.messages-一个消息传递框架。
  • django.contrib.staticfiles–管理静态文件的框架。

其中一些应用需要数据库表,而另一些则不需要。回想一下,当我们试图运行 Django 开发服务器时,我们得到了以下警告。

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them

此警告告诉我们“未应用的迁移”。未应用的迁移意味着在所有应用成功运行之前,需要提交一些更改。迁移只是普通的 Python 文件,它描述了我们想要提交给数据库的更改。这些更改可能像重命名列一样微不足道,也可能像创建或更改数据库模式一样复杂。在任何时候,我们都可以使用manage.pyshowmigrations子命令来获取已应用和未应用迁移的列表。

$ ./manage.py showmigrations
admin
 [ ] 0001_initial
 [ ] 0002_logentry_remove_auto_add
auth
 [ ] 0001_initial
 [ ] 0002_alter_permission_name_max_length
 [ ] 0003_alter_user_email_max_length
 [ ] 0004_alter_user_username_opts
 [ ] 0005_alter_user_last_login_null
 [ ] 0006_require_contenttypes_0002
 [ ] 0007_alter_validators_add_error_messages
 [ ] 0008_alter_user_username_max_length
contenttypes
 [ ] 0001_initial
 [ ] 0002_remove_content_type_name
sessions
 [ ] 0001_initial

前面的输出显示了所有已安装应用下的未应用迁移文件列表(不带.py扩展名)。我们知道这些迁移未被应用,因为迁移名称前面的方括号([])未被选中。如果它们被应用,我们会在迁移名称前面看到[x]。从输出中我们还可以推断出django.contrib.admin有 2 个未应用的迁移,django.contrib.auth有 8 个,django.contrib.contenttypes有 2 个,django.contrib.sessions有 1 个未应用的迁移。为了应用这些迁移,我们使用migrate子命令。

在终端或命令提示符下,输入以下命令以应用迁移。

$ ./manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
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 sessions.0001_initial... OK

再次运行./manage.py showmigrations命令。这次您将在每个迁移文件前面看到[x],因为我们刚刚应用了挂起的迁移。

$ ./manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
$

在这一点上,我们并不期望您完全理解迁移。我们将在第课“T2 Django 的移民”中详细了解他们。现在,请记住这一点——在 Django,我们使用迁移来创建/更改/删除数据库中的表。我们将在下一节检查由migrate子命令创建的表。

使用runserver子命令再次运行开发服务器。这一次,您将看不到任何关于未应用迁移的警告。

$ ./manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).
March 19, 2018 - 10:01:48
Django version 1.11, using settings 'django_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[19/Mar/2018 10:01:56] "GET / HTTP/1.1" 200 1716

没有必要使用 Django 提供的所有应用。如果您不想使用任何特定的应用,只需将其从列表中删除即可。假设由于某种原因您不想使用django.contrib.staticfiles应用,在移除django.contrib.staticfiles后,INSTALLED_APPS列表将如下所示:

djangobin/django _ project/django _ project/settings . py

#...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',    
]
#...

但是现在我们什么都需要,所以把django.contrib.staticfiles加回INSTALLED_APPS列表。最后,INSTALLED_APPS的设定应该是这样的:

djangobin/django _ project/django _ project/settings . py

#...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',    
    'django.contrib.staticfiles',
]
#...

查看数据库

在本教程中,我们将使用名为 Navicat 的图形数据库管理程序不时查看我们的 SQLite 数据库的当前状态。像 Navicat 这样的程序使查看表格、以图形方式创建/更改记录变得非常容易,而无需编写一行 SQL。

但是,如果你是一个 SQL 专业人员,并且熟记 SQL 命令,你可以自由地从命令提示符或终端打开db.sqlite3文件,并从那里继续。

Navicat 不是免费软件,但是他们提供 30 天的试用期。还有许多其他的免费软件 Sqlite Browser,HeidiSQL,Sqlectron,Valentina Studio,DBeaver 等等,它们可以让你做或多或少相同的事情。

以下是使用 Navicat 打开 SQLite 数据库的说明,其他程序的说明大致相同。

启动 Navicat 高级版,然后转到连接> SQLite。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在新建连接窗口中,指定连接名称,选择“现有数据库文件”,浏览db.sqlite3文件所在位置。跳过密码字段,单击窗口底部的“测试连接”按钮。如果连接成功,请关闭对话框并单击“确定”按钮保存连接。如果遇到任何错误,请检查 SQLite 数据库文件的位置,然后重试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要查看表格,请单击左侧窗格中的连接名称,然后双击“main”。您应该会在窗口的右侧看到一个表格列表,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,目前 SQLite 数据库中有 11 个表。

正在创建 Django 应用

要创建一个新的应用,首先要确保你当前的工作目录与manage.py文件所在的目录相同。之后,执行以下命令。

$ ./manage.py startapp djangobin

该命令将在项目根目录(djangobin/django_project)内创建一个名为djangobin的新目录。我们新创建的目录的目录结构如下:

djangobin/
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

1 directory, 7 files

以下是每个文件和文件夹的摘要:

文件名 描述
admin.py 该文件包含将 djangobin 应用连接到 Django 提供的管理应用所需的所有配置。我们将在第课 Django 管理应用中详细学习如何操作。
apps.py 该文件包含特定于应用的配置。
__init__.py __init__.py文件只是 Python 把这个(djangobin)目录当作一个包的说法。
migrations 这个目录将存储我们所有的迁移文件。换句话说,它会将我们所做的所有更改存储在数据库中。
migrations/__init__.py __init__.py文件只是 Python 的说法,把这个(migrations)目录当成一个包。
models.py 这个文件包含我们的应用模型。换句话说,这是我们定义表和它们之间关系的地方。
test.py 这个文件包含对我们的应用进行单元测试的功能。
views.py 该文件包含处理请求和返回响应的视图函数。

我们已经创建了一个新的应用djangobin,现在我们必须通知我们的 Django 项目它的存在。为此,打开位于项目配置目录(djangobin/django_project/django_project)中的settings.py文件,并在INSTALLED_APPS列表的末尾追加'djangobin',,如下所示:

djangobin/django _ project/django _ project/settings . py

#...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'djangobin',
]

#...

为了验证 Django 是否获得了我们新的djangobin应用,请重新启动服务器。如果您可以成功启动服务器而没有任何错误,那么这意味着您已经为下一步做好了准备。



MVC 模式与 Django

原文:https://overiq.com/django-1-11/mvc-pattern-and-django/

最后更新于 2020 年 7 月 27 日


MVC 模式

在 MVC 框架出现之前,web 编程将数据库代码与页面的服务器端代码混合在一起。如果你已经用像 PHP 这样的语言编程了一段时间,你已经在某种程度上做到了。这个问题并不是 PHP 特有的;事实上,在大多数服务器端语言中,代码至少在三种语言之间共享,例如 Python(或 PHP)、SQL、HTML。

创建 MVC 模式是为了将业务逻辑与表示分离开来。MVC 是当今最流行的架构。许多流行的框架,如 Ruby on Rails、Laravel、CodeIgniter 甚至 Django 都使用它。MVC 架构将应用分为以下三层:

  1. 模特。
  2. 查看。
  3. 控制器。

让我们分别讨论它们。

**模型:**模型表示数据在数据库中的组织方式。换句话说,在 MVC 模式中,我们使用模型来定义我们的数据库表以及它们之间的关系。

**视图:**视图是当你访问一个站点时看到的内容。例如,一篇博客文章,一个联系表等等,都是视图的例子。视图包含最终将发送给客户端的所有信息,即网络浏览器。通常,视图是 HTML 文档。

**控制器:**控制器控制信息流。当您请求一个页面时,该请求被传递给控制器,然后它使用编程逻辑来决定需要从数据库中提取什么信息以及应该将什么信息传递给视图。控制器是 MVC 架构的核心,因为它充当了模型和视图之间的粘合剂。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面是一个 MVC 博客应用中涉及的步骤的概要。

  1. 网络浏览器或客户端将请求发送到网络服务器,要求服务器显示博客文章。
  2. 服务器接收的请求被传递给应用的控制器。
  3. 控制器要求模型获取博客文章。
  4. 模型将博客文章发送给控制器。
  5. 然后,控制器将博客文章数据传递给视图。
  6. 该视图使用博客文章数据来创建一个 HTML 页面。
  7. 最后,控制器将 HTML 内容返回给客户端。

MVC 模式不仅帮助我们创建和维护一个复杂的应用。当涉及到关注点的分离时,它真的会发光。例如,在一个网络开发公司中,有网络设计师,也有开发人员。网页设计师的工作是创建视图。开发人员采用这些视图,并将它们与模型和控制器结合在一起。

决哥 MTV

Django 非常接近 MVC 模式,但是它使用的术语略有不同。Django 本质上是一个 MTV(模型-模板-视图)框架。Django 使用术语“视图模板”和“控制器视图”。换句话说,在 Django 中,视图被称为模板,控制器被称为视图。因此,我们的 HTML 代码将在模板中,Python 代码将在视图和模型中。



Django 的视图和 URL 配置

原文:https://overiq.com/django-1-11/views-and-urlconfs-in-django/

最后更新于 2020 年 7 月 27 日


在前面的章节中,我们已经学习了如何设置 Django 项目和运行开发服务器。在本章中,我们将学习在 Django 中创建动态网页的基础知识。

创建第一个视图

让我们从简单开始。在本节中,我们将创建一个输出“你好,Django”的网页。为此,请在您最喜欢的文本编辑器中打开位于 djangobin 应用(即djangobin/django_project/djangobin)中的views.py。此时,views.py应该是这样的:

djangobin/django_project/djangobin/views.py

from django.shortcuts import render

# Create your views here.

删除所有内容并输入以下代码。

djangobin/django_project/djangobin/views.py

from django.shortcuts import HttpResponse

def index(request):
    return HttpResponse("<p>Hello Django</p>")

我们刚刚创建了一个简单的视图函数。

那么什么是视图函数呢?

一种功能,其工作是接受请求并返回正确的响应。

让我们一次添加一行代码。

  1. 在第 1 行,我们正在从django.shortcuts模块导入HttpResponse类。
  2. 在第 4-5 行,我们定义了index()函数,该函数返回一个HttpResponse对象的实例。

每个视图函数都有一个名为request的参数,这是一个类型为HttpRequest的对象。HttpRequest对象包含触发此视图的当前 web 请求的信息。

视图函数必须返回一个HttpResponse对象。要创建HttpResponse对象,只需将表示页面内容的字符串传递给构造函数。

现在我们已经创建了一个简单的视图。要调用这个视图,您必须在 URL 配置中创建一个 URL 模式,简称为 URLconf。你可以把 URLconf 看作是 Django 支持的网络应用的目录。换句话说,URLconf 是 URL 和应该为这些 URL 调用的视图函数之间的映射。这是 Django 的说法,这个 URL 调用这个视图函数,那个 URL 调用那个视图函数,等等。

我们使用url()函数创建网址模式。它接受两个参数,一个匹配网址的正则表达式和为该网址调用的视图函数的名称。

让我们创建一个网址模式。打开位于 Django 项目配置目录中的urls.py文件(即djangobin/django_project/django_project)。这个文件也被称为全网站urls.pyurls.py文件的内容应该是这样的。

djangobin/django _ project/django _ project/URLs . py

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

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

**注意:**为了节省空间,从上面的代码片段中删除了注释。

要将index()视图函数绑定到 URL 模式,我们需要做两件事:

  1. 在导入列表的末尾添加from djangobin import views

  2. 通过在urlpatterns列表的开头添加以下一行来创建新的 URL 模式。

    url(r'^$', views.index),
    
    

站点范围内urls.py文件的内容现在应该如下所示:

djangobin/django _ project/django _ project/URLs . py

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

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

如果尚未运行,使用./manage.py runserver命令启动 Django 开发服务器,并访问http://127.0.0.1:8000/。您应该会看到这样的页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

传递给url()函数的第一个参数是正则表达式字符串,第二个参数是视图函数的名称。正则表达式r'^$'什么都不匹配。换句话说,r'^$'指的是网站的根。如果我们的域是http://example.com/,那么对http://example.com/的请求将调用index()查看功能。当用户请求http://example.com/时,Django 使用urlpatterns列表来决定调用哪个方法。当它找到匹配的网址模式时,它会调用与该模式相关联的视图函数。如果没有找到匹配的模式,将返回一个 HTTP 404 错误。

别忘了 Django 所说的视图实际上是一个控制器。

url()函数接受许多其他可选参数,其中一个参数是name关键字参数。name关键字参数允许我们为 URL 模式指定一个唯一的名称。那么我们为什么要给我们的网址模式命名呢?定义名称允许我们在模板和视图中自动创建网址。我们将在第课中看到如何在 Django创建网址。现在,让我们给我们新创建的网址模式命名。

djangobin/django _ project/django _ project/URLs . py

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

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

Django 404 错误

我们的 URLconf 目前只包含两种 URL 模式——一种是由 Django 提供的,另一种是我们自己编写的。那么如果你请求一个不同的网址会发生什么呢?打开浏览器,尝试访问http://127.0.0.1:8000/django/

如果请求的网址与 URLconf 中任何现有的网址模式都不匹配,那么 Django 将抛出一个 404 错误。由于请求的网址没有在 URLconf 中定义,Django 将抛出一个 HTTP 404 未找到错误。以下是它的外观:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关于这个页面需要注意的重要一点是,它给出的信息比要求的要多。请注意,它确切地告诉了 Django 在抛出 HTTP 404 错误之前尝试了哪些 URL 模式。当然,这是敏感信息,应该只向参与开发网络应用的人披露。

那么,如果这个信息是敏感的,那么为什么 Django 首先要透露这一切呢?

因为默认情况下 Django 项目安装时将DEBUG模式设置为True。要查看此设置,请在位于djangobin/django_project/django_project的 Django 项目配置目录中打开settings.py

djangobin/django _ project/django _ project/settings . py

#...

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '#=5=zv^cmqse-d=@#qp8f1bbto=235pz=we723*rt9is_$&hu)'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

#...

要关闭调试模式,请将DEBUG的值设置为False。当调试模式关闭时,Django 输出一个没有任何敏感信息的通用 HTTP 404 响应。由于我们目前处于开发阶段,请将DEBUG设置为True

以正确的方式映射网址

目前,我们的 Django 项目中只有一个名为 djangobin 的应用。一般来说,一个项目至少由 3-4 个应用组成。如果我们继续为整个网站的urls.py文件中的每个应用编写 URLconf,很快就会变得一团糟。因此,我们可以通过为每个应用创建“T2”来使我们的应用模块化,而不是直接将网址映射到整个站点urls.py,这样我们就可以更有效、更容易地管理网址。为此,首先在 djangobin 应用中创建urls.py文件,并向其中添加以下代码。

决哥/决哥 _ 项目/决哥/URL . py】

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

在第 1 行和第 2 行,我们正在导入必要的功能和模块。在第 4 行,我们正在为 djangobin 应用创建 URLconf。

下一步是将 djangobin 应用的 URLconf 通知给 Django 项目。为此,按如下方式修改站点范围内的urls.py:

djangobin/django _ project/django _ project/URLs . py

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

urlpatterns = [
    url(r'^index/', include('djangobin.urls')),
    url(r'^admin/', admin.site.urls),
]

include()功能告诉整个站点的urls.py文件在 djangobin 应用中存在urls.py。这里需要注意的重要一点是,在正则表达式中r'^index/'没有尾随的$字符,而是有一个尾随的斜线/。这样做是因为每当 Django 遇到include()函数时,它都会砍掉与该点匹配的网址部分,并将网址的剩余部分发送到包含的 URLconf 进行进一步处理。

但是,我们想在根网址http://127.0.0.1:8000/而不是http://127.0.0.1:8000/index/显示hello world,为此,请将正则表达式r'^index/'替换为r''

djangobin/django _ project/django _ project/URLs . py

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

urlpatterns = [
    url(r'', include('djangobin.urls')),
    url(r'^admin/', admin.site.urls),
]

打开浏览器,导航至http://127.0.0.1:8000/。Django 将以"Hello Django"回应迎接您。

深入挖掘网址

让我们看看当你请求一个页面时会发生什么。

  1. 当你请求一个页面时,Django 做的第一件事就是删除 URL 的主机部分,例如,在 URL https://overiq.com/contact/中,主机部分是overiq.com

  2. 然后,Django 从 Django 项目的settings.py文件中读取变量ROOT_URLCONF的值。那么ROOT_URLCONF有什么作用呢?

ROOT_URLCONF包含必须首先加载的 URLconf。它也被称为根 URLconf 或站点范围 URLconf。在我们的例子中,它指向位于djangobin/django_project/django_project目录中的urls.py

djangobin/django _ project/django _ project/settings . py

#...

ROOT_URLCONF = 'django_project.urls'

#...

  1. 读取ROOT_URLCONF后,Django 用请求的 URL 逐一检查根 URLconf 中的每个 URL 模式,直到找到匹配。如果没有找到模式,将返回 404 错误。

  2. 另一方面,如果找到一个模式,那么 Django 调用相关的视图函数。如果url()函数的第二个参数包含对include()函数的调用,那么 Django 会将网址中与该点匹配的部分去掉,并将网址的剩余部分发送到包含的 URLconf 进行进一步处理。

这里有一个例子:

假设请求的 URL 是http://example.com/time/,Django 做的第一件事就是移除 URL 的主机部分。剥离宿主部分后http://example.com/time/变成/time/。然后对字符串/time/逐个检查每个网址模式,直到找到匹配。如果找到匹配,则调用相应的视图函数。否则,将返回一个 HTTP 404 错误。

输出动态数据

我们在上面部分创建的视图函数非常简单,尽管如此,它还是向您介绍了一些基本概念,如视图函数、URL 模式以及 Django 如何在幕后处理 URL。在我们的下一个视图中,我们将输出一些动态内容。让我们创建一个简单的网页来输出当前的日期和时间。如果你做过一些 Python 编程,那么你可能已经知道 Python 有datetime模块来处理日期和时间。以下是如何使用它的快速复习:

>>>
>>> import datetime
>>>
>>> current_datetime = datetime.datetime.now()
>>>
>>> current_datetime
datetime.datetime(2017, 1, 24, 13, 59, 42, 135163)
>>>
>>> print(current_datetime)
2017-01-24 13:59:42.135163
>>>

需要注意的是,上面的代码片段是纯 Python,与 Django 无关。要返回当前日期和时间,我们首先必须创建一个新视图。在 djangobin app 中打开views.py,在index()视图功能下方新增一个名为today_is()的视图功能,如下所示:

djangobin/django_project/djangobin/views.py

from django.http import HttpResponse
import datetime

def index(request):
    return HttpResponse("<p>Hello Django</p>")

def today_is(request):
    now = datetime.datetime.now()
    html = "<html><body>Current date and time: {0}</body></html>".format(now)
    return HttpResponse(html)

让我们浏览一下在views.py文件中所做的更改:

  1. 在第 2 行,我们添加了一个导入语句来导入datetime模块,这样我们就可以计算当前的日期和时间。

  2. 在第 9-12 行,我们定义了today_is()函数。在第 10 行,我们通过调用now()方法计算当前日期和时间,并将结果分配给now变量。在第 11 行,我们使用字符串对象的format()方法创建一个 HTML 响应。字符串中的{0}只是当前日期和时间的占位符,将被变量now的值替换。需要注意的是,变量now代表的是一个datetime.datetime对象,而不是一个常规的字符串,但是当now的值代替{0}打印在字符串内部时,datatime.datetime对象的__str__()方法将datatime.datetime对象转换为字符串。最后,视图返回一个包含生成响应的HttpResponse()对象。

有了视图功能,让我们创建一个新的 URL 模式来调用这个视图。打开 djangobin app 的urls.py并添加以下 URL 模式调用today_is()查看功能如下:

决哥/决哥 _ 项目/决哥/URL . py】

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^time/$', views.today_is, name='time'),
    url(r'^$', views.index, name='index'),
]

我们添加了一个新的网址模式,将/time/网址映射到today_is()视图功能。你现在可能已经掌握了窍门。如果尚未运行,请启动服务器并访问http://127.0.0.1:8000/time/。如果一切顺利,Django 会告诉你现在的日期和时间。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建动态网址

动态网址是包含一个或多个影响网页输出的可变部分的网址。到目前为止,我们只创建了静态网址。

让我们创建另一个视图来显示用户配置文件。在views.py文件中添加profile()查看功能如下:

djangobin/django_project/djangobin/views.py

#...
def today_is(request):
    #...

def profile(request):
    return HttpResponse("<p>Profile page of user</p>")

如果用户访问网址路径/user/<username>/,我们希望显示用户配置文件,其中<username>是分配给每个用户的唯一字母数字标识符。

匹配字母数字字符的正则表达式是^[A-Za-z1-3]+$。因此,网址模式^/user/[A-Za-z0-9]+/$将匹配像/user/cs101//user/james//user/100ninja//user/1000等网址路径。但是不会匹配/user/string#$$$//user/high five//user/_10days/等 URL 路径。

我们的网址模式^/user/[A-Za-z0-9]+/$工作得很好,但是有一个问题。要显示用户配置文件,我们需要在查看功能的 URL 中访问用户名。有两种方法可以解决这个问题。第一种方法包含一点小技巧,第二种方法相当简单。

回想一下,视图函数的request参数包含触发视图的当前 web 请求的所有信息。request对象有一个名为path的属性,它返回主机名后的网址部分。例如,如果用户请求http://127.0.0.1:8000/user/foo/,那么request.path将返回/user/foo/。现在,你只需要去掉/user/和尾部斜线,你就完成了。

第二种解决方案很简单,因此推荐使用。为了将 URL 参数传递给视图函数,我们使用了一种叫做正则表达式组或简称为组的东西。我们使用以下语法创建命名组:

(?P<name>pattern)

name是 Python 标识符,用来指代匹配的子串,pattern是实际的正则表达式。Django 在幕后所做的是,它接受命名组,并将其值作为关键字参数传递给视图函数。以下是我们应用命名组后的完整网址模式:

url(r'^user/(?P<username>[A-Za-z0-9]+)/$', views.profile, name='profile')

将此网址模式添加到urlpatterns列表的末尾,如下所示:

决哥/决哥 _ 项目/决哥/URL . py】

#...

urlpatterns = [
    url(r'^time/$', views.today_is, name='todays_time'),
    url(r'^$', views.index, name='djangobin_index'),
    url(r'^user/(?P<username>[A-Za-z0-9]+)/$', views.profile, name='profile'),
]

接下来,更新profile()视图函数以接受名为username的附加参数,如下所示:

djangobin/django_project/djangobin/views.py

#...

def profile(request, username):
    return HttpResponse("<p>Profile page of #{}</p>".format(username))

从现在开始,像http://127.0.0.1:8000/user/foo/这样的 URL 请求将调用profile()视图函数,如下所示:

profile(request, username='foo')

访问http://localhost:8000/user/dragoon/会出现如下用户档案页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要注意的是,无论用于执行匹配的正则表达式是什么,由命名组匹配的子字符串总是作为字符串传递给视图函数。例如:

url(r'^user/(?P<user_id>[0-9]+)/$', views.profile),

在这个网址模式中,命名组只匹配整数。换句话说,网址模式将匹配类似/user/100//user/9999//user/1234567/等网址路径。但是profile()视图函数仍将使用字符串值调用:

profile(request, user_id='1000')

最后,这里有一些网址模式和它们匹配的网址:

网址模式 描述 示例网址
url(r'^post/\d+/$', viewname) 正则表达式\d+匹配一个或多个数字 该网址模式匹配类似/post/1//post/123//post/9999/等字符串。但是,与/post/a//post/123abc//post/!@#$%等不匹配;
url(r'^blog/(?P<slug>[\w-]+)/$', viewname) 正则表达式(?P<slug>[\w-]+)匹配一个或多个单词字符(字母数字或下划线)或破折号(-)。该正则表达式通常用于匹配 URL 中的 slug 该网址模式匹配类似/blog/a-long-hyphenated-title//blog/mypost//blog/1111/等字符串。但与/blog/?search=query//blog/%3Apython/等不匹配;
url(r'^archive/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', viewname) 正则表达式(?P<year>\d{4}+)/(?P<month>\d{2}+)/(?P<day>\d{2}+)/匹配 ISO 8601 格式的日期,即 YYYY-MM-DD。 该网址模式匹配类似/archive/2010/01/01//archive/5000/30/30//archive/9999/99/99/等字符串;但不匹配/archive/2018/5/5//archive/11/11/2018/等字符串;
url(r'^user/(?P<username>[\w.@+-]+)/$', viewname) 正则表达式(?P<username>[\w.@+-]+)匹配用户名和电子邮件。 该网址模式匹配类似/user/@draco//user/foobar007//user/[email protected]/等字符串;但不匹配/user//user/$w00t/等字符串;

将网址参数设为可选

偶尔,您会遇到希望将 URL 参数设为可选的情况。

假设我们想在 URL 路径/books/<category>/显示一个图书列表,其中<category>是图书类别的占位符。访问/books/self-help/会显示自助类书籍的列表。然而,访问/books/网址,会显示默认科幻类别的书籍。因此,网址路径/books/<category>/中的<category>是可选的。要完成此任务,请创建两个 URL 模式,一个不带可选参数,另一个带可选参数。

打开urls.py文件,添加如下两个网址模式:

决哥/决哥 _ 项目/决哥/URL . py】

urlpatterns = [
    url(r'^time/$', views.today_is, name='todays_time'),
    url(r'^$', views.index, name='djangobin_index'),
    url(r'^user/(?P<username>[A-Za-z0-9]+)/$', views.profile, name='profile'),
    url(r'^books/$', views.book_category, name='book_category'),
    url(r'^books/(?P<category>[\w-]+)/$', views.book_category, name='book_category'),
]

这里有两件事需要注意。第一,两种网址模式使用相同的视图功能。第二,两种网址模式的名称也是一样的。

接下来,在views.py中添加book_category查看功能如下:

djangobin/django_project/djangobin/views.py

#...
def profile(request, username):
    return HttpResponse("<p>Profile page of #{}</p>".format(username))

def book_category(request, category='sci-fi'):
    return HttpResponse("<p>Books in {} category</p>".format(category))

如果您现在访问http://localhost:8000/books/,您将看到默认sci-fi类别的书籍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

另一方面,如果您访问http://localhost:8000/books/action/,您将被显示来自action类别的书籍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将额外的参数传递给视图函数

除了 URL 参数,我们还可以向视图函数传递额外的参数,如字典。url()函数可以将可选的第三个参数作为字典,包含额外的关键字参数传递给视图函数。内置视图函数通常使用这种技术来定制默认行为(我们将在后面的章节中讨论)。

urls.py文件中,在urlpatterns列表的末尾添加名为extra_args的新网址模式:

决哥/决哥 _ 项目/决哥/URL . py】

urlpatterns = [
    #...
    url(r'^book/(?P<category>[\w-]+)/$', views.book_category, name='book_category'),
    url(r'^extra/$', views.extra_args, {
    
    'arg1': 1, 'arg2': (10, 20, 30)},  name='extra_args'),
]

接下来,在views.py文件的末尾添加一个名为extra_args的视图函数:

djangobin/django_project/djangobin/views.py

#...
def book_category(request, category='sci-fi'):
    return HttpResponse("<p>Books in {} category</p>".format(category))

def extra_args(request, arg1, arg2):
    return HttpResponse("<p>arg1: {} <br> arg2: {} </p>".format(arg1, arg2))

如果您现在访问http://localhost:8000/extra/,您将获得以下页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,extra_args()视图功能中的arg1arg2参数是必需的。未能通过它们将导致这样的TypeError:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以通过提供一些默认值来使arg1arg2成为可选的。修改extra_args()接受默认值如下:

djangobin/django_project/djangobin/views.py

#...

def extra_args(request, arg1=None, arg2=None):
    return HttpResponse("<p>arg1: {} <br> arg2: {} </p>".format(arg1, arg2))

extra_args网址模式中删除词典,访问http://localhost:8000/extra/。你会得到这样的回答:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

视图限制

到目前为止,我们已经能够使用音乐电视模式的视图部分完成一个基本的应用。但是,由于以下原因,我们的应用受到严重限制:

  1. 目前,我们正在视图中硬编码 HTML 代码。稍后,如果我们想修改我们的 HTML,逐个查看每个视图将是非常痛苦的。Django 附带了一个强大的模板系统,允许我们轻松创建复杂的 HTML 页面,而不是将它们硬编码在视图中。如果我们直接在视图中硬编码 HTML,我们将无法在 HTML 中使用 Django 模板系统提供的循环或条件语句(我们将在 Django 课程的模板标签中看到如何使用它们)。

  2. 在现实世界中,页面由许多动态组件组成。使用format()方法在大页面中嵌入动态内容非常容易出错且繁琐。

  3. 在这一点上,我们还没有将数据库引入场景,因为这会造成更多的混乱。

在接下来的章节中,我们将看到将模型和模板与视图结合使用如何帮助我们大大简化所有这些问题。



创建网址和自定义响应

原文:https://overiq.com/django-1-11/creating-urls-and-custom-response/

最后更新于 2020 年 7 月 27 日


反转网址模式

从 URL 模式的名称创建 URL 的过程称为反转 URL。那我们为什么要这么做呢?为什么不只是硬编码的网址?

因为在以后,如果你想更新网址结构,你必须手动访问每个 Python 模块和模板来进行更改。另一方面,如果你已经通过反转网址模式创建了网址,你只需要更新urls.py文件中的网址模式。模板和模块会自动选择变更。

反向()函数

要反转网址,我们使用reverse()功能。它接受网址模式的名称,并返回不带主机部分的网址。当reverse()无法反转网址时,会引发NoReverseMatch异常。要使用这个功能,我们首先要从django.shortcuts模块导入。

要查看动作中的reverse()方法,通过发出以下命令打开交互式 Python shell。

$ ./manage.py shell
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
>>>

注意到这里有些不同了吗?好了,这里我们使用./manage.py shell而不仅仅是python来启动 Python shell。但是为什么呢?因为除了调用 Python 解释器之外./manage.py shell还导入了最少的 Django 来使用。我们将这个 Shell 称为 Django Shell。

目前,我们的 djangobin 应用包含以下 6 种网址模式:

决哥/决哥 _ 项目/决哥/URL . py】

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^time/$', views.today_is, name='time'),
    url(r'^$', views.index, name='index'),
    url(r'^user/(?P<username>[A-Za-z0-9]+)/$', views.profile, name='profile'),
    url(r'^books/$', views.book_category, name='book_category'),
    url(r'^books/(?P<category>[\w-]+)/$', views.book_category, name='book_category'),
    url(r'^extra/$', views.extra_args, name='extra_args'),
]

让我们尝试在 Django shell 中逐个反转每个 URL 模式。

>>>
>>> from django.shortcuts import reverse
>>>
>>> 
>>> reverse("time")
'/time/'
>>> 
>>>
>>> reverse("index")
'/'
>>>
>>>
>>> reverse("book_category")
'/books/'
>>>
>>> 
>>> reverse("extra_args")
'/extra/'
>>> 
>>>

我们已经成功反转了 4 个网址。剩下的两种 URL 模式呢?

剩下的两个 URL 模式中有一个或多个动态组件,因此,我们需要传递这些组件来成功地反转它们。试图在不传递动态组件的情况下在这样的 URL 模式上调用reverse()会抛出NoReverseMatch异常。

>>> 
>>> reverse("profile")
...  
django.urls.exceptions.NoReverseMatch: Reverse for 'profile' with no arguments not found. 1 pattern(s) tried: ['user/(?P<username>[A-Za-z0-9]+)/$']
>>> 
>>>
Traceback (most recent call last):

还要注意,有两个同名的 URL 模式,book_category,第一个没有动态组件,第二个有一个动态组件。当我们在前面的例子中调用reverse("book_category")时,Django 足够聪明,可以推断出我们想要在没有动态组件的情况下反转 URL。

要反转动态网址模式,请将动态组件作为列表传递给args关键字参数。

reverse('url_pattern', args=['arg1', 'arg2'])

以下是一些例子:

>>> 
>>> 
>>> reverse("book_category", args=['horror'])
'/books/horror/'
>>> 
>>> 
>>> reverse("profile", args=['foobar'])
'/user/foobar/'
>>> 
>>>

我们还可以选择将动态组件作为字典传递给reverse():

reverse('url_pattern', kwargs={
    
    'arg1': 'val1', 'arg2': 'val2'})

以下是一些例子:

>>> 
>>> 
>>> reverse("book_category", kwargs={
    
    'category': 'crime'})
'/books/crime/'
>>> 
>>> 
>>> reverse("profile", kwargs={
    
    'username': 'tor'})
'/user/tor/'
>>> 
>>>

使用kwargs时,请记住密钥的名称必须与 URL 模式中的命名组的名称相同。否则会得到NoReverseMatch异常。

自定义响应

默认情况下,HttpResponse对象使用Content-Type:text/html头和 HTTP 状态代码 200 创建。我们可以分别使用content_typestatus关键字参数来更改内容类型标题和状态代码。

def custom_response(request):
    return HttpResponse("<p>Custom response 1</p>", content_type='text/plain')

这将使用内容类型text/plain向客户端发送响应。换句话说,客户端(浏览器)将按字面意思渲染文本<p>Custom response 1</p>,而不是将其解释为 HTML。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在构建 API 时,您将需要以 JSON 的形式发送数据。这可以通过将content_type标题设置为application/json来轻松完成。例如:

def custom_response(request):
    import json
    data = {
    
    'name': 'john', 'age': 25}
    return HttpResponse(json.dumps(data), content_type='application/json')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面是我们如何更改默认状态代码:

def custom_response(request):
    return HttpResponse("<h1>HTTP 404 Not Found</h1>", status=404)

该视图功能将返回 HTTP 404 未找到错误,内容为<h1>HTTP 404 Not Found</h1>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们还可以通过将HttpResponse实例视为字典来设置额外的头。

def custom_header(request):
    res = HttpResponse(status=302)
    res['location'] = 'http://example.com/'
    return res

该视图功能将使用临时重定向(HTTP 302 重定向)将用户重定向至http://example.com/

下面是另一个例子:

def custom_header(request):
    res = HttpResponse('some data')
    res['content-disposition'] = 'attachment; filename=file.txt;'
    return res

此查看功能将强制浏览器将响应视为文件附件。我们将使用完全相同的技术来下载代码片段。

HttpResponse 的常见子类

下表列出了HttpResponse的一些常见子类:

ClassName 描述
HttpResponseRedirect 它将重定向到路径作为第一个参数,并执行临时重定向(HTTP 状态代码 302)。路径可以是完全限定的(如http://example.com/contact)、绝对的(/books/crime/)或相对的(search/)。
HttpResponsePermanentRedirect HttpResponseRedirect相同,但执行永久重定向(HTTP 状态代码 301)
HttpResponseNotFound HttpResponse相同,但返回状态码 404。
HttpResponseForbidden HttpResponse相同,但返回状态码 403。
HttpResponseBadRequest HttpResponse相同,但返回状态码 400。
HttpResponseServerError HttpResponse相同,但返回状态码 500。

这些班级都住在django.http包里。但是也可以从django.shortcuts模块导入
HttpResponsePermanentRedirect类。

下面的清单演示了如何使用这些类:

from django.http import (
    HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseNotFound,
    HttpResponseBadRequest, HttpResponseForbidden, HttpResponseServerError)

def tem_redirect(request):
    return HttpResponseRedirect("http://example.com")

def perm_redirect(request):
    return HttpResponsePermanentRedirect("http://example.com")

def not_found(request):
    return HttpResponseNotFound("Not Found")

def forbidden(request):
    return HttpResponseForbidden("Request Forbidden - 403")

def bad_request(request):
    return HttpResponseBadRequest("Bad Request - 400")

def server_error(request):
    return HttpResponseServerError("Internal Server Error - 500")

触发 HTTP 错误的内置快捷方式

Django 还提供了一些从视图函数中显式触发 HTTP 错误的快捷方式。下表列出了一些异常及其触发的 HTTP 错误:

例外 HTTP 状态代码
django.http.Http404django.shortcuts.Http404 404 未找到
django.core.exceptions.SuspiciousOperation 400 个错误请求
django.core.exceptions.PermissionDenied 403 禁止
Exception 500 内部服务器错误

下面的列表显示了如何使用这些异常:

from django.http import Http404
from django.core.exceptions import SuspiciousOperation, PermissionDenied

def view_func1(request):
    if True: # some condition
        raise Http404  # show 404 Not Found
    else:
        return HttpResponse("hello")

def view_func2(request):
    if True: # some condition
        raise SuspiciousOperation # show 400 Bad Request
    else:
        return HttpResponse("hello")

def view_func3(request):
    if True: # some condition
        raise PermissionDenied # show 403 Forbidden
    else:
        return HttpResponse("hello")

def view_func4(request):
    if True: # some condition
        raise Exception # show 500 Internal Server Error
    else:
        return HttpResponse("hello")

DEBUGTrue时,所有这些异常都会显示一个描述问题性质的详细页面。例如,Http404异常将生成如下页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SuspiciousOperation异常会生成这样一个页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦我们进入生产(DEBUGFalse)它们都会返回一个通用的错误页面,只包含错误的名称,没有任何敏感的细节。例如,Http404异常将生成如下页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PermissionDenied异常会生成这样一个页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重定向()快捷方式

在前面几节中,我们已经看到了执行临时重定向(HTTP 状态代码 302)和永久重定向(HTTP 状态代码 301)的几种方法。redirect()是将用户重定向到不同网址的快捷功能。它接受要重定向到的网址模式或完全限定网址的网址路径或名称。要使用它,首先从django.shortcuts模块导入。

from django.shortcuts import redirect

以下查看功能将用户重定向至http://example.com

def view_func(request):
    return redirect('http://example.com')

默认情况下,redirect()执行临时重定向(HTTP 状态代码 302),以执行永久重定向通过permanent=True。例如:

def view_func(request):
    return redirect('http://example.com', permanent=True)

如果网址接受参数,将它们传递给网址模式名称后面的redirect()。例如:

def view_func(request):
    return redirect('profile', 'foobar', permanent=True)

该视图功能将使用永久重定向将用户重定向至/user/foobar/

命名空间 URL

随着项目的增长,您将很难防止 URL 模式之间的名称冲突。例如,假设我们有两个应用,一个论坛和一个博客。两者的urls.py文件中都有一个名为index的网址模式。那么,如果你试图使用reverse()功能来反转index的网址模式,会发生什么呢?

Django 将根据应用 URLConf 在全网站urls.py文件中注册的顺序生成网址。位于列表末尾的 URLConf 将覆盖其上方 URLConf 中同名的中的 URL 模式。

我们可以通过在 URL 模式中添加一个名称空间来解决这个问题。命名空间网址允许我们使用一个唯一的标识符来引用一组网址。

有两种方法可以添加命名空间:

  1. app_name属性。
  2. include()功能。

让我们探索这两种方法:

使用 app_name

从 djangobin 应用打开urls.py,定义urlpatterns列表正上方的app_name变量如下:

决哥/决哥 _ 项目/决哥/URL . py】

from django.conf.urls import url
from . import views

app_name = 'djangobin'

urlpatterns = [
    url(r'^time/$', views.today_is, name='time'),
    url(r'^$', views.index, name='index'),
    url(r'^user/(?P<username>[A-Za-z0-9]+)/$', views.profile, name='profile'),
    url(r'^books/$', views.book_category, name='book_category'),
    url(r'^books/(?P<category>[\w-]+)/$', views.book_category, name='book_category'),
    url(r'^extra/$', views.extra_args, name='extra_args'),
]

从现在开始,要反转网址,你必须在网址模式的名称前面加上djangobin,后面加上一个冒号(:)。

djangobin:url_name

下面的 shell 会话演示了名称空间 URL 模式的反转:

>>> 
>>> reverse("djangobin:time")
'/time/'
>>> 
>>> reverse("djangobin:profile", args=['foobar'])
'/user/foobar/'
>>> 
>>> 
>>> reverse("djangobin:book_category", args=['biography'])
'/books/biography/'
>>> 
>>> 
>>> reverse("djangobin:index")
'/'
>>> 
>>>

试图在不指定名称空间的情况下反转 URL 将导致NoReverseMatch异常:

>>> 
>>> reverse("index")
Traceback (most recent call last):
  ...
django.urls.exceptions.NoReverseMatch: Reverse for 'index' not found. 'index' is not a valid view function or pattern name.
>>> 
>>> 
>>> reverse("book_category", args=['action'])
Traceback (most recent call last):
  ...
django.urls.exceptions.NoReverseMatch: Reverse for 'book_category' not found. 'book_category' is not a valid view function or pattern name.
>>> 
>>>

使用包含()函数

回想一下include()函数用于包含来自指定路径下的应用的 URLConf。

创建名称空间的另一种方法是在定义名称空间的同时包含一个 URLConf。打开全网站urls.py文件并修改include()以包含名为namespace的额外关键字参数,如下所示:

djangobin/django _ project/django _ project/URLs . py

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

urlpatterns = [    
    url(r'', include('djangobin.urls', namespace='djangobin')),
    url(r'^admin/', admin.site.urls),
]

在本教程中,我们将使用第二种方法来命名 URL,因此请注释掉我们在 djangobin 的urls.py文件中使用app_name变量定义命名空间的那一行。

这就是创建命名空间网址所需要的。



Django 模板基础

原文:https://overiq.com/django-1-11/basics-of-django-templates/

最后更新于 2020 年 7 月 27 日


什么是 Django 模板?

把 Django 模板想象成创建一个完整的 HTML 页面所需的脚手架。Django 模板只不过是一个包含静态内容的文本文件,以及一个指定一些逻辑、循环和数据显示的特殊动态标记。

Django 模板存放在哪里?

在我们开始构建模板之前,让我们先花一些时间来学习 Django 如何搜索模板。在位于djangobin/django_project/djangobin的 djangobin 应用中创建一个名为templates的新目录。但是为什么要templates目录呢?因为默认情况下,Django 会在每个已安装应用的templates目录中搜索模板。

如果出于某种原因,您想要关闭此行为,请打开settings.py并将'APP_DIRS'值设置为False,如下所示:

djangobin/django _ project/django _ project/settings . py

#...

TEMPLATES = [
    {
    
    
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': False,
        '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',
            ],
        },
    },
]

#...

目前,我们没有任何合理的理由关闭此行为,因此再次将其切换回True

Django 将所有templates目录视为一个目录。这意味着如果你有两个应用,博客和论坛分别有blog/templates/list.htmlforum/templates/list.html模板,那么对于 Django 来说,这两个文件是相同的,它将使用它首先找到的模板(取决于INSTALLED_APPS设置中列出的应用的顺序)。由于这种行为,Django 建议在templates目录中用应用的名称创建一个子目录。在templates目录下新建一个名为djangobin的子目录。我们将在这里存储 djangobin 应用的所有模板。

一个简单的 Django 模板

为了让事情变得清晰,让我们以 Django 模板为例。下面的列表显示了一个简单的博客文章模板:

<!DOCTYPE html>
<html>
    <head>
        <title>Blog Post</title>
    </head>

    <body>
    <h1>{
    
    {
    
     post_title }}</h1>

    <p>Published by <span>Author : {
    
    {
    
     author|title }} </span></p>

    <p>{
    
    {
    
     post_content }}</p>

    <p>Related Posts:</p>
    <ul>
        {
    
    % for item in item_list %}
            <li>{
    
    {
    
     item }}</li>
        {
    
    % endfor %}
    </ul>

    {
    
    % if comments %}
        <p>This post has some comments</p>
    {
    
    % else %}
        <p>This post has no comments</p>
    {
    
    % endif %}

    </body>
</html>

请注意,除了 HTML 代码之外,我们还使用特殊的标记来表示页面的动态部分。

让我们一步一步来看代码。

  1. 任何被双花括号({ { post_title }})包围的文本都是一个变量,例如,第 8 行(<h1>{ { post_title }}</h1>)中的代码意味着输出变量post_titleh1标记中的值。所以如果post_title的值是"Django Templates",那么<h1>Django Templates</h1>就会被打印出来。你可能会说,但是我们如何指定变量值呢?别担心,我们稍后会谈到这一点。

  2. 例如,第 16 行中由单个大括号({ )和百分号(%)包围的文本,即{% for item in item_list %}被称为模板标签。模板标签允许我们做一些非常具体的事情。在这种情况下,{% for %}标签的工作原理非常像 Python 中的 for 循环,通常用于循环遍历列表或字典。

    就像我们可以使用{% for %}标签在模板中模拟一个循环一样。我们可以使用{% if %}标签向我们的模板添加逻辑 if 语句。在第 21 行,我们有一个{% if %}模板标签。

    {
          
          % if comments %}
        <p>This post has {
          
           comment.count } comments</p>
    {
          
          % else %}
        <p>This post has no comments</p>
    {
          
          % endif %}
    
    

    以下是它的工作原理:

    首先,评估变量comments的值。如果是真的,则打印<p>This post has some comments</p>,否则打印<p>This post has no comments</p>

  3. 最后,在第 10 行,我们使用了一个过滤器,即{ { author|title }}。过滤器是格式化变量输出的一种方式。我们使用管道字符(|)后跟变量名后面的过滤器名称来指定过滤器。如果变量author的值是"bob",那么由于title的过滤"Bob"将被打印而不是"bob"title过滤器将字符串中每个单词的第一个字符转换为大写,其余字符转换为小写。

Django 模板可以访问许多其他过滤器;我们将在 Django 中的模板过滤器一课中讨论一些重要的内容。除此之外,您还可以创建自己的过滤器。

使用 Django 模板系统

模板引擎是将模板加载并渲染为标记的程序。每个模板引擎定义一种语言来创建模板。我们在前面的部分已经看到了这种语言的一些部分,例如,{ { post_title }}{% if comments %}等。Django 提供的模板引擎简称为 Django 模板语言DTL 。在 Django 1.8 之前,DTL 是唯一的选择。但是对于 Django 1.8 和更高版本,我们可以使用像 Jinja2 这样的外部模板引擎。然而,如果你没有任何合理的理由使用 Jinja2,只要坚持 DTL,你就会没事。在本教程中,我们将只使用 DTL。

在我们开始在视图中使用模板之前,让我们深入了解一下它们的工作原理。以下是使用 Django 模板所涉及的基本工作流程:

  1. 通过将模板的内容作为原始字符串传递,创建一个新的Template对象。
  2. 用给定的一组变量调用模板对象上的render()方法。render()方法在评估所有变量和模板标签后返回完全渲染的代码。

让我们尝试在 DjangoShell 中创建一些Template对象。

$ ./manage.py shell
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
>>>
>>> from django import template
>>> t = template.Template("We are learning {
    
    { name }}")
>>> c = template.Context({
    
    'name': 'Django'})
>>> print(t.render(c))
We are learning Django
>>>

以下是上述代码的工作原理。

  1. 在第 8 行,我们正在从django包导入template模块。

  2. template模块有Template类。在第 9 行,我们通过将一个原始字符串传递给Template类的构造函数来创建一个新的Template对象。

  3. 我们的模板字符串由一个变量组成。为了将值传递给这个变量,我们使用Context类。通过传递将变量名映射到值的字典来实例化Context类。

  4. 最后,渲染模板调用Context对象作为参数的render()方法。

一旦加载了模板,就可以根据需要多次渲染,而无需重新加载。

>>>
>>> c = template.Context({
    
    'name': 'a new web framework'})
>>> print(t.render(c))
We are learning a new web framework
>>>

但是如果我们不向Context构造函数提供任何值,会发生什么呢?在这种情况下,您将不会收到任何错误消息,而 Django 将不会打印任何内容。这在一开始听起来可能令人沮丧,但它非常有用,因为在现实世界的应用中,网站仅仅因为缺少一个值就开始抛出错误是不可接受的。

>>>
>>> c = template.Context()
>>> print(t.render(c))
We are learning
>>>

Context字典中的键区分大小写,因此如果变量名与模板中预期的变量名略有不同,Django 将不会打印任何内容。

>>>
>>> c = template.Context({
    
    'Name': 'Django framework'})
>>> print(t.render(c))
We are learning
>>>

因此,确保上下文字典中键的名称与模板中变量的名称相同非常重要。

上下文变量查找

到目前为止,我们一直在向模板传递简单的数据,主要是字符串。然而,Django 模板系统可以轻松处理复杂的数据结构,如列表、字典甚至对象。访问模板中这些复杂数据结构的关键是使用点字符(.)。这可以用一些例子来最好地描述。

通过字典

>>>
>>> framework = {
    
    
... 'first': 'Django',
... 'second': 'Laravel',
... 'third': 'Spring',
... 'fourth': 'CodeIgniter'
... }
>>>
>>> t = template.Template("We are learning {
    
    { framework.first }}")
>>> c = template.Context({
    
    'framework': framework})
>>> print(t.render(c))
We are learning Django
>>>

同样,我们可以使用点运算符(.)来访问对象属性。

传递对象

>>>
>>> import datetime
>>>
>>> now = datetime.datetime.now()
>>>
>>> now.day, now.month, now.year
(25, 1, 2017)
>>>
>>>    
>>> t = template.Template("Date is {
    
    { now.day }}-{
    
    { now.month }}-{
    
    { now.year }}")
>>> c = template.Context({
    
    'now':now})
>>> print(t.render(c))
Date is 25-1-2017
>>>

在上面的例子中,我们使用了一个内置的类。同样的行为也适用于我们创建的类。

>>> 
>>> class Player:
...    def __init__(self, name):
...        self.name = name
...    def __str__(self):
...        return self.name
... 
>>> 
>>> p1 = Player('bob')
>>> 
>>> t = template.Template("The player name is {
    
    { player }}")
>>> 
>>> c = template.Context({
    
    'player': p1})
>>> 
>>> t.render(c)
'The player name is bob'
>>> 
>>>

调用方法

使用点运算符(.)您也可以在模板中调用对象的方法,但是请记住,在这样做时,我们不包括括号()

>>>
>>> name = "django"
>>> name.capitalize()
'Django'
>>>
>>> name.upper()
'DJANGO'
>>> name
'django'
>>>
>>> t = template.Template("{
    
    { var.capitalize }} learning {
    
    { var.upper }}")
>>> c = template.Context({
    
    'var': name})
>>> print(t.render(c))
Django learning DJANGO
>>>

需要注意的是,我们只能在模板内部调用不需要任何参数的方法。

传递列表

我们还可以使用点(.)运算符后跟索引位置来访问列表的元素。记住 python 中的列表是0索引的。

>>>
>>> list = ['Ruby on Rails', 'Django', 'Laravel']
>>> t = template.Template("We are learning {
    
    { list.1 }} ")
>>> c = template.Context({
    
    'list': list})
>>> print(t.render(c))
We are learning Django
>>>

虽然,我们可以在 Python 中使用负索引来访问列表的元素,但是这个功能在 Django 模板中是不可用的。

点查找的顺序

点(.)字符在模板内部有特殊含义。变量名后面的点(.)表示查找。当 Django 模板系统在变量名后面遇到一个点(.)时,它会按照这个特定的顺序尝试执行查找。

  1. 字典查找- var.['key']
  2. 属性查找- var.key
  3. 方法调用查找- var.key()
  4. 列表-索引查找- var.1

模板系统使用第一个有效的查找。

我们也可以一个接一个地连锁查找。例如:

{
    
    {
    
     person.first_name.capitalize }}`

这里首先评估person.first_name,然后对结果应用capitalize()方法。



Django 中的模板标签

原文:https://overiq.com/django-1-11/template-tags-in-django/

最后更新于 2020 年 7 月 27 日


在前一章中,我们已经向您介绍了一些基本的模板标签和过滤器。在本课中,我们将详细讨论一些重要的内置模板标签。

if 标签

以下是{% if %}标记的语法:

语法:

{
    
    % if condition %}
    <p>Print this line</p>
{
    
    % endif %}

以下是它的工作原理:

{% if %}标记计算condition的值,如果它为真(Python 中的变量计算为真,如果它包含非空值或非False布尔值),那么模板系统将显示{% if %}{% endif %}之间的所有内容。例如:

{
    
    % if var %}
    <p>Print this para</p>
{
    
    % endif %}

如果var的值为10,则打印<p>Print this para</p>。另一方面,如果p的值是[](空数组)或{}(空字典)或0(数字零)或False(布尔假),则不会打印任何内容。

用相应的{% endif %}关闭每个{% if %}很重要。否则,Django 将抛出TemplateSyntaxError异常。

您也可以在{% if %}标签上添加一个可选的{% else %}标签,如下所示:

{
    
    % if var %}
    <p>Print this para</p>
{
    
    % else %}
    <p>Else print the other para</p>
{
    
    % endif %}

以下是它的工作原理:

首先评估var的值,如果为真,则打印<p>Print this para</p>。否则,<p>Else print the other para</p>将被印刷。

也可以增加一个或多个{% elif %}子句,增加一些条件。例如:

{
    
    % if count < 10 %}
    <p>Print this para</p>
{
    
    % elif count < 20 %}    
    <p>Otherwise print this</p>
{
    
    % elif count < 30 %}
    <p>Let's try this</p>
{
    
    % else %}
    <p>Okay everything failed print this now</p>
{
    
    % endif %}

以下是它的工作原理:

每个条件被依次评估,一旦一个条件被发现为真,相应块中的代码就被执行,所有其他条件的评估被跳过。

评论

Django 模板使用以下语法编写注释。

{
    
    # This is a comment #}

使用此语法编写的注释不会在 HTML 源代码中渲染。此外,您不能将此注释扩展到多行。

{
    
    # This is
not a comment #}

如果要多行写注释,使用{% comment %}标记:

{
    
    % comment %}
This is a comment expanding to
multiple lines.
{
    
    % endcomment %}

使用逻辑运算符

也可以使用逻辑andornot运算符测试多个条件。例如:

and运算符

{
    
    % if palindrome and even %}
    <p>Number is palindrome and even</p>
{
    
    % endif %}

如果两个变量评估为真,则打印<p>Number is palindrome and even</p>。否则,将不会打印任何内容。

not运算符

{
    
    % if not post_list %}
    <p>There are no blog post</p>
{
    
    % endif %}

not运算符否定该条件。因此,只有当post_listFalse(或为空)时,上述代码才会打印<p>There are no blog posts</p>。换句话说,如果没有博文打印<p>There are no blog posts</p>

or运算符

{
    
    % if post_list or page_list %}
    <p>The site has some blog post or pages</p>
{
    
    % endif %}

如果两个变量中的任何一个为真,那么将打印<p>The site has some blog post or pages</p>。否则,将不会打印任何内容。

下面是更多的例子:

{
    
    % if not post_list or author_list %}
    <p>There are no posts or there are some authors</p>
{
    
    % endif %}

这里not运算符只求post_list变量的反。所以字符串<p>There are no posts or there are some authors</p>只有在没有帖子(即post_list为空)或者有一些作者(author_list不为空)时才会被打印出来。

也可以在同一个标签内使用andor运算符。需要记住的重要一点是and的优先级高于or操作员。例如:

{
    
    % if post_list and page_list or author_list %}
    <p>The site has either both posts and pages or only author</p>
{
    
    % endif %}

该代码将被解释为:

{
    
    % if (post_list and page_list) or author_list %}
    <p>The site has either both posts and pages or only author</p>
{
    
    % endif %}

不过,我想澄清的是,不要动心用括号来分组条件。这是无效语法,会引发TemplateSyntaxError异常。

您也可以将一个{% if %}标签嵌套在另一个{% if %}标签中。例如:

{
    
    % if num < 10 %}
    {
    
    % if num > 5 %}
        <p>The num is greater than 5 but less than 10</p>
    {
    
    % endif %}    
{
    
    % endif %}

使用关系运算符

您也可以在模板标签中使用关系运算符><>=<=!===

>运算符

{
    
    % if num > 10 %}
    <p>The num is greater than 10</p>
{
    
    % endif %}

如果num大于10,则打印<p>The num is greater than 10</p>

<运算符

{
    
    % if num < 10 %}
    <p>The num is lesser than 10</p>
{
    
    % endif %}

如果num小于10,则打印<p>The num is lesser than 10</p>

>=运算符

{
    
    % if num >= 10 %}
    <p>The num is greater than or equal to 10</p>
{
    
    % endif %}

如果num大于或等于10,则打印<p>The num is greater than or equal to 10</p>

<=运算符

{
    
    % if num <= 10 %}
    <p>The num is lesser than or equal to 10</p>
{
    
    % endif %}

如果num小于或等于10,则打印<p>The num is lesser than or equal to 10</p>

==运算符

{
    
    % if num == 10 %}
    <p>The num is equal to 10</p>
{
    
    % endif %}

如果num等于10,则打印<p>The num is equal to 10</p>

!=运算符

{
    
    % if num != 10 %}
    <p>The num is not equal to 10</p>
{
    
    % endif %}

如果num不等于10,则打印<p>The num is not equal to 10</p>

innot inis运算符

in运算符

{
    
    % if 10 in num_list %}
    <p>Yes, the number 10 is in the num_list</p>
{
    
    % endif %}

如果num_list中有数字 10,则打印<p>Yes, the number 10 is in the num_list</p>

not in运算符

{
    
    % if 10 not in list %}
    <p>Yes, the number 10 is not in the list</p>
{
    
    % endif %}

如果num_list中不存在数字 10,则打印<p>Yes, the number 10 is in the num_list</p>

is运算符

就像在 Python 代码中一样,模板中的is运算符用于比较两个对象。如果两个物体相同,则is操作员返回True。否则False

{
    
    % if obj is user %}
    <p>Yes obj is same as user</p>
{
    
    % endif %}

这里,如果变量obj指向的对象与变量user相同,则打印文本<p>Yes obj is same as user</p>

用于标记

一个{% for %}标签允许我们用来循环一个序列(或集合)。我们可以使用{% for %}标签对列表、元组、字典等内容进行迭代。以下是{% for %}标签的语法:

{
    
    % for i in my_list %}
    <p>The value of i is {
    
    {
    
     i }}</p>
{
    
    % endfor %}

以下是它的工作原理:

当循环开始时,列表中的第一个值my_list被分配给变量i。然后模板引擎将渲染{% for %}{% endfor %}之间的所有内容。这个过程一直重复,直到列表中没有更多元素需要迭代。

要以相反的顺序打印列表,请在列表后添加reversed关键字,如下所示。

{
    
    % for i in my_list reversed %}
    <p>The value of i is {
    
    {
    
     i }}</p>
{
    
    % endfor %}

有时候在你的 Django 之旅中,你不得不处理列表中的列表。要访问列表,请将子列表的列表元素解包为单个变量。例如,假设我们的上下文中有以下列表。

list = [ ["uno", "one"], ["dos", "two"], ["tres", "three"], ["cuatro", "four"] ]

要在模板内循环列表,请执行以下操作:

{
    
    % for x, y in list %}
    <p>{
    
    {
    
     x }} : {
    
    {
    
     y }}</p>
{
    
    % endfor %}

输出将是:

uno : one
dos : two
tres : three
cuatro : four

同样,我们可以访问字典的元素。假设我们的上下文变量包含一个名为dict的字典。

dict = {
    
     'uno': 'one', 'dos': 'two', 'tres': 'three', 'cuatro': 'four' }

要在模板中访问该字典,请使用以下代码:

{
    
    % for k, v in dict.items %}
    <p>{
    
    {
    
     k }} : {
    
    {
    
     v }}</p>
{
    
    % endfor %}

输出如下所示:

cuatro : four
uno : one
tres : three
dos : two

请注意,字典中的元素没有特定的存储顺序。所以上面的输出可能会有所不同。

对于空标签

假设我们有一个名为post_list的变量,它包含一个帖子对象列表。我们的工作是打印所有博客文章的列表。我们可以这样做,使用如下的for标签:

{
    
    % for post in post_list %}
    <h2>{
    
    {
    
     post.title }}</h2>
{
    
    % endfor %}

但是有一个问题。我们还没有检查是否有任何博客文章存在。以下是更新后的代码:

{
    
    % if post_list %}
    {
    
    % for post in post_list %}
        <h2>{
    
    {
    
     post.title }}</h2>
    {
    
    % endfor %}
{
    
    % else %}
    No post published yet.
{
    
    % endif %}

这种模式非常普遍,Django 为此提供了一个很好的捷径。for标签可以附加一个{% empty %}标签。这个标签让你定义在列表为空的情况下输出什么。例如:

{
    
    % for post in post_list %}
    <h2>{
    
    {
    
     post.title }}</h2>
{
    
    % empty %}
    No post published yet.        
{
    
    % endfor %}

就像嵌套的if标签一样,我们可以有嵌套的for标签。

{
    
    % for post in post_list %}
    <p>{
    
    {
    
     post.content }}</p>
    <p>
        <ul>
            {
    
    % for tag in post.tags %}
            <li>{
    
    {
    
     tag }}</li>
            {
    
    % endfor %}            
        </ul>
    </p>
{
    
    % endfor %}

for标签提供了一个名为forloop的特殊变量。forloop有几个属性可以用来跟踪循环的进度。

forloop.counter -返回一个数字,表示循环的当前迭代。从1开始。例如,假设我们的上下文包含一个名为list的列表,定义如下:

list = [11,12,13]

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.counter }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

那么上面的 for 循环将打印以下输出:

1 Iteration - 11
2 Iteration - 12
3 Iteration - 13

forloop.counter0 -工作方式与forloop.counter相同,但以0而非1开头。

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.counter0 }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

输出:

0 Iteration - 11
1 Iteration - 12
2 Iteration - 13

forloop.revcounter -返回从循环结束开始的迭代次数。

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.revcounter }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

输出:

3 Iteration - 11
2 Iteration - 12
1 Iteration - 13

forloop.revcounter0 -与forloop.revcounter相同,但它是0索引的。

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.revcounter0 }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

输出:

2 Iteration - 11
1 Iteration - 12
0 Iteration - 13

forloop.first -如果当前迭代是第一次迭代,则返回布尔值True。否则False

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.first }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

输出:

True Iteration - 11
False Iteration - 12
False Iteration - 13

forloop.last -如果当前迭代是最后一次迭代,则返回布尔值True。否则False

{
    
    % for i in list %}
    <p>{
    
    {
    
     forloop.last }} Iteration - {
    
    {
    
     i }}</p>
{
    
    % endfor %}

输出:

False Iteration - 11
False Iteration - 12
True Iteration - 13

forloop.parentloop -它在嵌套的for循环中用于引用父for循环中的forloop变量。例如:

{
    
    % for i in list %}
    <table>
    {
    
    % for j in i %}
        <tr>
            <td>{
    
    {
    
     forloop.parentloop.counter }} - {
    
    {
    
     forloop.counter }} - {
    
    {
    
     i }}</td>
        </tr>
    {
    
    % endfor %}
    </table>
{
    
    % endfor %}

标签 url

url标签用于生成模板内部的网址。它具有以下语法:

{
    
    % url 'url_name' arg1 arg2 %}

其中url_name是 URL 模式的名称。arg1arg2指的是网址要求的参数。如果网址不接受任何参数,只需传递网址模式的名称。成功后,它将返回网址中没有主机部分的部分。如果不能创建网址NoReverseMatch则抛出异常。

如果你有命名空间的网址,就像我们在第章【创建网址和自定义响应】中调用reverse()函数时所做的那样,指定完全限定的名称:

{
    
    % url 'my_app:url_name' arg1 arg2 %}

以下是一些例子:

>>> 
>>> from django import template
>>>
>>> t = template.Template("{% url 'djangobin:time' %}") 
>>> t.render(template.Context())
'/time/'
>>> 
>>> 
>>> t = template.Template("{% url 'djangobin:index' %}")
>>> t.render(template.Context())
'/'
>>> 
>>> 
>>> t = template.Template("{% url 'djangobin:profile' 'foobar' %}")
>>> t.render(template.Context())
'/user/foobar/'
>>> 
>>> 
>>> t = template.Template("{% url 'djangobin:book_category' 'crime' %}")
>>> t.render(template.Context())
'/book/crime/'
>>> 
>>>
>>> t = template.Template("{% url 'djangobin:contact' %}")
>>> t.render(template.Context())
Traceback (most recent call last):
    ...
django.urls.exceptions.NoReverseMatch: Reverse for 'contact' not found. 'contact' is not a valid view function or pattern name. 
>>>



Django 的模板过滤器

原文:https://overiq.com/django-1-11/template-filters-in-django/

最后更新于 2020 年 7 月 27 日


Django 过滤器用于在变量渲染为 HTML 代码之前修改变量的值。要使用过滤器,请在变量名后键入管道字符(|)后跟过滤器名称。

{
    
    {
    
     variable|filter_name }}

下过滤器

当应用于变量时,lower过滤器会将变量中的所有大写字符转换为等效的小写字符。例如:

<p>{
    
    {
    
     name|lower }}</p>

如果name变量的值是"Tom Sawyer",那么上面的代码将产生下面的 HTML。

<p>tom sawyer</p>

上部过滤器

upper滤镜与lower滤镜完全相反。它会将变量中的所有字符转换为大写等效字符。例如:

<p>{
    
    {
    
     name|upper }}</p>

如果name变量的值是"tom sawyer",那么上面的代码将产生下面的 HTML。

<p>TOM SAWYER</p>

capfirst 滤波器

capfirst过滤器仅将变量中的第一个字符转换为其大写等效字符。例如:

<p>{
    
    {
    
     name|capfirst }}</p>

如果变量name包含"tom sawyer",那么上面的代码将产生下面的 HTML。

<p>Tom sawyer</p>

您也可以链接过滤器。例如:

<p>{
    
    {
    
     name|lower|capfirst }}</p>

这里name变量首先被转换成小写字符,然后对结果应用capfirst过滤器。

标题过滤器

title过滤器将每个单词的第一个字母大写。例如:

<p>{
    
    {
    
     name|title }}</p>

如果变量name包含"tom sawyer",那么上面的代码将产生下面的 HTML。

<p>Tom Sawyer</p>

长度过滤器

length过滤器决定数值的长度。它可以处理字符串、列表、字典和元组。例如:

<p>The length variable name is {
    
    {
    
     name|length }}</p>

如果变量name包含"tom sawyer",那么上面的代码将产生下面的 HTML。

<p>The length variable name is 10</p>

截断词过滤器

truncatewords过滤器在一定数量的单词后截断(缩短)字符串。截断字符串后,它将...(称为省略号)附加到截断字符串的末尾。truncatewords是你可以传递论点的过滤器之一。将参数传递给筛选器类型冒号(:),后跟双引号内的参数("")。要将多个参数传递给过滤器,请使用逗号(,)分隔它们。truncatewords接受单个参数,表示后面要截断的字数。例如,要只输出博文的第一个10字,请执行以下操作:

<p>{
    
    {
    
     post|truncatewords:"10" }}</p>

如果post变量定义为:

post = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu lectus ut lacus posuere fringilla id eu turpis."

上面的代码将产生下面的 HTML。

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu ...</p>

截断滤波器

truncatechars过滤器类似于truncatewords,但它不是按单词截断,而是按字符截断。如果字符串大于指定的字符数,它将截断字符串。就像truncatewords过滤器一样,截断字符串后truncatechars过滤器会将...(省略号)追加到截断的字符串中。例如:

{
    
    {
    
     long_text|truncatechars:"10" }}

如果字符串大于10个字符,这将截断字符串并将...追加到末尾。

如果long_text包含"Lorem ipsum dolor sit amet, consectetur adipiscing elit",那么上面的代码会产生下面的 HTML。

<p>Lorem i...</p>

复数滤波器

pluralize过滤器用于处理后缀。让我们举个例子:

<p>You have {
    
    {
    
     num }} products in your cart</p>

如果n1,那么我们要显示。

<p>You have 1 product in your cart</p>

另一方面,如果n10,那么我们想要显示。

<p>You have 10 products in your cart</p>

注意字符串products中的s

我们可以使用pluralize过滤器轻松处理这些情况。

<p>You have {
    
    {
    
     num }} product{
    
    {
    
     num|pluralize }} in your cart</p>

现在,如果num1,输出将是:

<p>You have 1 product in your cart</p>

如果num的值为100,则输出为:

<p>You have 100 products in your cart</p>

默认情况下pluralize过滤器将"s"追加到字符串中。然而,并不是所有的复数单词都以"s"结尾,有些也像tomatoes一样以"es"结尾。例如:

<p>I have {
    
    {
    
     num }} tomato{
    
    {
    
     num|pluralize }}</p>

如果num等于5,则输出上述代码:

<p>I have 5 tomatos</p>

当然,这是错误的。要提供替代后缀,您可以将参数传递给pluralize过滤器。例如:

<p>I have {
    
    {
    
     num }} tomato{
    
    {
    
     num|pluralize:"es" }}</p>

如果num等于5,输出将是:

<p>I have 5 tomatoes</p>

还有一些单词不是用简单的后缀来复数的,比如日记和日记,樱桃和樱桃等等。为了处理这种特殊情况,你必须提供单数和复数后缀作为pluralize过滤器的参数。例如:

<p>I have {
    
    {
    
     num }} diar{
    
    {
    
     num|pluralize:"y,ies" }}</p>

如果num1,输出将为:

<p>I have 1 diary</p>

如果num5,输出将为:

<p>I have 5 diaries</p>

日期过滤器

我们使用date过滤器来格式化datetime.datedatetime.datetime对象。date过滤器使用一些特殊的格式字符来格式化datetime.datedatetime.datetime对象。要格式化日期,将一串格式字符作为参数传递给date过滤器。例如,假设我们的上下文有一个名为nowdatetime.datetime对象,其定义如下:

now = datetime.datetime.now()

我们的模板包含以下代码:

<p>Today is {
    
    {
    
     now }}</p>

如果我们不使用date过滤器,那么上面的代码会产生下面的 HTML。

<p>Today is Jan. 27, 2017, 4:28 p.m.</p>

以下是一些常用的格式字符串,可用于date过滤器:

性格;角色;字母 它有什么作用? 例子
d 使用两位数字打印一个月中的某一天 0131
D 使用三个字母打印星期几 Mon为周一,Tue为周二,以此类推
m 使用两位数字打印月份 01为 1 月,02为 2 月,依此类推
M 使用三个字母数字打印月份 Jan为 1 月,Feb为 2 月,依此类推
i 打印分钟 0059
h 以 12 小时格式打印小时数 0112
H 以 24 小时格式打印小时数 0023
s 打印秒数 0059
a 打印“上午”或“下午” a.m.p.m.
Y 使用完整的 4 位数字打印年份 20012014等等

让我们向日期过滤器添加一些格式字符,如下所示:

<p>Today is {
    
    {
    
     now|date:"D d M Y" }}</p>

这将输出如下内容:

<p>Today is Fri 27 Jan 2017</p>

换行过滤器

linebreaks过滤器将字符串中的换行符转换为适当的 HTML。如果一个字符串有换行符,它将被转换为<br/>,后面跟一个空行的换行符将被转换为</p>

{
    
    {
    
     text|linebreak }}

请考虑以下示例:

例 1:

content = '''\
this is
a content
'''

{
    
    {
    
     content|linebreaks }}

那么输出将是:

<p>this is<br />a test<br /></p>

在变量content中有两个换行符,第一个出现在单词is之后(第 2 行),第二个出现在单词content之后(第 3 行)。
linebreaks过滤器用<br />标签替换这些换行符,并将整个字符串包装在<p>标签中。

例 2:

content = '''\
this is

a test
'''

{
    
    {
    
     content|linebreaks }}

那么输出将是:

<p>this is</p>

<p>a test<br /></p>

linebreaksbr 过滤器

linebreaksbr过滤器仅将字符串中的换行符转换为<br>标记。例如:

{
    
    {
    
     text|linebreaksbr}}

现在如果text变量的值是"Filter string\n using linebreaksbr",输出将是:

Filter string<br /> using linebreaksbr

标签自动抓取

出于安全考虑,Django 模板系统会自动为您进行转义。考虑以下示例:

假设变量my_code包含"<p>this is short para </p>",模板中的代码为:

{
    
    {
    
     my_code }}

上面的代码将渲染为以下 HTML:

&lt;p&gt;THIS IS TEST&lt;/p&gt;

有两种方法可以关闭转义:

  1. safe过滤。
  2. autoescape过滤。

安全过滤器

{
    
    {
    
     my_code|safe }}

这段代码将产生以下 HTML 输出:

<p>this is short para </p>

safe过滤器告诉 Django Template 系统my_code变量是安全的,不需要转义。

关闭转义的另一种方法是使用autoescape标记

标签自动抓取

autoescape标签允许您在模板中转义/隐藏大块内容。它接受 on 或 off 作为参数,指示自动转义在块中是否有效。

例如:

{
    
    % autoescape on %}
    {
    
    {
    
     code_snippet }}
{
    
    % endautoescape %}

由于默认情况下 Django 模板系统会自动转义所有内容,因此您可能永远不需要使用上面的代码。相反,我们通常使用autoescape标签来关闭大部分内容的转义。例如:

{
    
    % autoescape off %}
    {
    
    {
    
     heading }}
    {
    
    {
    
     main_content }}
    {
    
    {
    
     footer }}
{
    
    % endautoescape %}

逸出过滤器

escape过滤器将以下字符转换为其 HTML 实体。

  • "替换为&quot
  • '替换为&#39
  • &替换为&amp
  • <替换为&lt
  • >替换为&gt

由于 Django 模板系统会自动转义所有内容,您可能永远不会使用escape过滤器:例如:

my_code = "<p>this is short para</p>"

{
    
    {
    
     my_code }}

与…相同

{
    
    {
    
     my_code|escape }}

autoescape关闭,我们想要逃离内容时,这个滤镜的效用就发挥出来了。例如:

{
    
    % autoescape off %}
    {
    
    {
    
     heading }}
    {
    
    {
    
     main_content }}
    {
    
    {
    
     footer }}
    {
    
    {
    
     comments }}
{
    
    % endautoescape %}

这里可以假设变量headingmain_contentfooter的内容是安全的。但是comments变量不是这样。这就是为什么关闭comments变量的转义不是一个好主意。要在模板中转义comments变量,您可以执行以下操作:

{
    
    % autoescape off %}
    {
    
    {
    
     heading }}
    {
    
    {
    
     main_content }}
    {
    
    {
    
     footer }}
    {
    
    {
    
     comments|escape }}
{
    
    % endautoescape %}

首先,这是足够的过滤器。要查看过滤器的完整列表,请查看 Django 文档中的页面。