Djongo 模型 - 如何摆脱:AssertionError 类型的对象不是 JSON 可序列化的

Djongo Models - How to get rid of : Object of type AssertionError is not JSON serializable

我的 django Restfull 应用程序出现了一个奇怪的问题! 我正在使用 Djongo 作为数据库引擎 (MongoDB)

问题似乎发生在 /usr/local/lib/python3.8/dist-packages/rest_framework/utils/encoders.py 中,有时会说 create() 函数返回空对象或返回 AssertionError作为最后不可序列化的对象!

型号:

class Product(models.Model):
   """
   Product Model
   """

   _id         = models.ObjectIdField()
   name        = models.CharField(max_length=MAX_PROD_NAME_LEN)
   sku         = models.CharField(max_length=MAX_PROD_SKU_LEN , unique=True)
   category    = models.CharField(max_length=MAX_PROD_CAT_LEN)
   desc        = models.TextField(default="")
   agent_desc  = models.TextField(default="")
   urls        = models.JSONField(blank=True , null=True)
   productType = models.CharField(max_length=MAX_PROD_TYPE_LEN)
   variants    = models.JSONField(default={
       # "op1":models.CharField(max_length=MAX_PROD_OPTION_LEN),
       # "op2":models.CharField(max_length=MAX_PROD_OPTION_LEN),
       # "op3":models.CharField(max_length=MAX_PROD_OPTION_LEN),
       # "quantity":models.IntegerField(default=1),
       # "price":models.DecimalField(max_digits=10, decimal_places=2),
   })

   options     = models.JSONField(default={})
   accessories = models.JSONField(default={
       # "name"  : None,
       # "attachements":[],
       # "quant" : None,
       # "desc"  : None
   })
   created_at  = models.DateTimeField(auto_now_add=True , null=False)
   updated_at  = models.DateTimeField(auto_now=True, null=True)
   deleted_at  = models.DateTimeField(default=None)
   is_deleted  = models.BooleanField(default=False)

   objects     = models.DjongoManager() # built-in Model's objects Alike !
   
   
   def __str__(self):
       return self._id

查看:


class ProductView(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer


    def list(self, request):
        queryset = Product.objects.all()
        serializer = ProductSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        print(f"Retrieving infos of {pk}")
        queryset = Product.objects.all()
        oid = None
        try:
            oid = ObjectId(pk)
        except Exception:
            return Response("Product not found !")
        
        product = get_object_or_404(queryset, _id=oid)
        serializer = ProductSerializer(product)
        return Response(serializer.data)

    def create(self, request):

        try:
            print(f'{type(request.data)} -- {dict(request.data)}')

            serializer = ProductSerializer(data=dict(request.data))
            prod = serializer.save()
            # if serializer.is_valid(raise_exception=True):
            return Response({'status':'success' , 'object':prod._id})
            # else:
            #     return(Response({'status':'failed' , 'error':'Failed Creating Product'}))
        except Exception as ex:
            return(Response({'status':'failed' , 'error':ex}))

    # def create(self, request, *args, **kwargs):
    #     response = super().create(request, *args, **kwargs)
    #     instance = response.data
    #     return Response({'status': 'success', 'pk': instance['pk']})
        

序列化器:

class ProductSerializer(ModelSerializer):
    """
    Product Serialiazer !
    """
    class Meta(object):
        model   = Product
        fields  = '__all__'

创建这个外部脚本来测试 post 请求是否正常

请注意,身份验证令牌运行良好,一切顺利,只有创建不起作用 Test_Post.py :

import requests as req
import json
from sys import argv
from datetime import datetime as dtm

link = 'http://localhost:8000/api/token/'
r=req.post(link , {'username':'root', 'password':'root'})
#print(r.text)
auth_access = json.loads(r.text)['access']
pyload = {
    "name" : "prod2",
    "sku" : f"asaasaku2{dtm.now()}",
    "category" : "casaat2",
    "desc" : "desasc2",
    "agent_desc" : "asaagentDesc2",
    "productType" : "typdef2f",
#    "variants" : {
#        "op1" : "o1",
#        "op2" : "o2",
#        "op3" : "o3",
#        "quantity" : 10.0,
#        "price" : 500.0
#    },
#    "accessories" : [ 
#        "a1", 
#        "a2"
#    ]
}

r = req.post(f'http://localhost:8000{argv[1]}', json=pyload, headers={'content-type':'application/json','Authorization':'Bearer '+str(auth_access)})

print(r.text)

错误:

Internal Server Error: /prods/
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/lib/python3.8/site-packages/django/core/handlers/base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/lib/python3.8/site-packages/django/core/handlers/base.py", line 143, in _get_response
    response = response.render()
  File "/usr/lib/python3.8/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/usr/local/lib/python3.8/dist-packages/rest_framework/response.py", line 70, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/usr/local/lib/python3.8/dist-packages/rest_framework/renderers.py", line 100, in render
    ret = json.dumps(
  File "/usr/local/lib/python3.8/dist-packages/rest_framework/utils/json.py", line 25, in dumps
    return json.dumps(*args, **kwargs)
  File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
    return cls(
  File "/usr/lib/python3.8/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.8/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.8/dist-packages/rest_framework/utils/encoders.py", line 68, in default
    return super().default(obj)
  File "/usr/lib/python3.8/json/encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type AssertionError is not JSON serializable

问题

这是你的问题行:

return(Response({'status':'failed' , 'error':ex}))

您正在传递实际的执行对象 (ex) 作为响应的一部分,该对象又被序列化为 JSON。但是没有内置的方法可以做到这一点。

解决方案

而不是 return 处理整个异常,您应该 return 只处理消息。像这样:

return(Response({'status':'failed' , 'error':str(ex)}))

还有...

这样做是不好的做法:

except Exception as ex:

这将捕获您不想要的所有可能的错误。有些事情应该在不同的点失败。 Django 有它自己内置的异常处理,所以没有什么会完全崩溃你的服务器。如果出现了你不希望的错误,你需要知道这一点,并让错误一直冒出来。不是这样捕捉的。它使调试变得更加困难。

捕获特定错误要好得多。这样做的另一个好处是您可以编写自己的消息,这对最终用户来说更有意义。例如:

except AssertionError:
    return(Response(
         {'status':'failed' , 
          'error': 'Your own message here'
         })
    )