Django 支持许多不同的数据库服务器,官方支持 PostgreSQL、MariaDB、MySQL、Oracle 和 SQLite。
打开 mysite/settings.py 。这是个包含了 Django 项目设置的 Python 模块。默认情况下,这个配置文件使用 SQLite 作为默认数据库。如果不熟悉数据库,或者只是想尝试下 Django,可以选择使用 SQLite。Python 内置 SQLite,无需安装额外安装数据库。当开始开发一个真正的项目时,还是推荐使用更具扩展性的数据库,例如Mysql,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,那么不需要在使用前做任何事——数据库会在需要的时候自动创建。
本文中将以默认的 SQLite 数据库为例进行讲解。
INSTALLED_APPS
在 mysite/settings.py中,可以看到 INSTALLED_APPS 设置项。这里包括了会在新项目中默认启用的所有 Django 应用 App。当然新创建的应用,也需要注册到 INSTALLED_APPS之下。
通常, INSTALLED_APPS 默认包括了以下 Django 的自带应用:
- django.contrib.admin -- 管理员站点, 你很快就会使用它。
- django.contrib.auth -- 认证授权系统。
- django.contrib.contenttypes -- 内容类型框架。
- django.contrib.sessions -- 会话框架。
- django.contrib.messages -- 消息框架。
- django.contrib.staticfiles -- 管理静态文件的框架。
这些应用被默认启用是为了给常规项目提供方便。默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。
创建默认应用使用的数据库表
执行以下命令(以后,默认情况下都在在VSCode的 Terminal Panel中输入这些命令):
python manage.py migrate
这个 migrate 命令查看 INSTALLED_APPS 配置,并根据 mysite/settings.py 文件中的数据库配置和随应用提供的数据库迁移文件,创建任何必要的数据库表。
从执行日志中可以看到,有关Auth,admin,sessions的表被创建了。
创建模型 Model
在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据。
在 polls 应用中,需要创建两个模型:问题 Question 和选项 Choice。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 。这将告诉 Django 每个字段要处理的数据类型。
每个 Field 类实例变量的名字(例如 question_text 或 pub_date )也是字段名,将会在 Python 代码里使用它们,而数据库会将它们作为列名。
可以使用可选的选项来为 Field 定义一个人类可读的名字。这个功能在很多 Django 内部组成部分中都被使用了,而且作为文档的一部分。如果某个字段没有提供此名称,Django 将会使用对机器友好的名称,也就是变量名。
定义某些 Field 类实例需要参数。例如 CharField 需要一个 max_length 参数。这个参数的用处不止于用来定义数据库结构,也用于验证数据。
Field 也能够接收多个可选参数;例如将 votes 的 default 也就是默认值,设为0。
注意:使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一。
让 Django 知道模型有一些变更
运行下面的命令:
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 里。
再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:
python manage.py migrate
#输出内容
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
这个 migrate 命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上, 也就是将对模型的更改同步到数据库结构上。
迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。改变模型需要这三步:
- 编辑 models.py 文件,改变模型。
- 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
- 运行 python manage.py migrate 来应用数据库迁移。
数据库 API
可以尝试一下 Django 创建的各种数据库 API来操作数据库。
在VSCode 中,显示 Terminal Panel中,按下图所示,打开 powershell panel。
输入以下命令打开 Python 命令行:
python manage.py shell
然后可以输入一些语句操作数据库。
>>> from polls.models import Choice, Question # 导入模型定义的类
# 查询 Question 表,还没有任何数据
>>> Question.objects.all()
# 插入一条数据
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# 保存数据到数据库
>>> q.save()
# 访问数据
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)
#修改数据并保持
>>> q.question_text = "What's up?"
>>> q.save()
# 查看数据表中数据
>>> Question.objects.all()
<QuerySet []>
>>> Question.objects.all()[0].question_text
"What's new?"
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 工具模块。
保存文件。最终 polls/models.py 文件内容如下:
from django.db import models
class Question(models.Model):
def __str__(self):
return self.question_text
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
def __str__(self):
return self.choice_text
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
运行 python manage.py shell 命令再次打开 Python 交互式命令行:
>>> from polls.models import Choice, Question
# 要添加 __str__() 函数
>>> Question.objects.all()
<QuerySet []>
# filter 数据过滤
>>> Question.objects.filter(id=1)
<QuerySet []>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet []>
# 找出当年提出的问题
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
#Id不存在,会出现错误
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
# 外键
>>> Question.objects.get(pk=1)
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# 显示 choice_set 数据
>>> q.choice_set.all()
# 添加3条 choice_set 数据
>>> q.choice_set.create(choice_text='Not much', votes=0)
>>> q.choice_set.create(choice_text='The sky', votes=0)
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Question objects.
>>> c.question
>>> q.choice_set.all()
<QuerySet [, , ]>
>>> q.choice_set.count()
3
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [, , ]>
# 删除数据
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()