你这段内容围绕 Django 的 **模型对象、Manager、QuerySet** 以及常见类型错误，做了非常详细的讲解。下面帮你把核心结论总结成一张清晰的脉络：

---

## 一、三个核心概念的区别

|概念|类型|含义|作用|
|---|---|---|---|
|**Book 对象**| `TestModel.models.Book` |数据库中的**一行记录**|表示单个数据对象，可访问字段、修改并 `save()` |
|**Book.objects**| `django.db.models.manager.Manager` |模型的**默认管理器**|数据库查询入口，提供 `all()`、`filter()`、`get()`、`create()` 等方法|
|**QuerySet**| `django.db.models.query.QuerySet` |**查询结果集**|可包含 0/1/多个对象，支持链式调用，惰性执行|

**关系：**  
`Book.objects`（Manager）→ 调用方法 → 返回 `QuerySet` → 取元素 → 得到 `Book` 对象 / dict / tuple / 单值。

---

## 二、为什么 `Book.objects[0]` 不行？

- `Book.objects` 是 **Manager**，不是 QuerySet，**不支持索引**。
    
- 必须先调用 `all()` / `filter()` 等返回 QuerySet，才能用 `[0]` 或 `.first()`。
    

正确方式：

python

Book.objects.first()          # 推荐，空表返回 None
Book.objects.all()[0]         # 可能 IndexError
Book.objects.filter()[0]      # 可能 IndexError

---

## 三、Manager 是什么？

- Django 模型与数据库交互的**主要接口**。
    
- 每个模型默认都有 `objects`，它是 `Manager` 的实例。
    
- 可自定义 Manager 添加额外查询方法。
    
- Manager 方法通常返回 **QuerySet** 或 **模型实例**。
    
- Manager 本身**不支持链式调用**，QuerySet 支持。
    

---

## 四、`'int' object is not subscriptable` 错误

含义：对 **整数** 使用了 `[0]`、`[:3]`、`["key"]` 等下标操作。

原因：

- `int` 类型没有 `__getitem__` 方法。
    
- 常见于误以为变量是字符串/列表/字典，实际是整数。
    

解决：

- 用 `type()` 检查变量类型。
    
- 需要下标时先转成 `str`、`list` 等可下标类型。
    

---

## 五、QuerySet 的数据形式为什么不同？

QuerySet 本身的类型始终是 `QuerySet`，但**内部元素形式**由调用的方法决定：

|方法|返回的 QuerySet 元素形式|示例|
|---|---|---|
| `all()` |**模型实例**| `<Book: Book object (1)>` |
| `values()` |**字典 dict**| `{'pk': 1, 'price': Decimal('300.00')}` |
| `values_list()` |**元组 tuple**| `(Decimal('300.00'), '菜鸟出版社')` |
| `values_list(flat=True)` |**单个值**| `Decimal('300.00')` |

所以你会看到：

python

<QuerySet [{'pk': 1, 'price': Decimal('300.00')}, ...]>
<QuerySet [(Decimal('300.00'), '菜鸟出版社'), ...]>

虽然元素形式不同，但 `type(books)` 仍然是 `QuerySet`。

---

## 六、为什么 `type(books)` 总是 QuerySet？

因为 `books[0]` 只是**取 QuerySet 的第一个元素**，`books` 变量本身没有改变。

python

my_list = [1, 2, 3]
print(my_list[0], type(my_list))  # 1 <class 'list'>

同理：

python

books[0]          # 第一个元素，可能是 Book / dict / tuple
type(books)       # 仍然是 QuerySet
type(books[0])    # 才是第一个元素的真实类型

---

## 七、一句话总结

> **Manager 是查询入口，QuerySet 是查询结果集，Book 对象是单行记录。**  
> QuerySet 的类型不变，但内部元素形式由 `all()` / `values()` / `values_list()` 决定。  
> 取元素用 `[0]` 或 `.first()`，但 `type()` 要看的是**元素本身**，而不是 QuerySet 变量。