Django是一个开放源代码的Web应用框架,由Python写成。采用了MVT的框架模式,即模型M,视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。
框架介绍
Django 项目是一个Python定制框架,它源自一个在线新闻 Web 站点,于 2005 年以开源的形式被释放出来。Django 框架的核心组件有:
- 用于创建模型的对象关系映射
- 为最终用户设计的完美管理界面
- 一流的 URL 设计
- 设计者友好的模板语言
- 缓存系统。
架构设计
Django是一个基于MVC构造的框架。但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),称为 MTV模式。它们各自的职责如下:
从以上表述可以看出Django 视图不处理用户输入,而仅仅决定要展现哪些数据给用户,而Django 模板 仅仅决定如何展现Django视图指定的数据。或者说, Django将MVC中的视图进一步分解为 Django视图 和 Django模板两个部分,分别决定 “展现哪些数据” 和 “如何展现”,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板。
至于MVC控制器部分,由Django框架的URLconf来实现。URLconf机制是使用正则表达式匹配URL,然后调用合适的Python函数。URLconf对于URL的规则没有任何限制,你完全可以设计成任意的URL风格,不管是传统的,RESTful的,或者是另类的。框架把控制层给封装了,无非与数据交互这层都是数据库表的读,写,删除,更新的操作。在写程序的时候,只要调用相应的方法就行了,感觉很方便。程序员把控制层东西交给Django自动完成了。 只需要编写非常少的代码完成很多的事情。所以,它比MVC框架考虑的问题要深一步,因为我们程序员大都在写控制层的程序。现在这个工作交给了框架,仅需写很少的调用代码,大大提高了工作效率
快速安装:
-
安装Python
作为一个 Python Web 框架,Django 需要 Python, Python 包含了一个名为 SQLite 的轻量级数据库,所以你暂时不必自行设置一个数据库。
最新版本的 Python 可以通过访问 https://www.python.org/downloads/ 或者操作系统的包管理工具获取
2. 设置数据库
此步骤仅在你打算使用诸如 PostgreSQL, MySQL, 或者 Oracle 这些大型数据库引擎时需要。要安装这种数据库, 请参考 database installation information。
3. 安装 Django
pip install Django
或者
git clone https://github.com/django/django.git
- 验证
>>> import django
>>> print(django.get_version())
2.2
编写你的第一个 Django 应用
需求:
它将由两部分组成:
- 一个让人们查看和投票的公共站点。
- 一个让你能添加、修改和删除投票的管理站点。
创建项目
打开命令行,cd 到一个你想放置你代码的目录,然后运行以下命令:
django-admin startproject mysite
让我们看看 startproject
创建了些什么:
这些目录和文件的用处是:
- 最外层的:file: mysite/ 根目录只是你项目的容器,** Django 不关心它的名字**,你可以将它重命名为任何你喜欢的名字。
-
manage.py
: 一个让你用各种方式管理 Django 项目的命令行工具。你可以阅读 django-admin and manage.py 获取所有manage.py
的细节。 - 里面一层的
mysite/
目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如mysite.urls
). -
mysite/__init__.py
:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。如果你是 Python 初学者,阅读官方文档中的 更多关于包的知识。 -
mysite/settings.py
:Django 项目的配置文件。如果你想知道这个文件是如何工作的,请查看 Django settings 了解细节。 -
mysite/urls.py
:Django 项目的** URL 声明,就像你网站的“目录”**。阅读 URL调度器 文档来获取更多关于 URL 的内容。 -
mysite/wsgi.py
:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。阅读 如何使用 WSGI 进行部署 了解更多细节。
用于开发的简易服务器
python manage.py runserver
[图片上传中...(image.png-3ac1a5-1559911924187-0)]
更换端口:
python manage.py runserver 8080
创建投票应用
python manage.py startapp polls
这将会创建一个 polls 目录,它的目录结构大致如下:
编写第一个视图
让我们开始编写第一个视图吧。打开 polls/views.py,把下面这些 Python 代码输入进去:
polls/views.py
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
这是 Django 中最简单的视图。如果想看见效果,我们需要将一个 URL 映射到它——这就是我们需要 URLconf 的原因了。
为了创建 URLconf,请在 polls 目录里新建一个 urls.py 文件。你的应用目录现在看起来应该是这样
在 polls/urls.py 中,输入如下代码:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
下一步是要在根 URLconf 文件中指定我们创建的 polls.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:
mysite/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
启动服务
python manage.py runserver 8012
编写你的第一个 Django 应用,第 2 部分
数据库配置
默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。请执行以下命令:
python manage.py migrate
这个migrate
命令检查 INSTALLED_APPS
****设置,为其中的每个应用创建需要的数据表,至于具体会创建什么,这取决于你的 mysite/settings.py
设置文件和每个应用的数据库迁移文件。这个命令所执行的每个迁移操作都会在终端中显示出来
E:\codeDev\djangoDemo\mysite>python 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 admin.0003_logentry_add_action_flag_choices... 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 auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying sessions.0001_initial... OK
创建模型
在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据
在这个简单的投票应用中,需要创建两个模型:
问题 Question 和选项 Choice。
Question 模型包括问题描述和发布时间。
Choice 模型有两个字段,选项描述和当前得票数。每个选项属于一个问题。
这些概念可以通过一个简单的 Python 类来描述。按照下面的例子来编辑 polls/models.py 文件:
polls/models.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
激活模型
上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:
- 为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
- 创建可以与 Question 和 Choice 对象进行交互的 Python 数据库 API。
但是首先得把 polls 应用安装到我们的项目里
mysite/settings.py
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
- 现在你的 Django 项目会包含 polls 应用。接着运行下面的命令:
E:\codeDev\djangoDemo\mysite>python manage.py makemigrations polls
Migrations for 'polls':
polls\migrations\0001_initial.py
- Create model Question
- Create model Choice
通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移。
迁移是 Django 对于模型定义(也就是你的数据库结构)的变化的储存形式 - 没那么玄乎,它们其实也只是一些你磁盘上的文件。如果你想的话,你可以阅读一下你模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。别担心,你不需要每次都阅读迁移文件,但是它们被设计成人类可读的形式,这是为了便于你手动修改它们。
Django 有一个自动执行数据库迁移并同步管理你的数据库结构的命令 - 这个命令是 migrate
,我们马上就会接触它 - 但是首先,让我们看看迁移命令会执行哪些 SQL 语句
-
sqlmigrate
命令接收一个迁移的名称,然后返回对应的 SQL:(非必须)
python manage.py sqlmigrate polls 0001
你将会看到类似下面这样的输出(我把输出重组成了人类可读的格式):
E:\codeDev\djangoDemo\mysite>python manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIAL
LY DEFERRED);
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
- 现在,再次运行
migrate
命令,在数据库里创建新定义的模型的数据表:
python manage.py migrate
结果如下:
E:\codeDev\djangoDemo\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。我们会在后面的教程中更加深入的学习这部分内容,现在,你只需要记住,改变模型需要这三步:
- 编辑
models.py
文件,改变模型。 - 运行
python manage.py makemigrations
为模型的改变生成迁移文件。 - 运行
python manage.py migrate
来应用数据库迁移。
数据库迁移被分解成生成和应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。
初试 API
现在让我们进入交互式 Python 命令行,尝试一下 Django 为你创建的各种 API。通过以下命令打开 Python 命令行:
python manage.py shell
E:\codeDev\djangoDemo\mysite>python manage.py shell
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from polls.models import Choice, Question
In [2]: Question.objects.all()
Out[2]:
In [3]: from django.utils import timezone
In [4]: q = Question(question_text="What's new?", pub_date=timezone.now())
In [5]: q.save()
In [6]: q.id
Out[6]: 1
In [7]: q.question_text
Out[7]: "What's new?"
In [8]: q.pub_date
Out[8]: datetime.datetime(2019, 5, 30, 4, 11, 17, 277390, tzinfo=)
In [9]: q.question_text = "What's up?"
In [10]:
In [10]: q.save()
In [11]: Question.objects.all()
Out[11]: ]>
In [12]:
等等。
对于我们了解这个对象的细节没什么帮助。让我们通过编辑 Question
模型的代码(位于 polls/models.py
中)来修复这个问题。给 Question
和 Choice
增加**__str__()**
方法。
polls/models.py
给模型增加 __str__()
方法是很重要的,这不仅仅能给你在命令行里使用带来方便,Django 自动生成的 admin 里也使用这个方法来表示对象。
注意:这些都是常规的 Python方法。让我们添加一个自定义的方法,这只是为了演示
polls/models.py
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
E:\codeDev\djangoDemo\mysite>python manage.py shell
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from polls.models import Choice, Question
In [2]: Question.objects.all()
Out[2]: ]>
In [3]: Question.objects.filter(id=1)
Out[3]: ]>
In [4]: Question.objects.filter(question_text__startswith='What')
Out[4]: ]>
In [5]: from django.utils import timezone
In [6]: current_year = timezone.now().year
In [7]: Question.objects.get(pub_date__year=current_year)
Out[7]:
In [8]:
In [8]: Question.objects.get(id=2)
---------------------------------------------------------------------------
DoesNotExist Traceback (most recent call last)
in
----> 1 Question.objects.get(id=2)
D:\Programs\Python\Python37\lib\site-packages\django\db\models\manager.py in manager_method(self, *args, **kwargs)
80 def create_method(name, method):
81 def manager_method(self, *args, **kwargs):
---> 82 return getattr(self.get_queryset(), name)(*args, **kwargs)
83 manager_method.__name__ = method.__name__
84 manager_method.__doc__ = method.__doc__
D:\Programs\Python\Python37\lib\site-packages\django\db\models\query.py in get(self, *args, **kwargs)
406 raise self.model.DoesNotExist(
407 "%s matching query does not exist." %
--> 408 self.model._meta.object_name
409 )
410 raise self.model.MultipleObjectsReturned(
DoesNotExist: Question matching query does not exist.
In [9]: Question.objects.get(pk=1)
Out[9]:
In [10]: q = Question.objects.get(pk=1)
In [11]: q.was_published_recently()
Out[11]: True
In [12]:
In [12]: q = Question.objects.get(pk=1)
In [13]: q.choice_set.all()
Out[13]:
In [14]: q.choice_set.create(choice_text='Not much', votes=0)
Out[14]:
In [15]: q.choice_set.create(choice_text='The sky', votes=0)
Out[15]:
In [16]: c = q.choice_set.create(choice_text='Just hacking again', votes=0)
In [17]: c.question
Out[17]:
In [18]: q.choice_set.all()
Out[18]: , , ]>
In [19]: q.choice_set.count()
Out[19]: 3
In [20]: Choice.objects.filter(question__pub_date__year=current_year)
Out[20]: , , ]>
In [21]: c = q.choice_set.filter(choice_text__startswith='Just hacking')
In [22]:
In [22]: c.delete()
Out[22]: (1, {'polls.Choice': 1})
In [23]:
介绍 Django 管理页面
创建一个管理员账号
首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:
python manage.py createsuperuser
键入你想要使用的用户名,然后按下回车键:
Username: admin
然后提示你输入想要使用的邮件地址:
Email address: [email protected]
最后一步是输入密码。你会被要求输入两次密码,第二次的目的是为了确认第一次输入的确实是你想要的密码。
Password: **********
Password (again): *********
Superuser created successfully.
E:\codeDev\djangoDemo\mysite>python manage.py createsuperuser
Username (leave blank to use 'm709767v'): admin
Email address: [email protected]
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
启动开发服务器
Django 的管理界面默认就是启用的。让我们启动开发服务器,看看它到底是什么样的。
如果开发服务器未启动,用以下命令启动它:
python manage.py runserver
现在,打开浏览器,转到你本地域名的 "/admin/" 目录, -- 比如 "http://127.0.0.1:8000/admin/" 。你应该会看见管理员登录界面:
进入管理站点页面
现在,试着使用你在上一步中创建的超级用户来登录。然后你将会看到 Django 管理页面的索引页
你将会看到几种可编辑的内容:组和用户。它们是由 django.contrib.auth
提供的,这是 Django 开发的认证框架。
向管理页面中加入投票应用
但是我们的投票应用在哪呢?它没在索引页面里显示。
只需要做一件事:我们得告诉管理页面,问题 Question 对象需要被管理。打开 polls/admin.py 文件,把它编辑成下面这样:
polls/admin.py
from django.contrib import admin
from .models import Question
admin.site.register(Question)
体验便捷的管理功能
现在我们向管理页面注册了问题 Question 类。Django 知道它应该被显示在索引页里:
点击 "Questions" 。现在看到是问题 "Questions" 对象的列表 "change list" 。这个界面会显示所有数据库里的问题 Question 对象,你可以选择一个来修改。这里现在有我们在上一部分中创建的 “What's up?” 问题。
点击 “What's up?” 来编辑这个问题(Question)对象:
注意事项:
- 这个表单是从问题
Question
模型中自动生成的 - 不同的字段类型(日期时间字段
DateTimeField
、字符字段CharField
)会生成对应的 HTML 输入控件。每个类型的字段都知道它们该如何在管理页面里显示自己。 - 每个日期时间字段
DateTimeField
都有 JavaScript 写的快捷按钮。日期有转到今天(Today)的快捷按钮和一个弹出式日历界面。时间有设为现在(Now)的快捷按钮和一个列出常用时间的方便的弹出式列表。
页面的底部提供了几个选项:
- 保存(Save) - 保存改变,然后返回对象列表。
- 保存并继续编辑(Save and continue editing) - 保存改变,然后重新载入当前对象的修改界面。
- 保存并新增(Save and add another) - 保存改变,然后添加一个新的空对象并载入修改界面。
- 删除(Delete) - 显示一个确认删除页面。
编写你的第一个 Django 应用,第 3 部分
如何创建公用界面——也被称为“视图”
概况
Django 中的视图的概念是「一类具有相同功能和模板的网页的集合」。比如,在一个博客应用中,你可能会创建如下几个视图:
- 博客首页——展示最近的几项内容。
- 内容“详情”页——详细展示某项内容。
- 以年为单位的归档页——展示选中的年份里各个月份创建的内容。
- 以月为单位的归档页——展示选中的月份里各天创建的内容。
- 以天为单位的归档页——展示选中天里创建的所有内容。
- 评论处理器——用于响应为一项内容添加评论的操作。
而在我们的投票应用中,我们需要下列几个视图:
- 问题索引页——展示最近的几个投票问题。
- 问题详情页——展示某个投票的问题和不带结果的选项列表。
- 问题结果页——展示某个投票的结果。
- 投票处理器——用于响应用户为某个问题的特定选项投票的操作。
在 Django 中,网页和其他内容都是从视图派生而来。每一个视图表现为一个简单的 Python 函数(或者说方法,如果是在基于类的视图里的话)。Django 将会根据用户请求的 URL 来选择使用哪个视图(更准确的说,是根据 URL 中域名之后的部分)。
在你上网的过程中,很可能看见过像这样美丽的 URL: "ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B" 。别担心,Django 里的 URL 规则 要比这优雅的多!
一个 URL 模式定义了某种 URL 的基本格式——举个例子:/newsarchive/
。
为了将 URL 和视图关联起来,Django 使用了 'URLconfs' 来配置。URLconf 将 URL 模式映射到视图。
本教程只会介绍 URLconf 的基础内容,你可以看看 URL调度器 以获取更多内容。
编写更多视图
现在让我们向 polls/views.py 里添加更多视图。这些视图有一些不同,因为他们接收参数:
polls/views.py
from django.http import HttpResponse
# 问题索引页——展示最近的几个投票问题
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
# 问题详情页——展示某个投票的问题和不带结果的选项列表
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
# 问题结果页——展示某个投票的结果
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
# 投票处理器——用于响应用户为某个问题的特定选项投票的操作。
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
把这些新视图添加进 polls.urls
模块里,只要添加几个 url()
函数调用就行:
polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('/', views.detail, name='detail'),
# ex: /polls/5/results/
path('/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('/vote/', views.vote, name='vote'),
]
当某人请求你网站的某一页面时——比如说, "/polls/34/" ,Django 将会载入 mysite.urls
模块,因为这在配置项 **ROOT_URLCONF**
中设置了。然后 Django 寻找名为 urlpatterns
变量并且按序匹配正则表达式。在找到匹配项 'polls/'
,它切掉了匹配的文本("polls/"
),将剩余文本——"34/"
,发送至 'polls.urls' URLconf 做进一步处理。在这里剩余文本匹配了 '
,使得我们 Django 以如下形式调用 detail()
:
detail(request=, question_id=34)
question_id=34 由
为每个 URL 加上不必要的东西,例如 .html ,是没有必要的。不过如果你非要加的话,也是可以的
path('polls/latest.html', views.index),
但是,别这样做,这太傻了。
写一个真正有用的视图
每个视图必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse
对象,或者抛出一个异常,比如 Http404
。至于你还想干些什么,随便你。
你的视图可以从数据库里读取记录,可以使用一个模板引擎(比如 Django 自带的,或者其他第三方的),可以生成一个 PDF 文件,可以输出一个 XML,创建一个 ZIP 文件,你可以做任何你想做的事,使用任何你想用的 Python 库。
Django 只要求返回的是一个 HttpResponse
,或者抛出一个异常。
因为 Django 自带的数据库 API 很方便,我们曾在 教程第 2 部分 中学过,所以我们试试在视图里使用它。我们在 index()
函数里插入了一些新内容,让它能展示数据库里以发布日期排序的最近 5 个投票问题,以空格分割
polls/views.py
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
这里有个问题:页面的设计写死在视图函数的代码里的。如果你想改变页面的样子,你需要编辑 Python 代码。所以让我们使用 Django 的模板系统,只要创建一个视图,就可以将页面的设计从代码中分离出来。
首先,在你的** polls
目录里创建一个 templates
**目录。Django 将会在这个目录里查找模板文件。
你项目的 TEMPLATES
配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 DjangoTemplates
后端,并将 **APP_DIRS
**设置成了 True。这一选项将会让 DjangoTemplates
在每个 INSTALLED_APPS
文件夹中寻找 "templates" 子目录。这就是为什么尽管我们没有像在第二部分中那样修改 DIRS 设置,Django 也能正确找到 polls 的模板位置的原因。
在你刚刚创建的 templates 目录里,再创建一个目录 polls,然后在其中新建一个文件 index.html 。换句话说,你的模板文件的路径应该是 polls/templates/polls/index.html 。因为 Django 会寻找到对应的 app_directories ,所以你只需要使用 polls/index.html 就可以引用到这一模板了
将下面的代码输入到刚刚创建的模板文件中:
以列表方式展示前5个投票详情
polls/templates/polls/index.html
{% if latest_question_list %}
{% for question in latest_question_list %}
- {{ question.question_text }}
{% endfor %}
{% else %}
No polls are available.
{% endif %}
然后,让我们更新一下 polls/views.py 里的 index 视图来使用模板:
polls/views.py
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
上述代码的作用是,载入 polls/index.html
模板文件,并且向它传递一个上下文(context)。这个上下文是一个字典,它将模板内的变量映射为 Python 对象。
用你的浏览器访问 "/polls/" ,你将会看见一个无序列表,列出了我们在 教程第 2 部分 中添加的 “What's up” 投票问题,链接指向这个投票的详情页。
一个快捷函数: [render()](https://docs.djangoproject.com/zh-hans/2.1/topics/http/shortcuts/#django.shortcuts.render "django.shortcuts.render")
「载入模板,填充上下文,再返回由它生成的 HttpResponse
对象」是一个非常常用的操作流程。于是 Django 提供了一个快捷函数,我们用它来重写 index()
视图:
polls/views.py
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context= {'lon_list': latest_question_list}
return render(request, 'index.html', context)
抛出 404 错误
现在,我们来处理投票详情视图——它会显示指定投票的问题标题。下面是这个视图的代码:
polls/views.py
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'detail.html', {'question': question})
这里有个新原则。如果指定问题 ID 所对应的问题不存在,这个视图就会抛出一个 Http404
异常。
我们稍后再讨论你需要在 polls/detail.html
里输入什么,但是如果你想试试上面这段代码是否正常工作的话,你可以暂时把下面这段输进去:
polls/templates/polls/detail.html
{{ question }}
这样你就能测试了
一个快捷函数: get_object_or_404()
尝试用 get()
函数获取一个对象,如果不存在就抛出 Http404
错误也是一个普遍的流程。Django 也提供了一个快捷函数,下面是修改后的详情 detail()
视图代码:
polls/views.py
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'detail.html', {'question': question})
使用模板系统
回过头去看看我们的 detail() 视图。它向模板传递了上下文变量 question 。下面是 polls/detail.html 模板里正式的代码:
polls/templates/polls/detail.html
{{ question.question_text }}
{% for choice in question.choice_set.all %}
- {{ choice.choice_text }}
{% endfor %}
模板系统统一使用点符号来访问变量的属性。在示例 {{ question.question_text }}
中,首先 Django 尝试对 question
对象使用字典查找(也就是使用 obj.get(str) 操作),如果失败了就尝试属性查找(也就是 obj.str 操作),结果是成功了。如果这一操作也失败的话,将会尝试列表查找(也就是 obj[int] 操作)。
在 {% for %}
循环中发生的函数调用:question.choice_set.all
被解释为 Python 代码 question.choice_set.all()
,将会返回一个可迭代的 Choice
对象,这一对象可以在 {% for %}
标签内部使用。
去除模板中的硬编码 URL
还记得吗,我们在 polls/index.html
里编写投票链接时,链接是硬编码的:
问题在于,硬编码和强耦合的链接,对于一个包含很多应用的项目来说,修改起来是十分困难的。然而,因为你在 polls.urls
的 url()
函数中通过 name 参数为 URL 定义了名字,你可以使用 **{% url %}
**标签代替它:
这个标签的工作方式是在 polls.urls
模块的 URL 定义中寻具有指定名字的条目。你可以回忆一下,具有名字 'detail' 的 URL 是在如下语句中定义的:
...
# the 'name' value as called by the {% url %} template tag
path('/', views.detail, name='detail'),
...
如果你想改变投票详情视图的 URL,比如想改成 polls/specifics/12/ ,你不用在模板里修改任何东西(包括其它模板),只要在 polls/urls.py 里稍微修改一下就行:
...
# added the word 'specifics'
path('specifics//', views.detail, name='detail'),
...
访问index: 127.0.0.1:8000/polls/
为 URL 名称添加命名空间
教程项目只有一个应用,polls 。在一个真实的 Django 项目中,可能会有五个,十个,二十个,甚至更多应用。Django 如何分辨重名的 URL 呢?举个例子,polls 应用有 detail 视图,可能另一个博客应用也有同名的视图。Django 如何知道 {% url %} 标签到底对应哪一个应用的 URL 呢?
答案是:在根 URLconf 中添加命名空间。在 polls/urls.py 文件中稍作修改,加上 app_name 设置命名空间:
polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('/', views.detail, name='detail'),
path('/results/', views.results, name='results'),
path('/vote/', views.vote, name='vote'),
]
现在,编辑 polls/index.html 文件,从:
polls/templates/polls/index.html
{{ question.question_text }}
修改为指向具有命名空间的详细视图:
polls/templates/polls/index.html
{{ question.question_text }}
编写你的第一个 Django 应用,第 4 部分
编写一个简单的表单
让我们更新一下在上一个教程中编写的投票详细页面的模板 ("polls/detail.html") ,让它包含一个 HTML