‘ObjectId’ object is not iterable#
使用fastapi,当返回结果里面包含mongodb的id,也就是ObjectId类型的时候,就会报错: TypeError("‘ObjectId’ object is not iterable")。
直接google搜索下,可以得到几种方法:
- 先使用str()方法将ObjectId转换成字符串
- 使用bson内置的json_util.dumps()方法将ObjectId转换成字符串
- 删除ObjectId字段
- 定义一个JSONEncoder类,将ObjectId转换成字符串
1
2
3
4
5
6
7
8
9
10
|
import json
from bson import ObjectId
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ObjectId):
return str(o)
return json.JSONEncoder.default(self, o)
JSONEncoder().encode(analytics)
|
- json.dumps(my_obj, default=str)
- 如果是老版本的fastapi
1
2
3
|
import pydantic
from bson import ObjectId
pydantic.json.ENCODERS_BY_TYPE[ObjectId]=str
|
看起来,第6种方法是比较优雅的,但是,对于没有使用pydantic的返回结果,就不适用了。而且,新版本的pydantic也不是这样的使用方法了。
其实,不管什么类型,只要是json不支持的,都会报错,比如datetime类型也会报错。
但是为什么fastapi返回datetime类型的时候不会报错呢?因为fastapi内部已经做了处理,将datetime类型转换成了字符串类型。
通过报错信息。
1
2
3
4
5
6
7
8
9
|
│ /xxxx/site-packages/fastapi/encoders.py:332 in │
│ jsonable_encoder │
│ │
│ 329 │ │ │ data = vars(obj) │
│ 330 │ │ except Exception as e: │
│ 331 │ │ │ errors.append(e) │
│ ❱ 332 │ │ │ raise ValueError(errors) from e │
│ 333 │ return jsonable_encoder( │
│ 334 │ │ data,
|
我们可以看到是在fastapi的encoders.py里面报错的。
打开encoders.py文件, 可以看到
1
2
3
|
for encoder, classes_tuple in encoders_by_class_tuples.items():
if isinstance(obj, classes_tuple):
return encoder(obj)
|
有点眼熟是不是,跟JSONEncoder类似,只不过是fastapi内部的实现。
稍微看一眼代码,可以看到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str,
datetime.date: isoformat,
datetime.datetime: isoformat,
datetime.time: isoformat,
datetime.timedelta: lambda td: td.total_seconds(),
Decimal: decimal_encoder,
Enum: lambda o: o.value,
frozenset: list,
deque: list,
GeneratorType: list,
IPv4Address: str,
IPv4Interface: str,
IPv4Network: str,
IPv6Address: str,
IPv6Interface: str,
IPv6Network: str,
NameEmail: str,
Path: str,
Pattern: lambda o: o.pattern,
SecretBytes: str,
SecretStr: str,
set: list,
UUID: str,
Url: str,
AnyUrl: str,
}
|
这个就是fastapi内部的处理方式,将不支持的类型通过对应的处理方式转换成支持的类型。
那么,我们就得到一种比较简单的处理方法了。在程序启动之前,将ObjectId类型添加到ENCODERS_BY_TYPE里面,调用str方法转换即可。
1
2
3
4
5
|
from bson import ObjectId
from fastapi.encoders import ENCODERS_BY_TYPE
#fastapi默认不支持ObjectId转json,这里做个映射,调用str方法转换
ENCODERS_BY_TYPE[ObjectId] = str
|
这样,就可以解决fastapi返回ObjectId类型的问题了。如果有其他类型要处理,也可以按照这个方法处理。
fastapi==0.111.0