FastAPI - 格式错误的十六进制 UUID 字符串

Posted

技术标签:

【中文标题】FastAPI - 格式错误的十六进制 UUID 字符串【英文标题】:FastAPI - badly formed hexadecimal UUID string 【发布时间】:2022-01-15 02:22:34 【问题描述】:

我正在使用 FastAPI 和 SQLModel 将数据插入 SQLite 数据库(我发布了一些字段,然后 SQLModel 添加了 UUID 和日期时间字段并插入到数据库中)。

在将数据发布到 FastAPI 时,我偶尔会收到 ValueError:

ValueError: badly formed hexadecimal UUID string

我认为我发送的数据没有问题,因为我没有发送 id 字段,所以可能是我的 SQLModel 配置错误?


模型

# -*- coding: utf-8 -*-
import uuid
from typing import Optional
from datetime import datetime, date

from pydantic import UUID4, HttpUrl
from sqlmodel import Field, SQLModel


class EventBase(SQLModel):
    title: str
    text: str
    date: date
    URL: HttpUrl
    category: Optional[str] = Field(default=None) 


class Event(EventBase, table=True):
    id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True)
    created_at: datetime = Field(default_factory=datetime.utcnow)

编辑:决定尝试这个 approach 用 uuid.UUID 替换 UUID4,但效果不佳:


Uvicorn 调试

INFO:     127.0.0.1:53312 - "POST /speeches/ HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "c:\github\speeches-api\env\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 373, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\debug.py", line 96, in __call__
    raise exc from None
  File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\debug.py", line 93, in __call__
    await self.app(scope, receive, inner_send)
  File "c:\github\speeches-api\env\lib\site-packages\fastapi\applications.py", line 208, in __call__
    await super().__call__(scope, receive, send)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
    raise exc
  File "c:\github\speeches-api\env\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\exceptions.py", line 82, in __call__
    raise exc
  File "c:\github\speeches-api\env\lib\site-packages\starlette\exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 656, in __call__
    await route.handle(scope, receive, send)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 259, in handle
    await self.app(scope, receive, send)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 61, in app
    response = await func(request)
  File "c:\github\speeches-api\env\lib\site-packages\fastapi\routing.py", line 226, in app
    raw_response = await run_endpoint_function(
  File "c:\github\speeches-api\env\lib\site-packages\fastapi\routing.py", line 161, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "c:\github\speeches-api\env\lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
  File "c:\github\speeches-api\env\lib\site-packages\anyio\to_thread.py", line 28, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
  File "c:\github\speeches-api\env\lib\site-packages\anyio\_backends\_asyncio.py", line 818, in run_sync_in_worker_thread
    return await future
  File "c:\github\speeches-api\env\lib\site-packages\anyio\_backends\_asyncio.py", line 754, in run
    result = context.run(func, *args)
  File ".\app\main.py", line 29, in create_events
    session.refresh(event)
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\session.py", line 2315, in refresh
    loading.load_on_ident(
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 407, in load_on_ident
    return load_on_pk_identity(
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 541, in load_on_pk_identity
    return result.one()
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1407, in one
    return self._only_one_row(
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 558, in _only_one_row
    row = onerow(hard_close=True)
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1271, in _fetchone_impl
    return self._real_result._fetchone_impl(hard_close=hard_close)
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1674, in _fetchone_impl
    row = next(self.iterator, _NO_ROW)
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 147, in chunks
    fetch = cursor._raw_all_rows()
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 392, in _raw_all_rows
    return [make_row(row) for row in rows]
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 392, in <listcomp>
    return [make_row(row) for row in rows]
  File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\sql\type_api.py", line 1537, in process
    return process_value(value, dialect)
  File "c:\github\speeches-api\env\lib\site-packages\sqlmodel\sql\sqltypes.py", line 59, in process_result_value
    value = uuid.UUID(value)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\uuid.py", line 169, in __init__
    raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string

【问题讨论】:

【参考方案1】:

我认为您需要在默认工厂中执行str(uuid.uuid4())

【讨论】:

那可悲的是没用【参考方案2】:

我了解到这个问题在 issue 中有所描述:数据验证'工作,除非 uuid.uuid4 默认工厂创建一个 UUID,其十六进制字符串以零开头。' p>

chriswhite199 在 cmets 中发布了建议的(临时)解决方案 below:

针对我的情况进行了调整

def new_uuid() -> uuid.UUID:
    # Note: Work around UUIDs with leading zeros: https://github.com/tiangolo/sqlmodel/issues/25
    # by making sure uuid str does not start with a leading 0
    val = uuid.uuid4()
    while val.hex[0] == '0':
        val = uuid.uuid4()
    return val

class Event(EventBase, table=True):
    id: UUID4 = Field(default_factory=new_uuid, primary_key=True)
    created_at: datetime = Field(default_factory=datetime.utcnow)

【讨论】:

以上是关于FastAPI - 格式错误的十六进制 UUID 字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何从 MySQL/MariaDB 中的二进制列格式化 uuid 字符串

RestKit:你如何映射到 NSUUID?

如何测试使用图像的 FastAPI api 端点?

UUID(uuid)js 生成

如何测试有效的 UUID/GUID?

javascript 生成 uuid