I'm having a weird problem in my django Restfull Application ! i'm using Djongo as db engine (MongoDB)
the problem seems to be happening in /usr/local/lib/python3.8/dist-packages/rest_framework/utils/encoders.py which sometimes say create() function returned null object or returned AssertionError as Object which is not Serializable at the end !
Model :
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
View :
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']})
Serializer :
class ProductSerializer(ModelSerializer):
"""
Product Serialiazer !
"""
class Meta(object):
model = Product
fields = '__all__'
made this external script to test if the post request goes well
Note the the auth token goes well and everything smooth , only the creation does not work 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)
error :
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
This is your problematic line:
return(Response({'status':'failed' , 'error':ex}))
You are passing the actual execption object (ex
) as part of the response, which is in turn serialised to JSON. But there is no built in methods for doing this.
Instead of returning the whole exception, you should return just the message. Something like this:
return(Response({'status':'failed' , 'error':str(ex)}))
It's bad practice to do this:
except Exception as ex:
This will capture all possible errors, which you don't want. Some things should fail at different points. Django has it's own exception handling built in, so nothing is going to completely crash your server. If something is going wrong which you don't expect you need to know about this, and let the error bubble all the way up. Not capture it like this. It makes debugging a lot more difficult.
It's much better to capture a specific error. An additional advantage of this is you can write your own message, which will make more sense to the end user. e.g:
except AssertionError:
return(Response(
{'status':'failed' ,
'error': 'Your own message here'
})
)