配置数据库

我们可以在mysite/settings.py中设置数据库。

通常会采用SQLite作为默认数据库,但是真正的项目中,会使用其他数据库,例如:MySQL、PostgreSQL。所以这时对不同数据库需要安装第三方的DB API驱动,例如mysqlclient、psycopg2等

使用其他数据库,也需要改变设置文件中的DATABSE 'default'中的一些键值:

  • ENGINE:可选值有:'django.db.backends.sqlite3''django.db.backends.postgresql''django.db.backends.mysql',或 'django.db.backends.oracle'
  • NAME:数据库名称,如果使用SQLite,数据库会是电脑上的一个文件夹,此时NAME回事一个完整的绝对路径
  • 如果使用的不是SQLite,则需要添加额外设置,例如下:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'python',
        'USER': 'python',
        'PASSWORD': 'password',
        'HOST': '0.0.0.0',
        'PORT': '3306',    
    }
}

除此之外,还要设置TIME_ZONE为自己的时区。

INSTAllED_APP设置项会包含项目中所有启用的Django应用,应用能在多个项目中使用,也可以打包发布应用,让别人使用

通常,INSTALLED_APP会包含如下自带应用:

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

python manage.py migrate

结果:

image-20220110115014141

创建模型

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

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

这些概念通过一个python类来描述,编辑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.db.models.Model的一个子类,每个模型有许多变量,他们都表示模型里的一个数据库字段。

每个字段都是Field类的实例,例如CharField表示字符字段,DateTimeField表示为日期事件字段。

每个Field变量的名字(例如question_text)也是字段名,所以要使用对机器友好的格式,他们会是数据的列名

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

注意在最后,我们使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一。

激活模型

创建模型的代码会给予Django很多信息,Django通过这些信息做以下事情:

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

在此之前,我们必须把polls应用安装到项目中

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

在配置类INSTALLED_APPS中添加设置以在项目中包含这个应用。

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

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

现在项目中会包含polls应用,接着运行以下命令:

python manage.py makemigrations polls

结果:

image-20220117102729419

通过运行这条命令,Django会检测对模型的修改,并把修改的部分存储作为一次迁移

迁移是Django对模型定义的变化的存储形式(它们其实也只是一些你磁盘上的文件)

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

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

python manage.py sqlmigrate polls 0001

结果:

image-20220117103219811

注意:

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

如果你感兴趣,你也可以试试运行 python manage.py check ;这个命令帮助你检查项目中的问题,并且在检查过程中不会对数据库进行任何操作。

Django有一个自动执行数据库迁移并同步管理数据库结构的命令:migrate,下面来运行这个命令:

python manage.py migrate

结果:

image-20220117103459051

这个 migrate命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上。

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

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

API

进入Python命令行,尝试用Django创建各种API,运行以下命令:

python manage.py shell

使用这条命令而非只用python是因为manage.py会设置DJANGO_SETTINGS_MODULE环境变量,可以让Django根据mysite/settings.py文件来设置Python包的导入路径。

成功执行该命令后,就可以尝试数据库API:

>>> from polls.models import Choice, Question  # 导入模块

>>> Question.objects.all()
<QuerySet []>

# 创建一个新的 Question.
# 默认设置文件启用了对时区的支持,对于pub_date来说是带有tzimfo的,所以应当使用timezone.now(),而非datetime.datetime.now()
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# 保存对象,必须使用save()
>>> q.save()

# 现在它会有一个id
>>> q.id
1

# 通过python属性访问模型的字段值
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# 改变字段值,然后保存
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() 显示数据库里所有的Question
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

image-20220307100307582

这里我们发现时区是UTC,所以我们需要到settings.py中修改时区

# TIME_ZONE = 'UTC'
# USE_TZ = Ture
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

除此之外,对于<QuerySet [<Question: Question object (1)>]>是无法看到这个对象细节,所以我们可以编辑Question模型的代码(polls/models.py)来解决这个问题。我们分别给Question和Choice增加__str__()方法

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 里也使用这个方法来表示对象。

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

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)

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

保存文件然后通过 python manage.py shell 命令再次打开 Python 交互式命令行:

>>> from polls.models import Choice, Question

>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django提供了一个完全由关键字参数驱动的数据库查找API。
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# 看看今年发表的question
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# 请求一个不存在的ID,这将引发一个异常。
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# 通过主键进行查找是最常见的情况,因此Django为精确查找主键提供了快捷方式。下面的代码与Question.objects.get(id=1)相同。
>>> Question.objects.get(pk=1)
<Question: What's up?>

# 确保我们的自定义方法正常工作。
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# 给这个question一些choice。create调用构造一个新的Choice对象,执行INSERT语句,将该选项添加到可用选项集,并返回新的Choice对象。
#(例如一个Question的choice)可以通过API访问。
>>> q = Question.objects.get(pk=1)

# 显示相关对象集中的任何选项——到目前为止没有
>>> q.choice_set.all()
<QuerySet []>

# 创建三个choice.
>>> 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对象具有访问其相关Question对象的API
>>> c.question
<Question: What's up?>

# 反之亦然:Question对象访问Choice对象。
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# API会根据您的需要自动地遵循关系
# 使用双下划线分隔关系。
# 这在许多层次上都是有效的;是没有限制的。
# 为pub_date在今年的任何question找到所有的choice
# (重用我们在上面创建的'current_year'变量).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# 我们删掉一个choice。为此使用delete()。
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

image-20220307102007402.png

image-20220307102203783

image-20220307102635277

Django 管理页面

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

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

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

创建一个管理员账号

通过以下命令创建一个能登录管理页面的用户:

python manage.py createsuperuser

image-20220307103418983

启动开发服务器

Django的管理界面默认是启用状态,下面我们来启动开发服务器

打开浏览器,转到本地域名的'/admin/'目录,例如http://127.0.0.1:8000/admin/ ,然后就会看到管理员界面

image-20220307103759277

如果开发服务器未启用,可以通过以下命令启用他:

python manage.py runserver

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

进入管理站点页面后,可以看到几种可以编辑的内容:组和用户。这些是由django.contrib.auth提供的,这是Django开发的认证框架。

image-20220307104152725

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

但是我们并没有看到投票应用,为此我们需要告诉管理,Question对象需要一个后台接口。

打开polls/admin.py文件,编辑如下:

from django.contrib import admin

from .models import Question

admin.site.register(Question)

现在管理页面中就注册了Question类。

image-20220307104434064

点击'Question',就会看到QUestion的列表,这个界面会显示所有数据库里的Question对象。

image-20220307104544446

注意事项:

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

通过点击 “今天(Today)” 和 “现在(Now)” 按钮改变 “发布日期(Date Published)”。然后点击 “保存并继续编辑(Save and add another)”按钮。然后点击右上角的 “历史(History)”按钮。你会看到一个列出了所有通过 Django 管理页面对当前对象进行的改变的页面,其中列出了时间戳和进行修改操作的用户名:

image-20220307104810035