美文网首页
04Django2.2.12-ORM 基础

04Django2.2.12-ORM 基础

作者: 运维开发_西瓜甜 | 来源:发表于2020-05-04 13:25 被阅读0次

本文链接: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.png
wget 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文件;这个文件是数据库生成的中间文文件,就是迁移文件。

image.png

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 或 IPv6
  • IPv4 只可以是 IPv4
  • IPv6 只可以是 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"),)

更多参考官方 Model Meta options 指南

七、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"'

相关文章

  • 04Django2.2.12-ORM 基础

    本文链接:https://www.jianshu.com/p/da1f51a166bd作者:西瓜甜 Web框架Dj...

  • 机械设备安装技术

    设备基础种类及应用 垫层基础允许产生沉降:大型储罐 浅基础扩展基础联合基础:轧机独立基础 深基础桩基础:适用于需要...

  • 基础,基础,基础

    如果有人现在问我,JAVA该怎么学,我会告诉他不要急于求成,少看视频,多练,多思考。但说到这里有人可能会反...

  • 【Android】知识点汇总,坚持原创ing

    Android基础 Java基础 Java基础——Java内存模型和垃圾回收机制 语法基础 语法基础——C语法基础...

  • Java 基础

    Java 基础01Java开发入门 Java 基础02Java编程基础 Java 基础03面向对象 Java 基础...

  • 零基础学画画从入门到放弃

    零基础应该怎么学画画?零基础那就从基础开始学啊!基础是什么?造型基础和色彩基础。 造型基础就是用点线面组成起码能让...

  • 面试题汇总

    1.Java基础面试问题 Java基础之基础问题 Java基础之面向对象 Java基础之数据结构 Java基础之I...

  • 基础基础还是基础

    这次去面试,还是被基础给打趴下了。 对于PHP7的新特性没有了解。 对于TP的新特性没有了解。 再一个就是独立完成...

  • 零基础学UI设计需要美术基础吗?

    零基础学UI设计需要美术基础吗?零基础学UI设计需要美术基础吗?零基础学UI设计需要美术基础吗?零基础学UI设计需...

  • 基础基础!

    人生中第一个自主设计的实验方案终于得到认可^O^在设计方案过程中认识到基础知识以及细心的重要性,还有半个学期可以努...

网友评论

      本文标题:04Django2.2.12-ORM 基础

      本文链接:https://www.haomeiwen.com/subject/mvyywhtx.html