本文链接:https://www.jianshu.com/p/da1f51a166bd
作者:西瓜甜
Web框架Django2.2.12-ORM基础操作
一、 基本介绍
关系对象映射(Object Relational Mapping,简称ORM)
是 Django 框架用于和数据库沟通的一种方式。
通过创建一个类来和数据库中的表进行对应,之后生成一个中间文件(移植文件),底层再参照这个中间文件操作数据库,创建一个表。
二、 ORM 是如何和数据库映射的
Django 规定在 models.py 文件内 创建一个类,这个类可以称为模型类,然后进行初始化、迁移后,Django就会根据这个类在数据库中创建相应的表以及表的字段内容。
其对应关系如下:
类名 ------> 数据库中的表名
类的数据属性 ------> 表字段
每一个实例化的对象 ------> 表中的每一条数据
shark如果参照上图就可以创建下面这样一个类
from django.db import models
class PriceHistory(models.Model):
data = models.DateTimeField()
price = models.DecimalField()
volume = models.PositiveIntegerField()
所有的模型类都应该直接或者间接的继承 django.db.models.Model
这个类。
models.DecimalField
models.DecimalField
models.PositiveIntegerField
这些都是字段在数据库中对应的属性,后面会详细介绍。
表格的主键 id 字段,Django 会自动添加,这里不用明确设置。
到此,这就是一个类到数据库中具体的表的具体实现。接下来就用一个实际的实例来实际的操作一下。
假设场景:
现在公司希望把服务的基础信息保存到数据库中。
之后用浏览器页面展示出来,这样的话,在任何地方,只要有网络有权限访问此网站的这个页面,就都可以看到这些信息。比如公司的财务需要核实资产,对账、出报表等,公司的仓库需要核实现有资产的利用率等,技术人员需要了解目前服务器的状态等。
根据之前学到的知识和写过的获取服务器信息的程序,可以得到下面的数据。
服务器基础信息数据
{
"base": {
"host_name": "iZ2zecj761el8gvy7p9y2kZ",
"kernel": "3.10.0-957.21.3.el7.x86_64",
"os": "CentOS Linux release 7.6.1810 (Core) ",
"manufacturer": "Alibaba Cloud",
"pod_name": "Alibaba Cloud ECS",
"sn": "0f7e3d86-7742-4612-9f93-e3a9e4754157"
},
"cpu": {
"cpu_name": "Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz",
"cpu_num": 1,
"cpu_cores_each": 1
},
"mem": [
{
"capacity": "4096 MB",
"slot": "DIMM 0",
"model": "RAM",
"speed": "Unknown",
"manufacturer": "Alibaba Cloud",
"sn": "Not Specified"
}
]
}
服务器的 CPU 一般不会改动,所以可以把 CPU 信息和 base (基础)信息放在一个表中,这个表就叫 server
表吧。
一台服务器应该会有多根内存,并且以后有可能增加,因此把内存信息单独放在一个单独的表中,这个表就叫他 memory
表吧。但是,这个内存表中的数据应该属于那个服务器呢?
是不是应该和之前的 server
表进行对应,这就是一个多对一的关系了。
就是内存表中的某些多条数据应该对应server
表中其中的一条数据。
这里先来实现 server
表。
合并好的服务器基础信息数据
base_info = {
"host_name": "iZ2zecj761el8gvy7p9y2kZ",
"kernel": "3.10.0-957.21.3.el7.x86_64",
"os": "CentOS Linux release 7.6.1810 (Core) ",
"manufacturer": "Alibaba Cloud",
"pod_name": "Alibaba Cloud ECS",
"sn": "0f7e3d86-7742-4612-9f93-e3a9e4754157"
"cpu_name": "Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz",
"cpu_num": 1,
"cpu_cores_each": 1
}
server 表是数据字典
字段 | 字段属性 | 说明信息 |
---|---|---|
id | 主键,自增列,Django 会自动创建 | |
host_name | varchar(128) | 主机名 变长字符串 |
kernel | varchar(128) | 内核版本 变长字符串 |
os | varchar(64) | 操作系统 变长字符串 |
manufacturer | varchar(64) | 服务器厂商 变长字符串 |
pod_name | varchar(64) | 服务器型号 变长字符串 |
sn | varchar(128) | 服务器的 SN 号 变长字符串 |
cpu_name | varchar(32) | CPU型号 变长字符串 |
cpu_num | int | 物理 CPU 颗数 整型 |
cpu_cores_each | int | 每颗物理 CPU 的核心数 整型 |
cmdb/models.py
根据以上信息可以在 cmdb/models.py
文件中创建一个模型类。
注意:服务器应该有不同的类型,比如机架式,刀片式等,因此这里增加了一个字段,用于表示某台服务器是属于那种类型。
class Server(models.Model):
server_type_choices = (
#(数据库中存的值, 前端展示出的内容)
(1, "机架式"),
(2, "刀片式"),
(3, "塔式"),
)
server_type = models.PositiveSmallIntegerField(
verbose_name="服务器类型",
choices=server_type_choices, # 指定从哪里选择类型
default=1 # 设置一个默认值
)
hostname = models.CharField(
verbose_name="主机名",
max_length=128, unique=True,
db_index=True # 表示此字段将会是一个索引字段
)
kernel = models.CharField(
verbose_name="内核", max_length=128)
sn = models.CharField(
verbose_name="SN号", max_length=64)
os = models.CharField(
verbose_name="操作系统", max_length=64)
manufacturer = models.CharField(
verbose_name="厂商", max_length=64,)
pod_name = models.CharField(
verbose_name="产品型号", max_length=32,)
manage_ip = models.GenericIPAddressField(
verbose_name="管理IP",
null=True, # 数据库中可以存空值 Null,Django 中默认都不允许存空
blank=True # Form 表单验证时此字典的值可以不提供
)
cpu_name = models.CharField(
verbose_name="CPU 型号", max_length=64)
cpu_num = models.IntegerField(
verbose_name="CPU 颗数")
cpu_cores_each = models.IntegerField(
verbose_name="每颗 CPU 核心数")
latest_date = models.DateTimeField(
verbose_name="更新时间",
auto_now=True, null=True)
create_at = models.DateTimeField(
verbose_name="创建时间",
auto_now_add=True)
class Meta:
# 后台管理页面上显示的名称,后面会讲到
verbose_name = "服务器表"
verbose_name_plural = verbose_name
db_table = "server" # 数据库中的实际表名
- 所有的Django模型类都必须继承 django.db.models.Model 类。它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
- 每个模型相当于单个数据库表(多对多关系例外,多对多关系会多生成一张关系表),每个模型类的数据属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。
- 通过其中类属性定义模型字段,模型字段必须是某种 models.XXField 类型,需要指明长度的类型,最大长度也是需要指明的。
- 通过模型类中的 Meta 子类定义模型元数据,比如数据库的表名、数据默认排序方式等。
模型类创建完毕后,接下来就可以准备和数据库进行连接操作了。
三、 安装依赖包
要想通过 ROM 操作数据库,ORM 底层还是需要用的 Python 连接数据库的包。
接下来首先来看看需要连接那种数据库。
1 使用 Django 自带的数据库 Sqlite
Django2.2 后使用需要使用 SQLite3.8或更新的版本,Cenos7中自带的版本是3.7。所以需要更新。
1.1 下载
https://www.sqlite.org/download.html
image.pngwget https://www.sqlite.org/2020/sqlite-amalgamation-3310100.zip
[root@qfedu.com ~]# wget https://www.sqlite.org/2020/sqlite-autoconf-3310100.tar.gz
1.2 安装
[root@qfedu.com sqlite-autoconf-3310100]# ./configure
[root@qfedu.com sqlite-autoconf-3310100]# make && make install
1.3 检查版本
[root@qfedu.com sqlite-autoconf-3310100]# sqlite3 --version
3.31.1 2020-01-27 19:55:54 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837bb4d6
1.4 确认项目中的配置
数据库的配置信息在 project_name/settings.py 文件中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
1.5 生成迁移文件
# 进入项目的主目录中执行如下命令
[root@qfedu.com qfcmdb]# python3 manage.py makemigrations
此命令执行成功后,会在编辑了 models.py
文件的应用中的 migrations
目录下创建 0001_initial.py
文件;这个文件是数据库生成的中间文文件,就是迁移文件。
1.6 利用迁移文件在数据库中创建表
# 继续执行如下命令来创建表
[root@qfedu.com qfcmdb]# python3 manage.py migrate
2 连接到 MySQL
Django2.2 后必须使用 mysqlclient,截至2020.05.06 不能再使用 pymysql 去兼容 mysqlclient 了。官方也推荐使用 mysqlclient。
mysqlclient
C 编写,接口精炼,自身运行速度快。缺点是安装困难,主要是依赖性强,比如会依赖一些 C 的头文件或者库文件。
建议安装在类 Unix 机器上,比如 Centos7、Mac 等。
在 shell 终端中执行如下命令安装,假如出现依赖,按照提示解决依赖。
[root@qfedu.com qfcmdb]# pip3 install mysqlclient
一般会依赖
四、配置数据库
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'cmdb', # 数据库名称,必须先在数据库中创建
'USER': 'root', # 数据库用户名
'PASSWORD': 'QFedu123!', # 数据库密码
'HOST': '', # 数据库主机名或IP,留空默认为localhost
'PORT': 3306, # 数据库端口, 默认为3306
}
}
五、 同步数据库
Django 根据代码中定义的类来自动生成数据库表。
1. 生成数据移植文件 (makemigrations)
# 在 shell 环境下的项目根目录下执行
[root@qfedu.com qfcmdb]# python3 manage.py makemigrations
此命令执行成功后,会在应用 server
的 migrations 目录下创建 0001_initial.py
文件;这个文件是数据库生成的中间文件,通过它可以指定当前的数据库版本。
在 makemigrations 的过程中,Django 会对比 models.py 中的模型与已有数据库之间的差异,如果没有差异则不做任何操作。
假如对 models.py 有任何改变,则在执行 makemigrations 的时候会同步更新到数据移植文件中。
2. 真正改变数据库
# 在 shell 环境下的项目根目录下执行
[root@qfedu.com qfcmdb]# python3 manage.py migrate
六、 常用字段 Field
下面是列出来部分常用的 Field。
更多请查阅官方文档:
Model field reference 模型字段指南
1 字符串类型相关
1.1 models.CharField(max_length=None)
字符串类型
varchar
, 必须提供max_length参数, max_length表示字符长度
1.2 mdels.TextField(*options)
字符串类型
text
一个大的文本字段。
1.3 models.EmailField(max_length = 254, **options)
字符串类型
varchar
,检查该值是使用一个有效的电子邮件地址
1.4 models.GenericIPAddressField(protocol='both', **options)
IPv4或IPv6地址
protoclol 指定了地址的类型:
both
IPv4 或 IPv6IPv4
只可以是 IPv4IPv6
只可以是 IPv6
匹配不区分大小写
1.5 models.SlugField(max_length=50, **options)
字母、数字、下划线、连接符(减号)的任意组合
1.6 models.URLField(max_length=200, **options)
可以验证 URL 地址
1.7 UUIDField(Field)
字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
2 数字类型
2.1 BooleanField(**options)
布尔值类型
2.2 SmallIntegerField()
小整数 -32768 ~ 32767
2.3 IntegerField(Field)
整数列(有符号的) -2147483648 ~ 2147483647
2.4 PositiveSmallIntegerField()
正小整数 0 ~ 32767
2.5 PositiveIntegerField()
正整数 0 ~ 2147483647
2.6 FloatField(Field)
浮点型
2.7 DecimalField((max_digits=None, decimal_places=None, **options))
一个固定精度的十进制数,由Python
Decimal
实例表示。
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
2.8 BinaryField(Field)
二进制类型
3 关于时间和日期
3.1 DateField(auto_now=False, auto_now_add=False, **options)
日期格式 YYYY-MM-DD
auto_now=True
每次保存时,更新此字段的值为当前时间,对“最后修改”的时间戳有用,只有调用 Model.save()时,此值才会自动更新。在以其他方式更新其他字段时,不会导致此字段自动更新。比如:Queryset.update() 不会自动更新。不允许在后台修改。
auto_now_add=True
首次创建对象时自动将字段设置为当前时间。可以用于创建时间戳。 并且不支持在后台修改。
default=date.today
支持后台修改
auto_now auto_now_add default 这三个是互斥的。
3.2 DateTimeField(auto_now=False, auto_now_add=False, **options))
日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
fromdjango.utils from timezone
default=timezone.now
3.3 TimeField(auto_now=False, auto_now_add=False, **options)
时间格式 HH:MM[:ss[.uuuuuu]]
4 字段里可以使用的选项(options)
null 如果设置为 True, 当该字段为空时,
Django 会将数据库中该字段设置为 NULL,默认为 False。
避免在基于字符串的字段(例如 CharField和 TextField上使用 null。
如果字符串字段的 null=True,那意味着对于“无数据”有两个可能的
值:NULL 和空字符串。
一个例外是当 CharField 同时具有 unique=True 和 blank=True 时,
需要设置为 null=True, 以便在使用空字符串保存多个对象时,
避免唯一的约束违规。
blank 如果设置为 True ,该字段允许为空。默认为 False 。
注意,这与null不同。null纯粹与数据库相关,而blank则与验证
相关。如果字段的blank=True,则表单验证将允许输入空值。
如果字段为空=假,则该字段是必需的。
db_column 数据库中字段的列名
db_tablespace 数据库中表空间
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 为数据库中字段建立索引
unique 数据库中字段是否可以建立唯一索引
verbose_name Admin中显示的字段名称
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices 一个可迭代的结构(比如,列表或是元组),用来给
这个字段提供选择项
如:
qf = models.IntegerField(
choices=[(0, '服务器'),
(1, '交换机'),],
default=0)
5 Mata 元数据
Meta 类的属性名由 Django 预先定义,常用的 Meta 类属性汇总如下:
# 映射的数据库的表名,假如不指定,则 Django 会自动创建,命名格式为:“app的名称的小写_模型类名的小写”
db_table
# 后台管理显示的表名称
verbose_name
# 后台管理显示的复数名称,默认表名后面会加字母 s
verbose_name_plural
# 联合索引
index_together = [
("host_name", "manage_ip"),
]
# 联合唯一,注意最后的英文逗号
unique_together = (("host_name", "manage_ip"),)
七、ORM 基本操作(重要)
1. 创建数据
1.1 方式一:实例化模型类:
安装工具 Ipython:
为了操作方便,我们可以在当前项目的虚拟 Python 环境中安装 Ipython 工具。
切换到项目的虚拟环境后,使用 pip3 安装 Ipython
[root@qfedu.com ~]# pip3 install ipython
安装成功后,进入到项目主目录下,运行如下命令可以自动加载 Django 的环境,加载 Django 环境后就可以导入模型了。
[root@qfedu.com ~]# cd qfcmdb
[root@qfedu.com qfcmdb]# python3 manage.py shell
[root@qfedu.com qfcmdb]#
from cmdb.models import Server
# 创建实例
server = Server()
# 给属性赋值,这里并没有改变数据库的表数据,进阶体现在内存中
server.host_name = "bj-dev-db-server-01"
server.kernel = "3.10.0-957.21.3.el7.x86_64"
server.manufacturer = "Alibaba Cloud"
server.pod_name = "Alibaba Cloud ECS"
server.sn = "0f7e3d86-7742-4612-8e81-f3a9e4754167"
server.cpu_name = "Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz"
server.cpu_num = 1
server.cpu_cores_each = 1
# 保存数据到表里,相当于 commit
server.save()
1.2 方式二:利用管理器的 Create 方法
Manager(管理器) objects
Manager
(管理器)是一种接口,它赋予了 Django 通过模型类操作数据库的能力。Django 应用中每个模型类拥有至少一个 Manager
,默认名称是 objects
。
Managers
只能通过模型类访问,而不是通过模型实例,目的是强制分离 “表级” 操作和 “行级” 操作。
如果你想只用一条语句创建并保存一个对象,使用模型类的 create()
方法。
server_info = {
"host_name": "iZ2zecj761el8gvy7p9y2kZ",
"kernel": "3.10.0-957.21.3.el7.x86_64",
"os": "CentOS Linux release 7.6.1810 (Core) ",
"manufacturer": "Alibaba Cloud",
"pod_name": "Alibaba Cloud ECS",
"sn": "0f7e3d86-7742-4612-9f93-e3a9e4754157",
"cpu_name": "Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz",
"cpu_num": 1,
"cpu_cores_each": 1
}
Server.objects.create(**server_info)
3 方式三: 一次创建多条数据:
# 创建一个字典,包含了一台服务器的信息
server1 = {
'host_name': 'bj-prod-dbserver-01',
'kernel': '3.10.0-957.21.3.el7.x86_64',
'sn': '0f7e3d86-7742-4612-9f93-e3a9e4754177',
'os': 'CentOS Linux release 7.6.1810 (Core) ',
'manufacturer': 'Alibaba Cloud',
'pod_name': 'Alibaba Cloud ECS',
'manage_ip': "192.168.1.100",
'cpu_name': 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz',
'cpu_num': 2,
'cpu_cores_each': 2
}
# 再创建一个字典,包含了另一台服务器的信息
server2 = {
'host_name': 'bj-prod-dbserver-02',
'kernel': '3.10.0-957.21.3.el7.x86_64',
'sn': '0f7e3d86-7742-4612-8e81-f3a9e4754187',
'os': 'CentOS Linux release 7.6.1810 (Core) ',
'manufacturer': 'Alibaba Cloud',
'pod_name': 'Alibaba Cloud ECS',
'manage_ip': "192.168.1.200",
'cpu_name': 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz',
'cpu_num': 2,
'cpu_cores_each': 2,
}
# 添加这两台服务器的信息到数据库中, bulk_create 接收的是一个含有模型类实例的列表
Server.objects.bulk_create([ Server(**server1), Server(**server2)])
2 QuerySet 查询数据
要从数据库中查询数据对象,需要通过模型类的管理器构建一个 QuerySet。
一个 QuerySet 代表来自数据库中对象的一个集合。
通过filters(过滤)可以根据给定参数缩小查询结果量,比如它可以有 0 个,1 个或者多个。
在 SQL 的层面上, QuerySet 对应 SELECT 语句,而filters对应类似 WHERE 或 LIMIT 的限制子句。
你能通过模型的管理器获取 QuerySet。
就像之前所说的,每个模型至少有一个管理器,默认名称是 objects
。
像下面这样直接通过模型类使用它:
In [15]: from cmdb.models import Server
In [16]: Server.objects
Out[16]: <django.db.models.manager.Manager at 0x10f2a6350>
再次强调,管理器(objects)只能通过模型类访问,而不是通过模型实例,目的是强制分离 “表级” 操作和 “行级” 操作。
如下操作是错误的
In [18]: server.objects
-----------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-18-e6fff3f82d21> in <module>
...略...
AttributeError: Manager isn't accessible via Server instances
3 基本查询 API
3.1 查询表中的所有数据
Server.objects.all()
In [19]: Server.objects.all()
Out[19]: <QuerySet [<Server: bj-dev-db-server-01>, <Server: iZ2zecj761el8gvy7p9y2kZ>, <Server: bj-prod-dbserver-01>, <Server: bj-prod-dbserver-02>]>
Server.objects.all()
相当于执行的 SQL 语句是: select 具体的所有字段 from 表名;
获取到的是 QuerySet,其内部的元素是一个一个的数据对象,就是模型类的实例;QuerySet 是可以被循环的,也支持索引取值和切片操作。
要查看具体对应的 SQL 语句,可以像下面这样获取到
In [31]: str(Server.objects.all().query)
Out[31]: 'SELECT "server"."id", "server"."server_type", "server"."host_name", "server"."kernel", "server"."sn", "server"."os", "server"."manufacturer", "server"."pod_name", "server"."manage_ip", "server"."cpu_name", "server"."cpu_num", "server"."cpu_cores_each", "server"."latest_date", "server"."create_at" FROM "server"'
循环:
In [50]: queryset = Server.objects.all()
In [51]: for server in queryset:
...: print(server.host_name)
...:
bj-dev-db-server-01
iZ2zecj761el8gvy7p9y2kZ
bj-prod-dbserver-01
bj-prod-dbserver-02
取出的数据对象,可以使用 .属性
的方式获取对应的值,也就是数据中一个字段的值。
索引获取到一个数据对象
In [52]: server = queryset[0]
In [53]: server.host_name
Out[53]: 'bj-dev-db-server-01'
切片 Limit
注意:切片后 获取到的结果仍然是 QuerySet
In [54]: subsqst = queryset[1:]
In [55]: subsqst
Out[55]:
[<Server: iZ2zecj761el8gvy7p9y2kZ>,
<Server: bj-prod-dbserver-01>,
<Server: bj-prod-dbserver-02>]
3.2 过滤查询
Server.objects.filter(cpu_num=1)
In [56]: Server.objects.filter(cpu_num=1)
Out[56]: <QuerySet [<Server: bj-dev-db-server-01>, <Server: iZ2zecj761el8gvy7p9y2kZ>]>
3.3 过滤的时候添加多个条件
通过在多个过滤条件之间添加英文的逗号:,
可以实现多个条件查询,相当于 and
In [57]: Server.objects.filter(cpu_num=1,
host_name="iZ2zecj761el8gvy7p9y2kZ")
Out[57]: <QuerySet [<Server: iZ2zecj761el8gvy7p9y2kZ>]>
3.4 获取包含字段名的数据集(包含的数据结果是字典)
如何获取包含某一个或者某几个字段的数据集?就像下面原生查询语句一样:
select id, host_name from server;
可以使用 objects.values("字段1", "字段2")
Server.objects.values("id", "host_name")
In [58]: Server.objects.values("id", "host_name")
Out[58]: <QuerySet [{'id': 1, 'host_name': 'bj-dev-db-server-01'}, {'id': 3, 'host_name': 'bj-prod-dbserver-01'}, {'id': 4, 'host_name': 'bj-prod-dbserver-02'}, {'id': 2, 'host_name': 'iZ2zecj761el8gvy7p9y2kZ'}]>
这样获取到的是表内所有的数据集。
.values
也可以使用在 filter
过滤中
指定字段
Server.objects.filter(cpu_num=1).values("id", "host_name", "cpu_num")
In [59]: Server.objects.filter(cpu_num=1).values(
"id", "host_name", "cpu_num")
Out[59]: <QuerySet [{'id': 1, 'host_name': 'bj-dev-db-server-01', 'cpu_num': 1}, {'id': 2, 'host_name': 'iZ2zecj761el8gvy7p9y2kZ', 'cpu_num': 1}]>
全部字段
Server.objects.filter(id=2).values()
In [69]: Server.objects.filter(id=2).values()
Out[69]: <QuerySet [{'id': 2, 'server_type': 1, 'host_name': 'iZ2zecj761el8gvy7p9y2kZ', 'kernel': '3.10.0-957.21.3.el7.x86_64', 'sn': '0f7e3d86-7742-4612-9f93-e3a9e4754157', 'os': 'CentOS Linux release 7.6.1810 (Core) ', 'manufacturer': 'Alibaba Cloud', 'pod_name': 'Alibaba Cloud ECS', 'manage_ip': None, 'cpu_name': 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz', 'cpu_num': 1, 'cpu_cores_each': 1, 'latest_date': datetime.datetime(2020, 5, 9, 14, 22, 6, 796154), 'create_at': datetime.datetime(2020, 5, 9, 14, 22, 6, 796236)}]>
3.5 只想要值,不要字段名称(包含的数据结构是元组)
所有数据且所有的值
Server.objects.values_list()
In [71]: Server.objects.values_list()
Out[71]: <QuerySet [(1, 1, 'bj-dev-db-server-01', '3.10.0-957.21.3.el7.x86_64', '0f7e3d86-7742-4612-8e81-f3a9e4754167', '', 'Alibaba Cloud', 'Alibaba Cloud ECS', None, 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz', 1, 1, datetime.datetime(2020, 5, 9, 14, 21, 39, 926247), datetime.datetime(2020, 5, 9, 14, 21, 39, 926296)), (2, 1, 'iZ2zecj761el8gvy7p9y2kZ', '3.10.0-957.21.3.el7.x86_64', '0f7e3d86-7742-4612-9f93-e3a9e4754157', 'CentOS Linux release 7.6.1810 (Core) ', 'Alibaba Cloud', 'Alibaba Cloud ECS', None, 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz', 1, 1, datetime.datetime(2020, 5, 9, 14, 22, 6, 796154), datetime.datetime(2020, 5, 9, 14, 22, 6, 796236)), (3, 1, 'bj-prod-dbserver-01', '3.10.0-957.21.3.el7.x86_64', '0f7e3d86-7742-4612-9f93-e3a9e4754177', 'CentOS Linux release 7.6.1810 (Core) ', 'Alibaba Cloud', 'Alibaba Cloud ECS', '192.168.1.100', 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz', 2, 2, datetime.datetime(2020, 5, 9, 14, 24, 32, 760752), datetime.datetime(2020, 5, 9, 14, 24, 32, 760827)), (4, 1, 'bj-prod-dbserver-02', '3.10.0-957.21.3.el7.x86_64', '0f7e3d86-7742-4612-8e81-f3a9e4754187', 'CentOS Linux release 7.6.1810 (Core) ', 'Alibaba Cloud', 'Alibaba Cloud ECS', '192.168.1.200', 'Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz', 2, 2, datetime.datetime(2020, 5, 9, 14, 24, 32, 760925), datetime.datetime(2020, 5, 9, 14, 24, 32, 760952))]>
用在过滤之后的数据其指定字段的值
Server.objects.filter(id=2).values_list("id","host_name")
In [72]: Server.objects.filter(id=2).values_list(
...: "id","host_name")
Out[72]: <QuerySet [(2, 'iZ2zecj761el8gvy7p9y2kZ')]>
注意:
以上的查询到的结果集中,无论包含的是字典、元组还是数据对象,这结果集任然是 QuerySet。所以整个结果集中都是支持迭代、索引、切片操作的。
3.6 直接获取一条数据
获取到表中的第一条数据对象
In [73]: Server.objects.first()
Out[73]: <Server: bj-dev-db-server-01>
获取到表中的最后一条数据对象
In [74]: Server.objects.last()
Out[74]: <Server: bj-prod-dbserver-02>
获取指定条件的一个对象
有的情况下,我们希望获取到的是直接的数据,不是 QuerySet,此时我们可以使用管理器 objects
的 .get()
方法
In [75]: Server.objects.get(id=2)
Out[75]: <Server: iZ2zecj761el8gvy7p9y2kZ>
.get()
方法获取到的是一个数据对象,并且在获取的时候,需要保证传递给 .get()
方法的参数能查到的数据只能是一个,多于一个或者没有都会报错。
3.7 获取更简化的数据
有的情况,需要通过某个字段的值作为查询的条件,希望查询到的结果是此条数据的主键 ID 号。
In [76]: server = Server.objects.filter(host_name='bj-dev-db-server-01').values_list("id")
In [77]: server
Out[77]: <QuerySet [(1,)]>
In [78]: server[0]
Out[78]: (1,)
In [79]: server[0][0]
Out[79]: 1
注意最后结果集中是一个嵌套的结构,要想获取到里面的数据,需要使用嵌套的索引,显然有些麻烦。
可以给 .values_list()
传递 flat=True
,来提前去掉一层。
In [80]: server = Server.objects.filter(host_name='bj-dev-db-server-01'
).values_list("id", flat=True)
In [81]: server
Out[81]: <QuerySet [1]>
In [82]: server[0]
Out[82]: 1
3.8 统计数量
In [84]: Server.objects.filter(cpu_num=1)
Out[84]: <QuerySet [<Server: bj-dev-db-server-01>, <Server: iZ2zecj761el8gvy7p9y2kZ>]>
In [85]: Server.objects.filter(cpu_num=1).count()
Out[85]: 2
4 删除数据
删除数据使用的 api 是 .delete()
这个操作是直接提交生效的。
In [87]: Server.objects.filter(id=4)
Out[87]: <QuerySet [<Server: bj-prod-dbserver-02>]>
# 删除数据
In [88]: Server.objects.filter(id=4).delete()
Out[88]: (1, {'cmdb.Memory': 0, 'cmdb.Server': 1})
In [89]: Server.objects.filter(id=4)
Out[89]: <QuerySet []>
5 更新数据
更新数据的操作也是自动提交生效的,同样推荐使用 filter 进行过滤后再更新,否则更新全表。
.uptate()方法
In [90]: Server.objects.values("id", "cpu_num")
Out[90]: <QuerySet [{'id': 2, 'cpu_num': 1}]>
In [91]: Server.objects.filter(id=2).update(cpu_num=2)
Out[91]: 1
In [92]: Server.objects.filter(id=2).values("id", "cpu_num")
Out[92]: <QuerySet [{'id': 2, 'cpu_num': 2}]>
更新多个字段时,字段直接用英文逗号分开。
比如
.update(字段1=值1, 字段2=值2)
也支持接收字典,
.update(**some_dict)
对象属性赋值的方式
In [93]: server = Server.objects.filter(id=3).first()
In [94]: server
Out[94]: <Server: bj-prod-dbserver-01>
In [95]: server.cpu_num = 4
In [96]: server.cpu_cores_each = 2
In [97]: server.save()
In [98]: Server.objects.filter(id=3).values("id", "cpu_num", "cpu_cores_each")
Out[98]: <QuerySet [{'id': 3, 'cpu_num': 4, 'cpu_cores_each': 2}]>
6 ORM 高级操作
双下划线查询
from db.models import Server
### 大于,小于
# 获取 id 大于 1 的数据
Server.objects.filter(id__gt=1)
# 获取 id 大于等于 1 的数据
Server.objects.filter(id__gte=1)
# 获取 id 小于 2 的数据
Server.objects.filter(id__lt=2)
# 获取id大于1 且 小于3的值
Server.objects.filter(id__gt=1, id__lt=3)
### in
# 获取id等于2、3的数据
Server.objects.filter(id__in=[2, 3])
### not in
Server.objects.exclude(id__in=[1, 3])
### isnull
# 这会找到字段的值是 None 的数据,MySQL 中会是 null
Server.objects.filter(manage_ip__isnull=True)
### range
# 范围 bettwen and
Server.objects.filter(id__range=[1, 2])
# 其他类似
# startswith,istartswith, endswith, iendswith,
### like
# 不区分大小写
Server.objects.filter(os__icontains="Centos")
Server.objects.filter(manage_ip__icontains="192.168.1")
### order by
# asc
Server.objects.filter(
os__icontains="Centos").order_by('id')
# desc
Server.objects.filter(
os__icontains="Centos").order_by('-id')
### group by
from django.db.models import Count, Min, Max, Sum
# 分组统计设备状态的数量
Server.objects.values("server_type").annotate(c=Count('server_type'))
# 返回数据:
<QuerySet [{'server_type': 1, 'c': 3}]>
# 转换为 SQL
In [74]: str(Server.objects.values("server_type").annotate(c=Count('server_type')).query)
Out[74]: 'SELECT "server"."server_type", COUNT("server"."server_type") AS "c" FROM "server" GROUP BY "server"."server_type"'
网友评论