Sof*_*ngs 6 python mongodb pymongo fastapi
我在通过 FastAPI 插入 MongoDB 时遇到一些问题。
下面的代码按预期工作。请注意该response变量尚未在 中使用response_to_mongo()。
这model是一个 sklearn ElasticNet 模型。
app = FastAPI()
def response_to_mongo(r: dict):
client = pymongo.MongoClient("mongodb://mongo:27017")
db = client["models"]
model_collection = db["example-model"]
model_collection.insert_one(r)
@app.post("/predict")
async def predict_model(features: List[float]):
prediction = model.predict(
pd.DataFrame(
[features],
columns=model.feature_names_in_,
)
)
response = {"predictions": prediction.tolist()}
response_to_mongo(
{"predictions": prediction.tolist()},
)
return response
Run Code Online (Sandbox Code Playgroud)
但是,当我predict_model()这样写并将response变量传递给response_to_mongo():
@app.post("/predict")
async def predict_model(features: List[float]):
prediction = model.predict(
pd.DataFrame(
[features],
columns=model.feature_names_in_,
)
)
response = {"predictions": prediction.tolist()}
response_to_mongo(
response,
)
return response
Run Code Online (Sandbox Code Playgroud)
我收到一条错误消息:
TypeError: 'ObjectId' object is not iterable
Run Code Online (Sandbox Code Playgroud)
从我的阅读来看,这似乎是由于 FastAPI 和 Mongo 之间的 BSON/JSON 问题造成的。但是,为什么当我不使用变量时它在第一种情况下起作用?这是由于 FastAPI 的异步特性吗?
Chr*_*ris 13
根据文档:
\n\n\n插入文档时,
\n"_id"如果文档\xe2\x80\x99t 尚未包含密钥,则会自动添加特殊密钥"_id",。该值"_id"在整个集合中必须是唯一的。返回InsertOneResultinsert_one()的\实例。有关“_id”的详细信息,请参阅有关 _id 的\n 文档。
因此,在您提供的示例的第二种情况下,当您将字典传递给函数时,Pymongo 将向您的字典添加从数据库检索数据所需的insert_one()唯一标识符(即);ObjectId因此,当从端点返回响应时,ObjectId序列化失败\xe2\x80\x94,因为,如本答案中详细描述的,FastAPI默认情况下会使用jsonable_encoder(到确保不可序列化的对象转换为 a str),然后返回 a JSONResponse,它使用标准json库来序列化数据。
使用此处演示的方法,通过默认ObjectId转换为str,因此,您可以response在端点内照常返回。
# place these at the top of your .py file\nimport pydantic\nfrom bson import ObjectId\npydantic.json.ENCODERS_BY_TYPE[ObjectId]=str\n\nreturn response # as usual\nRun Code Online (Sandbox Code Playgroud)\n将加载的内容转储BSON到有效JSON字符串,然后将其重新加载为dict,如此处和此处所述。
from bson import json_util\nimport json\n\nresponse = json.loads(json_util.dumps(response))\nreturn response\nRun Code Online (Sandbox Code Playgroud)\n定义一个自定义JSONEncoder,如此处所述,将其转换ObjectId为str:
import json\nfrom bson import ObjectId\n\nclass JSONEncoder(json.JSONEncoder):\n def default(self, o):\n if isinstance(o, ObjectId):\n return str(o)\n return json.JSONEncoder.default(self, o)\n\n\nresponse = JSONEncoder().encode(response)\nreturn response\nRun Code Online (Sandbox Code Playgroud)\n您可以拥有一个不带“ObjectId”( _id) 字段的单独输出模型,如文档中所述。response_model您可以使用端点装饰器中的参数来声明用于响应的模型。例子:
from pydantic import BaseModel\n\nclass ResponseBody(BaseModel):\n name: str\n age: int\n\n\n@app.get(\'/\', response_model=ResponseBody)\ndef main():\n # response sample\n response = {\'_id\': ObjectId(\'53ad61aa06998f07cee687c3\'), \'name\': \'John\', \'age\': \'25\'}\n return response\nRun Code Online (Sandbox Code Playgroud)\n在返回之前"_id"从字典中删除条目(请参阅此处了解如何从 a 中删除键):responsedict
response.pop(\'_id\', None)\nreturn response\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
6834 次 |
| 最近记录: |