# P08 QuerySet 和 Instance

## QuerySet

Django 的 ORM（Object-Relational Mapping，对象-关系映射）是一个非常强大的工具，它帮助你管理和查询数据库。ORM 能够让你使用Python（或其他编程语言）来操作数据库，就像你在操作Python对象一样。ORM用到三个类：Manager、QuerySet、Model。

从数据库中查询出来的结果一般是一个集合，这个集合叫做 QuerySet。

Django的ORM通过Model的objects属性提供各种数据操的接口（API），并通过 Model.objects 方法返回QuerySet，生产符合查询条件的列表，列表中包含多个 Instance。

QuerySet 的正式定义

```python
class QuerySet(model=None, query=None, using=None)[source]
```

### QuerySet 的两大特色

（1） 惰性：若这个对象中包含了需要的数据且需要使用时，它会去DB中获取数据，否则不会获取。 比如说在内部，创建、 过滤、切片和传递一个QuerySet，而没有进行真正的数据执行，不会真实操作数据库，被称为惰性。（2）缓存使用同一个查询集，第一次使用时会发生数据库的查询，然后Django会把结果缓存下来，再次使用这个查询集时会使用缓存的数据，减少了数据库的查询次数。

### QuerySet 的 API 方法

- `all()` :返回模型的所有对象  


- `filter()(**kwargs)` 用于返回符合条件的所有数据
- `get()` 方法与 `filter()` 的作用类似，用于返回符合条件的单个对象但是可能会返回多个值
- `delete()` ：可以删除符合条件的所有对象
- `update()` ：将符合条件的所有对象的某个字段值进行更新
- `create()` 是 `save()` 方法的快捷方式，用于创建并保存一个新的对象
- `count()` ：返回符合条件的对象数量
- `order_by()` ：对返回的对象进行排序，默认为升序。降序则在字段名前面加负号
- `values()`：用来指定提取的数据中需要提取哪些字段

### 调用方法是：模型名.objects.方法名

比如views.py中的代码示例

```python
queryset = GoodsCategory.objects.all()

category, created = GoodsCategory.objects.get_or_create(name=category_name)

goods = Goods.objects.filter(category=goods_category)
```

## Instance

Instance指的是一个 Django 模型的单个实例，也就是数据库中的一行数据。相比于 QuerySet（查询集合），它是针对单个对象的操作，用于创建、更新或者删除单个模型实例。

QuerySet 适用于需要查找多个对象或进行聚合操作的场景，而 Instance 适用于单独对象的创建、修改和删除操作。

创建一个对象

```python
Obj = Model(attr1=val1, attr2=val2)，Obj.save()
```

更新一个对象

```python
Obj = Model.objects.get(id=xxx)，Obj.attr1 = val1，Obj.save()
```

删除一个对象

```python
Obj = Model.objects.get(id=xxx)，Obj.delete()
```

## 操作演示

### 打开shell

我们使用 shell 来熟悉 QuerySet 和 Instance 的操作

**ctrl +  `**

打开 `vscode` 终端，执行进入 `erp` 目录，打开 `shell`

```
python manage.py shell
(ybw) PS D:\星球项目\django后端开发> cd .\erp\
(ybw) PS D:\星球项目\django后端开发\erp> python .\manage.py shell
(ybw) PS D:\星球项目\django后端开发\erp> python .\manage.py shell
Python 3.11.2 | packaged by Anaconda, Inc. | (main, Mar 27 2023, 23:35:04) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.13.2 -- An enhanced Interactive Python. Type '?' for help.

In [1]:
```

### 导入 models.py 中的2个类

```Python
 from apps.models import *
```

models.py 中有2个类

GoodsCategory 和 Goods

```Python
## 产品分类表
class GoodsCategory(Model):
    """产品分类"""

    name = CharField(max_length=64, verbose_name='分类名称')
    remark = CharField(max_length=64, null=True, verbose_name='备注', blank=True)

## 产品表
class Goods(Model):
    """产品"""

    # 外键
    category = ForeignKey(GoodsCategory, on_delete=SET_NULL, related_name='goods_set', null=True, verbose_name='产品分类',
                          blank=True, )
    # on_delete 

    number = CharField(max_length=32, verbose_name='产品编号')
    name = CharField(max_length=64, verbose_name='产品名称')
    barcode = CharField(max_length=32, null=True, blank=True, verbose_name='条码')
    spec = CharField(max_length=64, null=True, blank=True, verbose_name='规格')
    shelf_life_days = IntegerField(null=True, verbose_name='保质期天数')
    purchase_price = FloatField(default=0, verbose_name='采购价')
    retail_price = FloatField(default=0, verbose_name='零售价')
    remark = CharField(max_length=128, null=True, blank=True, verbose_name='备注')
```

### QuerySet 和 Instance 演示

#### QuerySet 示例

##### **all()** ：**返回全部对象**

```Python
In [2]: GoodsCategory.objects.all()
Out[2]: <QuerySet [<GoodsCategory: 水果>, <GoodsCategory: 动物>, <GoodsCategory: 电脑>, <GoodsCategory: 眼镜>, <GoodsCategory: 衣服>, <GoodsCategory: 皮鞋>, <GoodsCategory: 茶壶>]>

In [6]: Goods.objects.all()
Out[6]: <QuerySet [<Goods: 苹果>, <Goods: 桃子>, <Goods: 猴子>, <Goods: 大象>]>
```

这里分别显示了`GoodsCategory`和`Goods`的全部对象

##### values()：显示更详细信息

```Python
In [4]: GoodsCategory.objects.values()
Out[4]: <QuerySet [{'id': 2, 'name': '水果', 'remark': '水果'}, {'id': 3, 'name': '动物', 'remark': '动物'}, {'id': 9, 'name': '电脑', 'remark': None}, {'id': 13, 'name': '眼镜', 'remark': None}, {'id': 14, 'name': '衣服', 'remark': '衣服'}, {'id': 15, 'name': '皮鞋', 'remark': '皮鞋'}, {'id': 16, 'name': '茶壶', 'remark': None}]>

In [7]: Goods.objects.values()
Out[7]: <QuerySet [{'id': 1, 'category_id': 2, 'number': '1', 'name': '苹果', 'barcode': None, 'spec': None, 'shelf_life_days': 10, 'purchase_price': 3.0, 'retail_price': 5.0, 'remark': None}, {'id': 2, 'category_id': 2, 'number': '2', 'name': '桃子', 'barcode': None, 'spec': None, 'shelf_life_days': 10, 'purchase_price': 4.0, 'retail_price': 6.0, 'remark': None}, {'id': 3, 'category_id': 3, 'number': '3', 'name': '猴子', 'barcode': None, 'spec': None, 'shelf_life_days': 40, 'purchase_price': 60.0, 'retail_price': 100.0, 'remark': None}, {'id': 4, 'category_id': 3, 'number': '4', 'name': '大象', 'barcode': None, 'spec': None, 'shelf_life_days': 200, 'purchase_price': 100.0, 'retail_price': 200.0, 'remark': None}]>
```

可以看出`GoodsCategory`包含`id`，`name`，`remark `3个特征

`Goods`包含`id`，`category_id`，`number`，`name`，`barcode`，`spec`，`shelf_life_days`，`purchase_price`，`retail_price`，`remark `10个特征

```Python
In [16]: Goods.objects.values('number', 'name', 'retail_price')
Out[16]: <QuerySet [{'number': '1', 'name': '苹果', 'retail_price': 5.0}, {'number': '2', 'name': '桃子', 'retail_price': 6.0}, {'number': '3', 'name': '猴子', 'retail_price': 100.0}, {'number': '4', 'name': '大象', 'retail_price': 200.0}]>
```

只显示`number`，`name`，`retail_price `3个特征 

##### count()：计算对象数目

```Python
In [19]: GoodsCategory.objects.all().count()
Out[19]: 7
In [20]: Goods.objects.values().count()
Out[20]: 4
In [32]: queryset = GoodsCategory.objects.all()
In [33]: queryset.count()
Out[33]: 7
```

可以看出`GoodsCategory`包含7个对象，`Goods`包含4个对象

同时`QuerySet`的结果可以赋给变量，进行后续操作

##### filter()：筛选符合条件的对象

```Python
In [5]: GoodsCategory.objects.values().filter(remark=None)
Out[5]: <QuerySet [{'id': 9, 'name': '电脑', 'remark': None}, {'id': 13, 'name': '眼镜', 'remark': None}, {'id': 16, 'name': '茶壶', 'remark': None}]> 
```

返回`remark=None`的对象

```Python
In [18]: Goods.objects.values('number', 'name', 'retail_price').filter(number__gt=2)
Out[18]: <QuerySet [{'number': '3', 'name': '猴子', 'retail_price': 100.0}, {'number': '4', 'name': '大象', 'retail_price': 200.0}]>
```

返回`number > 2`的对象

```Python
In [23]: Goods.objects.values('number', 'name', 'retail_price').filter(number__gt=1, retail_price__lt=150)
Out[23]: <QuerySet [{'number': '2', 'name': '桃子', 'retail_price': 6.0}, {'number': '3', 'name': '猴子', 'retail_price': 100.0}]>
```

返回 `number > 1` 以及 `retail_price < 150` 的对象，即 `filter` 可以多个条件联合使用

##### exclude()：排除符合条件的对象

```Python
In [21]: GoodsCategory.objects.values().exclude(remark=None)
Out[21]: <QuerySet [{'id': 2, 'name': '水果', 'remark': '水果'}, {'id': 3, 'name': '动物', 'remark': '动物'}, {'id': 14, 'name': '衣服', 'remark': '衣服'}, {'id': 15, 'name': '皮鞋', 'remark': '皮鞋'}]>
```

去除`remark=None`的对象，即筛选出`remark != None`的对象

##### update()：修改数据并保存

```Python
In [25]: GoodsCategory.objects.filter(id=13).update(remark='眼镜')
Out[25]: 1
In [27]: GoodsCategory.objects.values().filter(id=13)
Out[27]: <QuerySet [{'id': 13, 'name': '眼镜', 'remark': '眼镜'}]>
```

将`GoodsCategory`的`id=13`的对象的 `remark` 改成眼镜

##### delete()：删除对象

```Python
In [28]: GoodsCategory.objects.filter(name='动物').delete()
Out[28]: (1, {'apps.GoodsCategory': 1})
In [29]: GoodsCategory.objects.all()
Out[29]: <QuerySet [<GoodsCategory: 水果>, <GoodsCategory: 电脑>, <GoodsCategory: 眼镜>, <GoodsCategory: 衣服>, <GoodsCategory: 皮鞋>, <GoodsCategory: 茶壶>]>
```

删除`name="动物"`的对象

##### order_by()：对筛选结果排序

```Python
In [30]: Goods.objects.values('number', 'name', 'retail_price').order_by('retail_price')
Out[30]: <QuerySet [{'number': '1', 'name': '苹果', 'retail_price': 5.0}, {'number': '2', 'name': '桃子', 'retail_price': 6.0}, {'number': '3', 'name': '猴子', 'retail_price': 100.0}, {'number': '4', 'name': '大象', 'retail_price': 200.0}]>
```

将Goods的`number`，`name`，`retail_price `3个特征 按照`retail_price`进行升序排列

```Python
In [31]: Goods.objects.values('number', 'name', 'retail_price').order_by('-retail_price')
Out[31]: <QuerySet [{'number': '4', 'name': '大象', 'retail_price': 200.0}, {'number': '3', 'name': '猴子', 'retail_price': 100.0}, {'number': '2', 'name': '桃子', 'retail_price': 6.0}, {'number': '1', 'name': '苹果', 'retail_price': 5.0}]>
```

通过添加负号，将 `Goods` 按照降序排列

#### Instance示例

##### get()：指定一个对象

```Python
In [42]: Goods.objects.get(name='苹果')
Out[42]: <Goods: 苹果>
In [43]: Goods.objects.values().get(name='苹果')
Out[43]:
{'id': 1,
 'category_id': 2,
 'number': '1',
 'name': '苹果',
 'barcode': None,
 'spec': None,
 'shelf_life_days': 10,
 'purchase_price': 3.0,
 'retail_price': 5.0,
 'remark': None}
```

返回`name="苹果"`的对象

##### 修改一个对象

```Python
In [44]: obj = Goods.objects.get(name='苹果')
In [45]: obj.purchase_price = 4.0
In [46]: obj.retail_price = 6.0
In [47]: obj.remark = '水果'
In [48]: obj.save()
In [52]: Goods.objects.values().get(name='苹果')
Out[52]:
{'id': 1,
 'category_id': 2,
 'number': '1',
 'name': '苹果',
 'barcode': None,
 'spec': None,
 'shelf_life_days': 10,
 'purchase_price': 4.0,
 'retail_price': 6.0,
 'remark': '水果'}
```

##### delete()：删除一个对象

```Python
In [56]: GoodsCategory.objects.values()
Out[56]: <QuerySet [{'id': 2, 'name': '水果', 'remark': '水果'}, {'id': 9, 'name': '电脑', 'remark': None}, {'id': 13, 'name': '眼镜', 'remark': '眼镜'}, {'id': 14, 'name': '衣服', 'remark': '衣服'}, {'id': 15, 'name': '皮鞋', 'remark': '皮鞋'}, {'id': 16, 'name': '茶壶', 'remark': None}]>
In [58]: obj2 = GoodsCategory.objects.get(id=16)
In [59]: obj2
Out[59]: <GoodsCategory: 茶壶>
In [60]: obj2.delete()
Out[60]: (1, {'apps.GoodsCategory': 1})
In [61]: GoodsCategory.objects.values()
Out[61]: <QuerySet [{'id': 2, 'name': '水果', 'remark': '水果'}, {'id': 9, 'name': '电脑', 'remark': None}, {'id': 13, 'name': '眼镜', 'remark': '眼镜'}, {'id': 14, 'name': '衣服', 'remark': '衣服'}, {'id': 15, 'name': '皮鞋', 'remark': '皮鞋'}]>
```

删除`GoodsCategory`中`id=2`的对象，即`name="茶壶"`的对象
