0%

django

1 Django 简介

1.1 基本介绍

Django 是一个高级的 Python Web 框架,用于快速开发可维护和可扩展的 Web 应用程序。

使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务。

Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

Django想要理解,首先要明白的就是Django的大致组成,它大致由四部分组成,也就是提到的MTV模型:

  • Model(模型):负责业务对象与数据库的对象(orm)

  • Template(模板):负责把数据展示给用户(前端的东西)

  • View(视图):负责业务逻辑,并在适当的时候会调用Model和Template

  • URL分发器:通过前台请求的url,来分配调到相应的函数操作,类似于网络中的路由表,控制网络的下一跳的地址。

  • 1、浏览器输入url发送访问请求,首先到达django的URL控制器(urls.py)。

  • 2、URL控制器通过URL地址分配跳转到相应的View视图(views.py)中的对应函数。

  • 3、View视图如果需要访问数据库操作数据,需要实例化Model模型中的类(models.py),然后通过对类的操作就是对数据库的操作。

  • 4、
    • a)如果未创建数据库,Model模型里面是以面向对象的形式将每张数据表以类的方式创建出来,然后通过makemigrations就可以同步到数据库来进行表的创建和表关系的建立。
    • b)如果已创建数据库,并且已经同步数据库,那么实例化类的对象,就可以操作数据库了。
  • 5、返回数据库请求的数据。

  • 6、上一步请求的数据实在View视图实例化类的地方接收数据。

  • 7、在View模型中,通过对数据的操作或者其他的相应操作、计算等,将数据参数传递到Template模板。

  • 8、将View模板中的数据接收,通过将其嵌入到前端页面的代码中,实现漂亮的可视化展示。

2. 通过案例入门

如果这是你第一次使用 Django 的话,你需要一些初始化设置。也就是说,你需要用一些自动生成的代码配置一个 Django project —— 即一个 Django 项目实例需要的设置项集合,包括数据库配置、Django 配置和应用程序配置。资料来源django中文文档

打开命令行,cd 到一个你想放置你代码的目录,然后运行以下命令:

1
django-admin startproject mysite
这行代码将会在当前目录下创建一个 mysite 目录。
1
2
3
4
5
6
7
8
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

  • 最外层的 mysite/ 根目录只是你项目的容器, 根目录名称对 Django 没有影响,你可以将它重命名为任何你喜欢的名称。
  • manage.py: 一个让你用各种方式管理 Django 项目的命令行工具。你可以阅读 django-admin 和 manage.py 获取所有 manage.py 的细节。(manage.py的作用个django-admin命令是一样的)
  • 里面一层的 mysite/ 目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如 mysite.urls).
  • mysite/init.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。如果你是 Python 初学者,阅读官方文档中的 更多关于包的知识。
  • mysite/settings.py:Django 项目的配置文件。如果你想知道这个文件是如何工作的,请查看 Django 配置 了解细节。
  • ** mysite/urls.py:**Django 项目的 URL 声明,就像你网站的“目录”。阅读 URL调度器 文档来获取更多关于 URL 的内容。
  • ** mysite/asgi.py:**作为你的项目的运行在 ASGI 兼容的 Web 服务器上的入口。阅读 如何使用 ASGI 来部署 了解更多细节。
  • mysite/wsgi.py:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。阅读 如何使用 WSGI 进行部署 了解更多细节。

2.1 Django 配置settings.py

Django 的配置文件包含 Django 应用的所有配置项。本文档介绍配置是如何生效的,以及哪些设置项是可配置的。首先知道配置文件仍然是一个使用模块级变量的一个 Python 文件。看一组例子:

1
2
3
ALLOWED_HOSTS = ['www.example.com']
DEBUG = False
DEFAULT_FROM_EMAIL = 'webmaster@example.com'

如果你将 DEBUG 设置为 False,同时你需要正确的设置 ALLOWED_HOSTS。

因为配置文件是一个 Python 模块,所以要注意以下几项:

  • 不能有 Python 语法错误
  • 可以用 Python 语法实现动态配置,如MY_SETTING = [str(i) for i in range(30)]
  • 可以从其他配置文件中引入变量

2.1.1 指定配置文件

当你使用 Django 的时候,你必须告诉它你正在使用哪些设置。通过使用环境变量 DJANGO_SETTINGS_MODULE 来实现。

DJANGO_SETTINGS_MODULE 的值是一个符合 Python 语法的路径,例如 mysite.settings。要注意配置模块应位于 Python 的 import 搜索路径中。

使用django-admin或者manage.py时,你可以一次性设定环境变量,也可以在运行此工具时,显式地传入配置模块,如:

  • 1.在Linux的bash中使用export设置DJANGO_SETTINGS_MODULE的值位mysite.settings

    1
    2
    export DJANGO_SETTINGS_MODULE=mysite.settings
    django-admin runserver

  • 2.或者使用 --settings命令行参数指定配置模块路径:

    1
    django-admin runserver --settings=mysite.settings

export命令是 shell 环境中用来设置或显示环境变量的命令,它在 Unix 和 Linux 系统中设置的环境变量是一次性的,这意味着它们只在当前的 shell 会话中有效。

此外在服务器环境下,要通知WSGI应用当前使用的是哪个配置文件,用到 os.environ(当前环境变量字典,运行设置、获取和删除当前环境变量):

1
2
>import os
>os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

2.1.2 默认配置文件

在无需配置的情况下,Django配置文件没必要定义任何配置项。因为每个配置项都有一个明确的默认值。默认值都保存在模块 django/conf/global_settings.py中。

Django按如下算法编译配置模块:

  • global_settings.py中加载配置项。
  • 从指定的配置文件中加载配置项,覆盖对应的全局配置项。

注意: 配置文件中不要再重复引用 global_settings,因为这样做是多余的。

2.1.3 使用django的settings对象

django.conf.settings是一个对象,该对象是整合过默认配置django/conf/global_settings.py以及你自定义的指定配置文件后所产生的,即在settings对象中你自己的配置已经覆盖了默认配置了,记住!!它是一个对象,而不是一个模块,因此应是这样使用:

1
2
3
4
from django.conf import settings

if settings.DEBUG:
# Do something

2.1.4 Django的配置项列表

既然是Django的配置,当然所使用的规则是由Django所规定,下面僵尸其配置关键key

  • ABSOLUTE_URL_OVERRIDES(默认:{}(空字典)):将 "app_label.model_name"形式的字符串映射到接受模型对象并返回其 URL 的函数的字典。这是一种在每个预安装基础上插入或覆盖 get_absolute_url() 方法的方式(在此配置中使用的模型名称应全部为小写,与实际模型类名称的大小写无关。)

    1
    2
    3
    4
    ABSOLUTE_URL_OVERRIDES = {
    'blogs.blog': lambda o: "/blogs/%s/" % o.slug,
    'news.story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
    }

  • ADMINS(默认: [] (空列表)):所有收到代码错误通知的人的列表。当 DEBUG=False 和 AdminEmailHandler 中设置了 LOGGING 时(默认情况下是这样做的),Django 会将请求/响应周期中出现的异常的详细信息通过邮件发送给这些人。列表中的每个项目应该是一个元组 (全名, 电子邮件地址)

    1
    [('John', 'john@example.com'), ('Mary', 'mary@example.com')]

  • ALLOWED_HOSTS(默认: [] (空列表)):一个代表这个 Django 网站可以服务的主机/域名的字符串列表。这是一个安全措施,以防止 HTTP 主机头攻击 ,即使在许多看似安全的 Web 服务器配置下也有可能发生。

  • CSRF_COOKIE_DOMAIN(默认: None):设置 CSRF cookie 时要使用的域。 这对于允许跨子域请求被排除在正常的跨站点请求伪造保护之外是很有用的。 它应该设置为一个字符串,如 ".example.com",以允许一个子域上的表单的 POST 请求被另一个子域的视图所接受。

  • DATABASES(默认: {} (空字典)):一个包含所有数据库配置的字典,用于 Django。它是一个嵌套的字典,其内容是将一个数据库别名映射到一个包含单个数据库选项的字典中。DATABASES 配置必须设置一个 default 数据库;也可以指定任何数量的其他数据库。最简单的配置文件是使用 SQLite 的单数据库配置。可以通过以下方式进行配置:

    1
    2
    3
    4
    5
    6
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': 'mydatabase',
    }
    }

  • INSTALLED_APPS(默认: [] (空列表)):一个字符串的列表,表示在这个 Django 安装中所有被启用的应用程序。每一个字符串都应该是一个 Python 的点分隔路径。

还有许多其他的配置key,详见django配置项

2.1.5 动态配置django的配置

上面讲的DJANGO_SETTINGS_MODULE--settings都是你在启动django框架时传入配置文件给它,不允许你动态配置,即程序运行到某个片段时配置是不支持的,因此就有了django.conf.settings.configure(default_settings, **settings)函数,通过 configure() 可以设置任何配置项,每个参数对应一个值。参数名称必须大写,而且参数名必须是真实存在。没有在 configure() 中出现的设置项在随后用到时,仍会使用默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.conf import settings  

# 如果尚未配置settings,进行动态配置
if not settings.configured:
settings.configure(
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
},
INSTALLED_APPS=[
'myapp',
],
# 其他必要的settings...
)

# 现在你可以导入并使用Django的模型和其他功能了
from myapp.models import MyModel
>请注意,django.conf.settings.configure() 应该在Django的任何代码被加载之前调用,以确保Django的内部状态被正确初始化。一旦Django已经加载了settings(例如,通过DJANGO_SETTINGS_MODULE环境变量),再调用configure()方法将不会有任何效果

  • 如果你没有设置 DJANGO_SETTINGS_MODULE\--settings 也没有调用 configure()那么 Django 会在第一次访问配置项时抛出 ImportError 异常。
  • 如果设置了 DJANGO_SETTINGS_MODULE,以某种方式访问配置的值,再 调用 configure() 的话,Django 将会抛出一个 RuntimeError 异常,表明已经设置了配置。
  • 重复调用 configure() 或是在访问任何一个配置项后再调用 configure(),也会引起错误。这意味着:确保要使用 configure() 或 DJANGO_SETTINGS_MODULE 其中一个,不要多,也不要少。

2.2 创建应用

在使用django-admin startproject mysite生成一个djabgo项目后,你可以运行下述语句启动django服务器,通过浏览器访问 http://127.0.0.1:8000/ 。你将看到一个“祝贺”页面,有一只火箭正在发射。你成功了!

1
$ python manage.py runserver

这是django默认的服务器,你需要根据你自己的需求搭建你自己的服务器项目,在django中,你要区分项目和应用

项目和应用有什么区别?应用是一个专门做某件事的网络应用程序——比如博客系统,或者公共记录的数据库,或者小型的投票程序。项目则是一个网站使用的配置和应用的集合。项目可以包含很多个应用。应用可以被很多个项目使用。

使用startapp来在一个项目创建app,你的应用可以存放在任何 Python 路径 中定义的路径。在这个教程中,我们将在你的 manage.py 同级目录下创建投票应用,而不是在mysite下。这样它就可以作为顶级模块导入,而不是 mysite 的子模块。

1
2
#创建一个名位poll的应用
python manage.py startapp polls
这将会创建一个 polls 目录,它的目录结构大致如下:
1
2
3
4
5
6
7
8
9
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
这个目录结构包括了投票应用的全部内容。

2.2.1 编写视图

让我们开始编写第一个视图吧。打开 polls/views.py,把下面这些 Python 代码输入进去:

1
2
3
4
5
from django.http import HttpResponse


def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
这是 Django 中最简单的视图。如果想看见效果,我们需要将一个 URL 映射到它——这就是我们需要 URLconf 的原因了。

为了创建 URLconf,要在 polls 目录里新建一个 urls.py 文件。你的应用目录现在看起来应该是这样:

1
2
3
4
5
6
7
8
9
10
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
urls.py
views.py
polls/urls.py 中,输入如下代码:
1
2
3
4
5
6
7
from django.urls import path

from . import views

urlpatterns = [
path('', views.index, name='index'),
]
下一步是要在根 URLconf 文件中指定我们创建的 polls.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:
1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]

  • include():include()函数通常用于URL配置中,它允许你将一个URL配置(通常是一个urls.py模块)包含(或“引入”)到另一个URL配置中

    1
    2
    3
    4
    5
    from django.urls import include, path  
    urlpatterns = [
    path('some/path/', include('myapp.urls')),
    # ... 其他URL模式
    ]

  • path函数:path 函数允许你以一种更简洁、可读的方式指定URL模式,而不需要使用正则表达式。它的基本语法如下:
    1
    path(route, view, name=None)
  • route:一个字符串,描述URL的路径模式。它不支持正则表达式,只能使用字面字符串或者路径转换器。
  • view:当URL模式匹配成功时要调用的视图函数或者基于类的视图。
  • name:一个可选的名字字符串,用于在Django的模板、模型等地方引用这个URL模式。

2.3 数据库配置

现在,打开 mysite/settings.py 。这是个包含了 Django 项目设置的 Python 模块。通常,这个配置文件使用 SQLite 作为默认数据库。如果你不熟悉数据库,或者只是想尝试下 Django,这是最简单的选择。Python 内置 SQLite,所以你无需安装额外东西来使用它。当你开始一个真正的项目时,你可能更倾向使用一个更具扩展性的数据库,例如 PostgreSQL,避免中途切换数据库这个令人头疼的问题。

如果你想使用其他数据库,你需要安装合适的 database bindings ,然后改变设置文件中 DATABASES 'default' 项目中的一些键值:

  • ENGINE -- 可选值有 'django.db.backends.sqlite3','django.db.backends.postgresql','django.db.backends.mysql',或 'django.db.backends.oracle'
  • NAME -- 数据库的名称。如果你使用 SQLite,数据库将是你电脑上的一个文件,在这种情况下,NAME 应该是此文件完整的绝对路径,包括文件名。默认值 BASE_DIR / 'db.sqlite3' 将把数据库文件储存在项目的根目录

如果你不使用 SQLite,则必须添加一些额外设置,比如 USER 、 PASSWORD 、 HOST 等等。想了解更多数据库设置方面的内容,请看上方的seting配置文档

此外,关注一下文件头部的 INSTALLED_APPS 设置项。这里包括了会在你项目中启用的所有 Django 应用。应用能在多个项目中使用,你也可以打包并且发布应用,让别人使用它们。

通常, INSTALLED_APPS 默认包括了以下 Django 的自带应用:

  • django.contrib.admin -- 管理员站点, 你很快就会使用它。
  • django.contrib.auth -- 认证授权系统。
  • django.contrib.contenttypes -- 内容类型框架。
  • django.contrib.sessions -- 会话框架。
  • django.contrib.messages -- 消息框架。
  • django.contrib.staticfiles -- 管理静态文件的框架。

这些应用被默认启用是为了给常规项目提供方便。

默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。请执行以下命令:

1
$ python manage.py migrate

这个 migrate 命令查看 INSTALLED_APPS 配置,并根据 mysite/settings.py 文件中的数据库配置和随应用提供的数据库迁移文件(我们将在后面介绍这些),创建任何必要的数据库表。你会看到它应用的每一个迁移都有一个消息。如果你有兴趣,运行你的数据库的命令行客户端,输入 (PostgreSQL), SHOW TABLES; (MariaDB,MySQL), .tables (SQLite)或 SELECT TABLE_NAME FROM USER_TABLES; (Oracle)来显示 Django 创建的表。

2.3.1 创建模型

在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据。

在这个投票应用中,需要创建两个模型:问题 Question 和选项 Choice。Question 模型包括问题描述和发布时间。Choice 模型有两个字段,选项描述和当前得票数。每个选项属于一个问题。

这些概念可以通过一个 Python 类来描述。按照下面的例子来编辑 polls/models.py 文件:

1
2
3
4
5
6
7
8
9
10
11
12
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.db.models.Model 类的子类。每个模型有许多类变量,它们都表示模型里的一个数据库字段

每个字段都是 Field 类的实例:

  • CharField:表示数据库的字符串类型,如varchar
  • DateTimeField:表示数据库的时间字段
  • IntegerField:表示数据库的整型字段

我们使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象

2.3.2 激活模型

上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:

  • 为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
  • 创建可以与 Question 和 Choice 对象进行交互的 Python 数据库 API。

但是首先得把 polls 应用安装到我们的项目里。

为了在我们的工程中包含这个应用,我们需要在配置类INSTALLED_APPS中添加设置。因为 PollsConfig类写在文件 polls/apps.py中,所以它的点式路径是 'polls.apps.PollsConfig'。在文件 mysite/settings.pyINSTALLED_APPS 子项添加点式路径后,它看起来像这样:

1
2
3
4
5
6
7
8
9
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 应用。接着运行下面的命令:

1
$ python manage.py makemigrations polls
你将会看到类似于下面这样的输出:
1
2
3
4
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移。(迁移是指Django 对于模型定义(也就是你的数据库结构)的变化的储存形式,而不是数据迁移,数据如果不是删除字段,一般不会丢失,使数据库平滑升级而不会丢失数据)

django 有一个自动执行数据库迁移并同步管理你的数据库结构的命令 - 这个命令是 migrate,我们马上就会接触它 - 但是首先,让我们看看迁移命令会执行哪些 SQL 语句。sqlmigrate 命令接收一个迁移的名称,然后返回对应的 SQL:

1
$ python manage.py sqlmigrate polls 0001
你将会看到下面这样的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL,
"question_id" bigint NOT NULL
);
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

请注意以下几点:

  • 输出的内容和你使用的数据库有关,上面的输出示例使用的是 PostgreSQL。
  • 数据库的表名是由应用名(polls)和模型名的小写形式( question 和 choice)连接而来。(如果需要,你可以自定义此行为。)
  • 主键(IDs)会被自动创建。(当然,你也可以自定义。)
  • 默认的,Django 会在外键字段名后追加字符串 "_id" 。(同样,这也可以自定义。)
  • 外键关系由 FOREIGN KEY 生成。你不用关心 DEFERRABLE 部分,它只是告诉 PostgreSQL,请在事务全都执行完之后再创建外键关系。
  • 这个 sqlmigrate 命令并没有真正在你的数据库中的执行迁移 - 相反,它只是把命令输出到屏幕上,让你看看 Django 认为需要执行哪些 SQL 语句。这在你想看看 Django 到底准备做什么,或者当你是数据库管理员,需要写脚本来批量处理数据库时会很有用

现在,再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:

1
2
3
4
5
6
ython manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
这个 migrate 命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。

迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。我们会在后面的教程中更加深入的学习这部分内容,现在,你只需要记住,改变模型需要这三步:

  • 编辑 models.py 文件,改变模型。
  • 运行 python manage.py makemigrations为模型的改变生成迁移文件。
  • 运行 python manage.py migrate来应用数据库迁移。

2.3.3 API

执行上述python manage.py migrate命令后,django就为我们创建了与这些数据库表交换的python API,我们可以通过shell来初步尝试:

1
python manage.py shell
>我们使用这个命令而不是简单的使用“python”是因为 manage.py 会设置 DJANGO_SETTINGS_MODULE 环境变量,这个变量会让 Django 根据 mysite/settings.py 文件来设置 Python 包的导入路径。

当你成功进入命令行后,来试试 数据库 API 吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

等等!!!<Question: Question object (1)> 对于我们了解这个对象的细节没什么帮助。让我们通过编辑 Question 模型的代码(位于 polls/models.py 中)来修复这个问题。给 QuestionChoice 增加 __str__()方法。

1
2
3
4
5
6
7
8
9
10
11
from django.db import models

class Question(models.Model):
# ...
def __str__(self):
return self.question_text

class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text

2.4 编写更多的视图

2.4.1 简单理解

现在让我们向 polls/views.py 里添加更多视图。这些视图有一些不同,因为他们接收参数:

1
2
3
4
5
6
7
8
9
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() 函数调用就行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.urls import path

from . import views

urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
当你把这个polls/urls.py配置进根目录即mysite/urls.py时,启动服务器: 当某人请求你网站的某一页面时——比如说, "/polls/34/" ,Django 将会载入 mysite.urls 模块(因为这在配置项 ROOT_URLCONF 中设置了)。然后 Django 寻找名为 urlpatterns 变量并且按序匹配正则表达式。在找到匹配项 'polls/',它切掉了匹配的文本("polls/"),将剩余文本——"34/",发送至 'polls.urls' URLconf 做进一步处理。在这里剩余文本匹配了 '/',使得我们 Django 以如下形式调用 detail():
1
detail(request=<HttpRequest object>, question_id=34)

question_id=34 来自 <int:question_id>。使用尖括号 "获得" 网址部分后发送给视图函数作为一个关键字参数。字符串的question_id部分定义了要使用的名字,用来识别相匹配的模式,而 int 部分是一种转换形式,用来确定应该匹配网址路径的什么模式。冒号 (:) 用来分隔转换形式和模式名。

2.4.2 写一个真正有用的视图

每个视图必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse 对象,或者抛出一个异常,比如 Http404 。至于你还想干些什么,随便你。

你的视图可以从数据库里读取记录,可以使用一个模板引擎(比如 Django 自带的,或者其他第三方的),可以生成一个 PDF 文件,可以输出一个 XML,创建一个 ZIP 文件,你可以做任何你想做的事,使用任何你想用的 Python 库。

Django 只要求返回的是一个 HttpResponse ,或者抛出一个异常。

如下是问题投票的views.py需要的代码

1
2
3
4
5
6
7
8
9
10
11
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)

# Leave the rest of the views (detail, results, vote) unchanged

这里有个问题:页面的前端设计如果写死在视图函数的代码里的。如果你想改变页面的样子,你需要编辑 Python 代码。所以让我们使用** Django 的模板templates系统,专门负责前端设计**,只要创建一个视图,就可以将页面的设计从代码中分离出来。

  • 首先,在你的 polls 目录里创建一个 templates 目录。Django 将会在这个目录里查找模板文件
  • 项目的 TEMPLATES配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 DjangoTemplates 后端即BACKEND为DjangoTemplates,并将 APP_DIRS 设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 "templates" 子目录。这就是为什么尽管我们没有像在第二部分中那样修改 DIRS 设置,Django 也能正确找到 polls 的模板位置的原因。
  • 在你刚刚创建的 templates 目录里,再创建一个目录 polls,然后在其中新建一个文件 index.html 。换句话说,你的模板文件的路径应该是** polls/templates/polls/index.html 。因为app_directories 模板加载器是通过上述描述的方法运行的,所以 Django 可以引用到 polls/index.html 这一模板了。**

为什么需要再创建一个polls文件:

原因:虽然我们现在可以将模板文件直接放在 polls/templates 文件夹中(而不是再建立一个 polls 子文件夹),但是这样做不太好。Django 将会选择第一个匹配的模板文件,如果你有一个模板文件正好和另一个应用中的某个模板文件重名,Django 没有办法 区分 它们。我们需要帮助 Django 选择正确的模板,最好的方法就是把他们放入各自的应用 命名空间 中,也就是把这些模板放入一个和 自身 应用重名的子文件夹里。

将下面的代码输入到刚刚创建的模板文件index.html中:

1
2
3
4
5
6
7
8
9
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

然后,让我们更新一下 polls/views.py 里的 index 视图来使用模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
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('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
上述代码的作用是,载入 polls/index.html 模板文件,并且向它传递一个上下文(context)。这个上下文是一个字典,它将模板内的变量映射为 Python 对象。

2.4.3 render函数

「载入模板,填充上下文,再返回由它生成的 HttpResponse 对象」是一个非常常用的操作流程。于是 Django 提供了一个快捷函数,那就是render,有了render后,我们用它再次重写 index() 视图:

1
2
3
4
5
6
7
8
9
from django.shortcuts import render

from .models import Question


def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)

2.4.4 抛出 404 错误

现在,我们来处理投票详情视图——它会显示指定投票的问题标题。下面是在polls/views.py这个视图的代码:

1
2
3
4
5
6
7
8
9
10
11
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, 'polls/detail.html', {'question': question})
我们稍后再讨论你需要在 polls/detail.html 里输入什么,但是如果你想试试上面这段代码是否正常工作的话,你可以暂时把下面这段输进polls/templates/polls/detail.html
1
2
3
4
5
6
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

上面的html模板文件都是django定义的格式,在html语言的基础上加了支持,更多看模板指南

2.4.5 request

django中传给views中函数的request参数是什么。在 Django 的 views(视图)中,当你处理一个 HTTP 请求时,通常会接收到一个 request 对象作为函数的第一个参数。这个 request 对象包含了关于当前请求的所有信息,包括 GET/POST 参数、请求头、用户会话等

具体来说,request 对象是一个 HttpRequest 类的实例,它包含以下主要属性和方法:

属性:

  • path: 一个字符串,表示请求的完整路径,不包括域名和查询字符串。
  • method: 一个字符串,表示 HTTP 请求方法,如 "GET" 或 "POST"。
  • GET: 一个类似字典的对象,包含所有的 GET 参数。
  • POST: 一个类似字典的对象,包含所有的 POST 参数(仅在 POST 请求中可用)。
  • FILES: 一个类似字典的对象,包含所有的上传文件(仅在 POST 请求中可用)。
  • COOKIES: 一个类似字典的对象,包含所有的 cookie。
  • session: 一个可读的类似字典的对象,用于在多个请求之间存储用户会话数据。
  • user: 一个 User 对象,表示当前用户。如果用户未登录,则为 AnonymousUser。
  • META: 一个包含所有 HTTP 请求头的字典。
  • body: 一个字节字符串,表示请求的原始内容体。
  • ...以及其他一些属性和方法。

方法:

  • is_ajax(): 如果请求是通过 AJAX 提交的,则返回 True。
  • is_secure(): 如果请求是通过 HTTPS 发送的,则返回 True。
  • is_http_method_allowed(method): 检查是否允许特定的 HTTP 方法。
  • resolve_conflict(): 仅在处理 PUT/PATCH 请求且有两个冲突的响应头时使用。
  • ...以及其他一些方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import render  

def my_view(request):
if request.method == 'GET':
# 处理 GET 请求
query_param = request.GET.get('param_name')
# ...
elif request.method == 'POST':
# 处理 POST 请求
form_data = request.POST.get('form_field_name')
# ...
# 你可以使用 request.user 来获取当前用户
current_user = request.user
# ...
return render(request, 'template.html', context)