UUID('...')无法序列化为JSON

78

当我尝试将UUID属性传递给URL参数时,出现了这个错误。

urlpatterns = [
    url(r'^historia-clinica/(?P<uuid>[W\d\-]+)/$', ClinicHistoryDetail.as_view(), name='...'),
]

视图.py

class ClinicHistoryDetail(...):
     ...
     my_object = MyModel.objects.create(...)
     ...
     return redirect(reverse('namespace:name', kwargs={'uuid' : my_object.id}))

模型.py

class MyModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    ...

有任何建议吗?


如果您能展示一下您是如何通过的,以及ClinicHistoryDetail的作用,那就更好了。 - Ozgur Vatansever
8个回答

90

关于此问题,Django 上有一个错误报告,但是 Python 文档中的所谓的“复杂编码器”可以帮助您。

import json
from uuid import UUID


class UUIDEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, UUID):
            # if the obj is uuid, we simply return the value of uuid
            return obj.hex
        return json.JSONEncoder.default(self, obj)

现在,如果我们做了这样的事情

json.dumps(my_object, cls=UUIDEncoder)

你的uuid字段应进行编码。


json.dumps(my_object.__dict__, cls=UUIDEncoder)正在运行。 - Daniel Eisenreich
1
有没有办法获取一个字符串uuid,然后调用toString方法或者其他什么的? - Alexander Mills
1
这会去掉连字符并更改值。踩! - mmla
2
@mmla 尝试使用 u = UUID(obj.hex),你会发现它并不会改变任何东西。 - sashaaero
5
如果想保留连字符,可以使用str(obj)替换obj.hex - mikebridge

40

将UUID转换为字符串。

uuid_str = str(uuid_item)


2
但是为什么 JSON 编码器不直接这样做呢? - Rikki

12
看起来在使用 DjangoJSONEncoder 时,这个错误对于 Django 已经不再相关了。不需要编写自定义编码器。Django 提供的编码器可以处理大多数已知的序列化问题。
import json
from django.core.serializers.json import DjangoJSONEncoder
 
json.dumps(my_object, cls=DjangoJSONEncoder)

详情请参考这里


6
我会使用转换函数进行处理,这很简单和干净。
import json
from uuid import UUID
def uuid_convert(o):
        if isinstance(o, UUID):
            return o.hex

json.dumps(users,indent=4,default=uuid_convert)

5

如果要在URL中使用UUID,您应该将其作为字符串传递:

 return redirect(reverse('namespace:name', kwargs={'uuid' : str(object.id)}))

提供信息 - 看起来WIM的答案更加详细。你的正则表达式肯定需要进一步优化。如果你最终使用slug的字符串表示形式,你需要使用这样的正则表达式:[A-Za-z0-9\-]+,允许字母数字和连字符。


你可能仍然会遇到错误,因为UUID或十六进制都不是可序列化的对象。你应该在kwargs字典中将它们转换为字符串。 str(object.id)str(object.id.hex) 然后,只需确保你的正则表达式与你选择的选项匹配。如果是十六进制,请使用 [0-9a-f]{32}。否则,请使用 [A-Za-z0-9\-]+ - Jordan Haines

5

另一种选择是使用default=str。我将其留作参考:

import uuid
import json

pk = uuid.UUID("04d8d2dc-e81b-4eaa-8883-d1e361a08da8")
o = json.dumps({"pk": pk}, default=str)
print(o)

输出:

{"pk": "04d8d2dc-e81b-4eaa-8883-d1e361a08da8"}

0
我在数据库模型中遇到了一个UUID字段的同样问题,我想要将其打印出来以进行调试。我发现pprint模块中的pprint()函数可以处理这个问题。你还可以给它一个indent参数,以获得与json.dumps()相同的缩进输出。

https://docs.python.org/3/library/pprint.html#pprint.pprint

例子:

>>> import uuid
>>> import pprint
>>> import json
>>> x = uuid.UUID('12345678123456781234567812345678')
>>> x
UUID('12345678-1234-5678-1234-567812345678')
>>> print(x)
12345678-1234-5678-1234-567812345678
>>> json.dumps(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
...
...
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: UUID('12345678-1234-5678-1234-567812345678') is not JSON serializable
>>> pprint.pprint(x)
UUID('12345678-1234-5678-1234-567812345678')

0
我使用了一个lambda函数来内联转换对象为字符串。
import uuid
import json

mydict = {
    "key1":"value1",
    "guid1": uuid.uuid4()
}

myjson = json.dumps(mydict, default=lambda x : str(x))

print(myjson)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接