《Django》入门 Notes(上)

@Seymour0314 来源官方文档https://docs.djangoproject.com/zh-hans/4.2/intro/

本文是Django 后端技术初级入门教程。

编程语言:Python

image-20230510110254656

初识 Django

Django 最初被设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发。以下内容简要介绍了如何使用 Django 实现一个数据库驱动的网络应用。

从哪里获得帮助:

如果你在阅读本教程的过程中有任何疑问,可以前往 FAQ 的 获取帮助 的版块。

官方文档:https://docs.djangoproject.com/zh-hans/4.2/topics/

快速安装指南

在 Windows 上安装 Django

安装Python
image-20230511140356777
  • Django 是一个 Python Web 框架,因此需要在您的机器上安装 Python。

Python官方下载地址 https://www.python.org/downloads/

查看版本

1
...\> py --version
配置一个虚拟环境
  • 最佳实践是为你创建的每一个Django项目创建一个独立的环境。

    在Python生态系统中有许多可以用来管理环境和包的可选工具,一些在 Python documentation 被推荐过。我们将会在这份指南中使用Python本身自带的 venv 来管理环境。

在当前文件夹下创建虚拟环境:

1
2
3
4
# 在 project-name 文件夹下创建一个虚拟环境(若project-name文件夹不存在,则会新建)
...\> py -m venv project-name
# 激活环境
...\> project-name\Scripts\activate.bat

每次你打开新的命令行窗口,你需要再次激活环境。

安装 Django
  • Django 可以轻松地在你的虚拟环境中使用 pip 安装。

确保虚拟环境已经激活,输入下面命令:

1
...\> py -m pip install Django

这将下载并安装最新的 Django 发布版本。

安装完成后,你可以在命令提示符运行 django-admin --version 验证你安装的 Django。

image-20230511142702003 image-20230511143558371

非虚拟环境查看版本号:

1
...\> py -m django --version
安装 Apache 和 mod_wsgi
  • 如果您只是想试验 Django,请跳到下一部分;

    Django 包含一个可用于测试的轻量级 Web 服务器,

    因此在准备好在生产环境中部署 Django 之前,您不需要设置 Apache。

运行你的数据库
  • 如果你打算使用 Django 的数据库 API 功能,你需要确保一个数据库服务器正在运行。

    Django 支持许多不同的数据库服务器,官方支持 PostgreSQLMariaDBMySQLOracleSQLite

如果你正在开发一个小项目或不打算在生产环境中部署的东西,SQLite 通常是最好的选择,因为它不需要运行一个单独的服务器。然而,SQLite 与其他数据库有许多不同之处,所以如果你正在开发一些实质性的东西,建议使用你计划在生产中使用的同一数据库进行开发。

除了官方支持的数据库,还有 第三方提供的后端 允许你在 Django 中使用其他数据库。

  • 除了数据库后端,你还要确保安装了 Python 数据库绑定。

  • 操作数据库

    • 如果你打算使用 Django 的 manage.py migrate 命令为你的模型自动创建数据库表(首先安装 Django 并创建项目后),

      你需要确保 Django 有权限在你使用的数据库中创建和修改表;

    • 如果你打算手动创建表,你可以授予 Django SELECTINSERTUPDATEDELETE 权限。

      创建具有这些权限的数据库用户后,你将在项目的配置文件中指定详细信息,详细信息参见 DATABASES

    • 如果你正在使用 Django 的 测试框架 来测试数据库查询,Django 将需要创建测试数据库的权限。

编写你的第一个 Django 应用

操作前记得激活环境

0.0 激活环境

1
2
# 激活环境
...\> project-name\Scripts\activate.bat

如若激活后,仍然报错说没有Django模块

那么:

1
...\> py -m pip install Django

安装好,查看django-admin --version是否正常输出版本号。

第 1 部分

让我们通过示例来学习。

通过这个教程,我们将带着你创建一个基本的投票应用程序。

它将由两部分组成:

  • 一个让人们查看和投票的公共站点。
  • 一个让你能添加、修改和删除投票的管理站点。

本教程是为Django 4.2 编写的,它支持 Python 3.8 及以后的版本。

我的版本号是:

  • Django 4.2.1
  • Python 3.11.1

1.1 创建项目

  • 如果这是你第一次使用 Django 的话,你需要一些初始化设置。

    也就是说,你需要用一些自动生成的代码配置一个 Django project —— 即一个 Django 项目实例需要的设置项集合,包括数据库配置、Django 配置和应用程序配置。

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

    我们可以放在与前面project文件夹的并行目录下。

    1
    ...\> django-admin startproject mysite

    这行代码将会在当前目录下创建一个 mysite 目录。

    image-20230511143733355

    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 的细节。

    • 里面一层的 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 进行部署 了解更多细节。

1.2 用于开发的简易服务器

  • 让我们来确认一下你的 Django 项目是否真的创建成功了。

    如果你的当前目录不是外层的 mysite 目录的话,请切换到此目录,然后运行下面的命令:

    1
    2
    # 记得先cd mysite
    ...\> py manage.py runserver

    你应该会看到如下输出:

    image-20230511144922199

    忽略有关未应用最新数据库迁移的警告,稍后我们处理数据库。

  • 你已经启动了 Django 开发服务器,这是一个用纯 Python 编写的轻量级网络服务器。

    我们在 Django 中包含了这个服务器,所以你可以快速开发,而不需要处理配置生产服务器的问题

    比如 Apache – 直到你准备好用于生产。

    但是,千万不要 将这个服务器用于和生产环境相关的任何地方。这个服务器只是为了开发而设计的。

    (我们在网络框架方面是专家,在网络服务器方面并不是。)

  • 服务器现在正在运行,通过浏览器访问 http://127.0.0.1:8000/

    你将看到一个“祝贺”页面,有一只火箭正在发射。你成功了!

    image-20230511145011477

一些问题:

  • 更换端口

    默认情况下,runserver 命令会将服务器设置为监听本机内部 IP 的 8000 端口。

    如果你想更换服务器的监听端口,请使用命令行参数。

    举个例子,下面的命令会使服务器监听 8080 端口:

    1
    ...\> py manage.py runserver 8080
  • 如果你想要修改服务器监听的IP,在端口之前输入新的。

    比如,为了监听所有服务器的公开IP(这你运行 Vagrant 或想要向网络上的其它电脑展示你的成果时很有用),

    使用:

    1
    ...\> py manage.py runserver 0.0.0.0:8000

    关于这个简易服务器的完整信息可以在 runserver 文档中找到。

  • 会自动重新加载的服务器 runserver

    用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍 Python 代码。

    所以你不需要为了让修改的代码生效而频繁的重新启动服务器。

    然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。

1.3 创建投票应用

现在你的开发环境——这个“项目” ——已经配置好了,你可以开始干活了。

  • 在 Django 中,每一个应用都是一个 Python 包,并且遵循着相同的约定。

    Django 自带一个工具,可以帮你生成应用的基础目录结构,这样你就能专心写代码,而不是创建目录了。

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

    在这个教程中,我们将在你的 manage.py 同级目录下创建投票应用。

    这样它就可以作为顶级模块导入,而不是 mysite 的子模块。

    请确定你现在处于 manage.py 所在的目录(即外层的mysite目录)下,然后运行这行命令来创建一个应用:

    1
    2
    # 创建一个名叫polls 的应用
    ...\> py manage.py startapp polls
    image-20230511150105443

    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

1.4 编写第一个视图

让我们开始编写第一个视图吧。

  • 打开 polls/views.py,把下面这些 Python 代码输入进去:

    polls/views.py

    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 的原因了。

  • 就在polls目录下,新建一个urls.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/mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:

    注意:改了两处:

    • import include
    • path(…)

    mysite/mysite/urls.py

    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()允许引用其它 URLconfs。

      每当 Django 遇到 include() 时,

      它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。

    我们设计include()的理念是使其可以即插即用。

    因为投票应用有它自己的 URLconf( polls/urls.py ),他们能够被放在 “/polls/” , “/fun_polls/” ,“/content/polls/”,或者其他任何路径下,这个应用都能够正常工作。

    • 何时使用 include()

      当包括其它 URL 模式时你应该总是使用 include()admin.site.urls 是唯一例外。

  • 你现在把 index 视图添加进了 URLconf。通过以下命令验证是否正常工作:

    1
    ...\> py manage.py runserver

    用你的浏览器访问 http://localhost:8000/polls/

    你应该能够看见 “Hello, world. You’re at the polls index.” ,这是你在 index 视图中定义的。

    image-20230511152225834

    没有找到页面?(你可能掉了/polls/

    如果你在这里得到了一个错误页面,

    检查一下你是不是正访问着http://localhost:8000/polls/

    而不应该是 http://localhost:8000/

关于 path 函数

  • 函数 path()具有四个参数,两个必须参数:routeview,两个可选参数:kwargsname

    1
    path(route,view,[kwargs],[name])
    • route

    一个匹配 URL 的准则(类似正则表达式)

    当 Django 响应一个请求时,它会从 urlpatterns 的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。

    这些准则不会匹配 GET 和 POST 参数或域名。例如,URLconf 在处理请求 https://www.example.com/myapp/ 时,它会尝试匹配 myapp/ 。处理请求 https://www.example.com/myapp/?page=3 时,也只会尝试匹配 myapp/

    • view

      调用视图

      当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,

      并传入一个 HttpRequest对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。

      稍后,我们会给出一个例子。

    • kwargs

    任意个关键字参数可以作为一个字典传递给目标视图函数。本教程中不会使用这一特性。

    • name

      为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。

第 2 部分

本教程从 教程1结束的地方开始。我们将设置数据库,创建第一个模型,并快速介绍 Django 自动生成的后台界面。

2.1 数据库配置

现在,打开 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,则必须添加一些额外设置,比如 USERPASSWORDHOST等等。想了解更多数据库设置方面的内容,请看文档:DATABASES

    SQLite 以外的其它数据库

    如果你使用了 SQLite 以外的数据库,请确认在使用前已经创建了数据库。你可以通过在你的数据库交互式命令行中使用 “CREATE DATABASE database_name;” 命令来完成这件事。

    另外,还要确保该数据库用户中提供 mysite/settings.py 具有 “create database” 权限。这使得自动创建的 test database 能被以后的教程使用。

    如果你使用 SQLite,那么你不需要在使用前做任何事——数据库会在需要的时候自动创建。

  • 编辑 mysite/settings.py 文件前,先设置 TIME_ZONE为你自己时区。

    中华人民共和国:PRC

    People’s Republic of China

    image-20230511160142502
  • 此外,关注一下文件头部的 INSTALLED_APPS 设置项。

    这里包括了会在你项目中启用的所有 Django 应用。应用能在多个项目中使用,你也可以打包并且发布应用,让别人使用它们。

    通常, INSTALLED_APPS

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

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

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

    1
    ...\> py manage.py migrate
    image-20230511161637443

    这个 migrate命令查看 INSTALLED_APPS 配置,并根据 mysite/settings.py 文件中的数据库配置和随应用提供的数据库迁移文件(我们将在后面介绍这些),创建任何必要的数据库表。

    你会看到它应用的每一个迁移都有一个消息。

    如果你有兴趣,运行你的数据库的命令行客户端,输入 \dt (PostgreSQL), SHOW TABLES; (MariaDB,MySQL), .tables (SQLite)或 SELECT TABLE_NAME FROM USER_TABLES; (Oracle)来显示 Django 创建的表。

    写给极简主义者

    就像之前说的,为了方便大多数项目,我们默认激活了一些应用,但并不是每个人都需要它们。如果你不需要某个或某些应用,你可以在运行 migrate前毫无顾虑地从 INSTALLED_APPS 里注释或者删除掉它们。 migrate 命令只会为在 INSTALLED_APPS里声明了的应用进行数据库迁移。

2.2 创建模型

在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型

也就是数据库结构设计和附加的其它元数据。

设计哲学

一个模型就是单个定义你的数据的信息源。模型中包含了不可缺少的数据区域和你存储数据的行为。Django 遵循 DRY 原则。目的就是定义你的数据模型要在一位置上,而且自动从该位置推导一些事情。

来介绍一下迁移 - 举个例子,不像 Ruby On Rails,Django 的迁移代码是由你的模型文件自动生成的,它本质上是个历史记录,Django 可以用它来进行数据库的滚动更新,通过这种方式使其能够和当前的模型匹配。

  • 在这个投票应用中,需要创建两个模型:问题 Question 和选项 Choice

    • Question 模型包括问题描述和发布时间。
    • Choice 模型有两个字段,选项描述和当前得票数。

    每个选项属于一个问题。

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

    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,日期时间字段被表示为 DateTimeField。这将告诉 Django 每个字段要处理的数据类型。

    • 每个 Field 类实例变量的名字(例如 question_textpub_date )也是字段名,所以最好使用对机器友好的格式。你将会在 Python 代码里使用它们,而数据库会将它们作为列名。

      你可以使用可选的选项来为 Field 定义一个人类可读的名字。这个功能在很多 Django 内部组成部分中都被使用了,而且作为文档的一部分。如果某个字段没有提供此名称,Django 将会使用对机器友好的名称,也就是变量名。

      在上面的例子中,我们只为 Question.pub_date 定义了对人类友好的名字。对于模型内的其它字段,它们的机器友好名也会被作为人类友好名使用。

    • 定义某些 Field类实例需要参数。

      例如 CharField 需要一个 max_length 参数。这个参数的用处不止于用来定义数据库结构,也用于验证数据,我们稍后将会看到这方面的内容。

    • Field也能够接收多个可选参数;在上面的例子中:我们将 votesdefault 也就是默认值,设为0。

    • 注意在最后,我们使用 ForeignKey定义了一个关系。

      这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一。

2.3 激活模型

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

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

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

设计哲学

Django 应用是“可插拔”的。你可以在多个项目中使用同一个应用。除此之外,你还可以发布自己的应用,因为它们并不会被绑定到当前安装的 Django 上。

  • 为了在我们的工程中包含这个应用,我们需要在配置类 INSTALLED_APPS 中添加设置。

    因为 PollsConfig 类写在文件 polls/apps.py 中,所以它的点式路径是

    1
    'polls.apps.PollsConfig'

    在文件 mysite/settings.pyINSTALLED_APPS子项添加点式路径后,它看起来像这样:

    image-20230511163554111
  • 现在你的 Django 项目会包含 polls 应用。接着运行下面的命令:

    1
    ...\> py manage.py makemigrations polls

    你将会看到类似于下面这样的输出:

    image-20230511163706521
    • 通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移

      • 迁移是 Django 对于模型定义(也就是你的数据库结构)的变化的储存形式

        它们其实也只是一些你磁盘上的文件。如果你想的话,你可以阅读一下你模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。别担心,你不需要每次都阅读迁移文件,但是它们被设计成人类可读的形式,这是为了便于你手动调整 Django 的修改方式。

  • Django 有一个自动执行数据库迁移并同步管理你的数据库结构的命令 ,这个命令是 migrate

    我们马上就会接触它 - 但是首先,让我们看看迁移命令会执行哪些 SQL 语句。

    sqlmigrate 命令接收一个迁移的名称,然后返回对应的 SQL:

    1
    ...\> py manage.py sqlmigrate polls 0001

    你将会看到类似下面这样的输出:

    image-20230511164322675
    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)和模型名的小写形式( questionchoice)连接而来。(如果需要,你可以自定义此行为。)
    • 主键(IDs)会被自动创建。(当然,你也可以自定义。)
    • 默认的,Django 会在外键字段名后追加字符串 "_id" 。(同样,这也可以自定义。)
    • 外键关系由 FOREIGN KEY 生成。你不用关心 DEFERRABLE 部分,它只是告诉 PostgreSQL,请在事务全都执行完之后再创建外键关系。
    • 它是为你正在使用的数据库定制的,因此特定于数据库的字段类型,例如“auto_increment”(MySQL)、“bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY”(PostgreSQL)或“integer primary key autoincrement” `` (SQLite) 会自动为您处理。 字段名称的引用也是如此——例如,使用双引号或单引号。
    • 这个 sqlmigrate命令并没有真正在你的数据库中的执行迁移 - 相反,它只是把命令输出到屏幕上,让你看看 Django 认为需要执行哪些 SQL 语句。这在你想看看 Django 到底准备做什么,或者当你是数据库管理员,需要写脚本来批量处理数据库时会很有用。

    如果你感兴趣,你也可以试试运行

    1
    python manage.py check

    这个命令帮助你检查项目中的问题,并且在检查过程中不会对数据库进行任何操作。

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

    1
    ...\> py manage.py migrate
    image-20230511164953600

    这个 migrate 命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。

    迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。

    我们会在后面的教程中更加深入的学习这部分内容,

    现在,你只需要记住,==改变模型需要这三步==:

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

    数据库迁移被分解成生成和应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。

    通过阅读文档 Django 后台文档 ,你可以获得关于 manage.py 工具的更多信息。

2.4 初试 API

现在让我们进入交互式 Python 命令行,尝试一下 Django 为你创建的各种 API。

  • 通过以下命令打开 Python 命令行:

    1
    ...\> py manage.py shell

    我们使用这个命令而不是简单的使用“python”是因为 manage.py 会设置 DJANGO_SETTINGS_MODULE环境变量,

    这个变量会让 Django 根据 mysite/settings.py 文件来设置 Python 包的导入路径。

    进入Shell交互命令行,能做什么?

    • 操作models (Django 已经为你创建了对应的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)>]>
  • 添加str方法

    <Question: Question object (1)> 对于我们了解这个对象的细节没什么帮助。

    让我们通过编辑 Question 模型的代码(位于 polls/models.py 中)来修复这个问题。

    QuestionChoice 增加 __str__()方法。

    polls/models.py

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

    给模型增加 __str__()方法是很重要的,这不仅仅能给你在命令行里使用带来方便,Django 自动生成的 admin 里也使用这个方法来表示对象。

    让我们再为此模型添加一个自定义方法:

    polls/models.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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)
    image-20230511185059293
    • 新加入的 import datetimefrom django.utils import timezone

      分别导入了 Python 的标准 datetime 模块和 Django 中和时区相关的 django.utils.timezone 工具模块。

      如果你不太熟悉 Python 中的时区处理,看看 时区支持文档 吧。

  • 再次操作Shell

    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    >>> from polls.models import Choice, Question

    # Make sure our __str__() addition worked.
    >>> Question.objects.all()
    <QuerySet [<Question: What's up?>]>

    # Django provides a rich database lookup API that's entirely driven by
    # keyword arguments.
    >>> Question.objects.filter(id=1)
    <QuerySet [<Question: What's up?>]>
    >>> Question.objects.filter(question_text__startswith="What")
    <QuerySet [<Question: What's up?>]>

    # Get the question that was published this year.
    >>> from django.utils import timezone
    >>> current_year = timezone.now().year
    >>> Question.objects.get(pub_date__year=current_year)
    <Question: What's up?>

    # Request an ID that doesn't exist, this will raise an exception.
    >>> Question.objects.get(id=2)
    Traceback (most recent call last):
    ...
    DoesNotExist: Question matching query does not exist.

    # Lookup by a primary key is the most common case, so Django provides a
    # shortcut for primary-key exact lookups.
    # The following is identical to Question.objects.get(id=1).
    >>> Question.objects.get(pk=1)
    <Question: What's up?>

    # Make sure our custom method worked.
    >>> q = Question.objects.get(pk=1)
    >>> q.was_published_recently()
    True

    # Give the Question a couple of Choices. The create call constructs a new
    # Choice object, does the INSERT statement, adds the choice to the set
    # of available choices and returns the new Choice object. Django creates
    # a set to hold the "other side" of a ForeignKey relation
    # (e.g. a question's choice) which can be accessed via the API.
    >>> q = Question.objects.get(pk=1)

    # Display any choices from the related object set -- none so far.
    >>> q.choice_set.all()
    <QuerySet []>

    # Create three choices.
    >>> q.choice_set.create(choice_text="Not much", votes=0)
    <Choice: Not much>
    >>> q.choice_set.create(choice_text="The sky", votes=0)
    <Choice: The sky>
    >>> c = q.choice_set.create(choice_text="Just hacking again", votes=0)

    # Choice objects have API access to their related Question objects.
    >>> c.question
    <Question: What's up?>

    # And vice versa: Question objects get access to Choice objects.
    >>> q.choice_set.all()
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
    >>> q.choice_set.count()
    3

    # The API automatically follows relationships as far as you need.
    # Use double underscores(双下划线) to separate relationships.
    # This works as many levels deep as you want; there's no limit.
    # Find all Choices for any question whose pub_date is in this year
    # (reusing the 'current_year' variable we created above).
    >>> Choice.objects.filter(question__pub_date__year=current_year)
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

    # Let's delete one of the choices. Use delete() for that.
    >>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
    >>> c.delete()

2.5 介绍 Django 管理页面

设计哲学

为你的员工或客户生成一个用户添加,修改和删除内容的后台是一项缺乏创造性和乏味的工作。因此,Django 全自动地根据模型创建后台界面。

Django 产生于一个公众页面和内容发布者页面完全分离的新闻类站点的开发过程中。站点管理人员使用管理系统来添加新闻、事件和体育时讯等,这些添加的内容被显示在公众页面上。Django 通过为站点管理人员创建统一的内容编辑界面解决了这个问题。

管理界面不是为了网站的访问者,而是为管理者准备的。

创建一个管理员账号

  • 首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:

    1
    ...\> py manage.py createsuperuser

    键入你想要使用的用户名,然后按下回车键:

    1
    Username: admin

    然后提示你输入想要使用的邮件地址:

    1
    Email address: admin@example.com

    最后一步是输入密码。你会被要求输入两次密码,第二次的目的是为了确认第一次输入的确实是你想要的密码。

    1
    2
    3
    Password: **********
    Password (again): *********
    Superuser created successfully.

    我的密码使用是:admin0314

    image-20230511192431718

启动开发服务器

  • Django 的管理界面默认就是启用的。让我们启动开发服务器,看看它到底是什么样的。

    如果开发服务器未启动,用以下命令启动它:

    1
    ...\> py manage.py runserver
  • 现在,打开浏览器,转到你本地域名的 “/admin/” 目录,

    比如 http://127.0.0.1:8000/admin/ 。你应该会看见管理员登录界面:

    image-20230511192719714

    因为 翻译 功能默认是开启的,如果你设置了 LANGUAGE_CODE,登录界面将显示你设置的语言(如果 Django 有相应的翻译)。

进入管理站点页面

  • 现在,试着使用你在上一步中创建的超级用户来登录。然后你将会看到 Django 管理页面的索引页:

    image-20230511192844441

    你将会看到几种可编辑的内容:组和用户。

    它们是由 django.contrib.auth提供的,这是 Django 开发的认证框架。

向管理页面中加入投票应用

但是我们的投票应用在哪呢?它没在索引页面里显示。

只需要再做一件事:

我们得告诉管理,问题 Question 对象需要一个后台接口。

  • 打开 polls/admin.py 文件,把它编辑成下面这样

    polls/admin.py

    1
    2
    3
    4
    5
    from django.contrib import admin

    from .models import Question

    admin.site.register(Question)

体验便捷的管理功能

  • 现在我们向管理页面注册了问题 Question 类。

    Django 知道它应该被显示在索引页里:

    image-20230511193227553
  • 点击 “Questions” 。

    现在看到是问题 “Questions” 对象的列表 “change list” 。

    这个界面会显示所有数据库里的问题 Question 对象,你可以选择一个来修改。

    这里现在有我们在上一部分中创建的 “What’s up?” 问题。

    image-20230511193405499
  • 点击 “What’s up?” 来编辑这个问题(Question)对象:

    image-20230511193459916

    注意事项:

    • 这个表单是从问题 Question 模型中自动生成的
    • 不同的字段类型(日期时间字段 DateTimeField 、字符字段 CharField)会生成对应的 HTML 输入控件。每个类型的字段都知道它们该如何在管理页面里显示自己。
    • 每个日期时间字段 DateTimeField都有 JavaScript 写的快捷按钮。日期有转到今天(Today)的快捷按钮和一个弹出式日历界面。时间有设为现在(Now)的快捷按钮和一个列出常用时间的方便的弹出式列表。

    页面的底部提供了几个选项:

    • 保存(Save) - 保存改变,然后返回对象列表。
    • 保存并继续编辑(Save and continue editing) - 保存改变,然后重新载入当前对象的修改界面。
    • 保存并新增(Save and add another) - 保存改变,然后添加一个新的空对象并载入修改界面。
    • 删除(Delete) - 显示一个确认删除页面。

    如果显示的 “发布日期(Date Published)” 和你在 教程 1 里创建它们的时间不一致,这意味着你可能没有正确的设置 TIME_ZONE。改变设置,然后重新载入页面看看是否显示了正确的值。

  • 编辑一下

    通过点击 “今天(Today)” 和 “现在(Now)” 按钮改变 “发布日期(Date Published)”。

    然后点击 “保存并继续编辑(Save and add another)”按钮。

    然后点击右上角的 “历史(History)”按钮。

    你会看到一个列出了所有通过 Django 管理页面对当前对象进行的改变的页面,其中列出了时间戳和进行修改操作的用户名:

    image-20230511193953423
  • 当你熟悉了数据库 API 之后,你就可以开始阅读 教程第 3 部分,

    下一部分我们将会学习如何为投票应用添加更多视图。