I'm trying out using AJAX to implement tags for Book Blog posts. I want to use TDD to check and make sure that they filter properly but am running into issues. Mainly when I try and Deserialize the JSONResponse content, it throws an error when I try and iter through it.
error message:
File "E:\04_projects\01_Python\10_book_blog\venv\Lib\site-packages\django\core\serializers\json.py", line 70, in Deserializer
yield from PythonDeserializer(objects, **options)
File "E:\04_projects\01_Python\10_book_blog\venv\Lib\site-packages\django\core\serializers\python.py", line 111, in Deserializer
Model = _get_model(d["model"])
~^^^^^^^^^
TypeError: string indices must be integers, not 'str'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "E:\04_projects\01_Python\10_book_blog\posts\tests\test_views.py", line 103, in test_returns_filtered_posts
for obj in decereal:
File "E:\04_projects\01_Python\10_book_blog\venv\Lib\site-packages\django\core\serializers\json.py", line 74, in Deserializer
raise DeserializationError() from exc
django.core.serializers.base.DeserializationError
test case that makes the error:
def test_returns_filtered_posts(self):
# makes one post with a tag and one post without
t1 = Tag.objects.create(tag_name='test', group_name='general')
p1 = Post.objects.create(book_title='test_book')
p2 = Post.objects.create(book_title='test_book2')
p1.tags.add(t1)
# simulates clicking the 'test' tag
response = self.client.post(reverse('ajax_post'), {'tag[]': ['test', 'general']})
# decerealizes the data
decereal = deserialize('json', response.content)
# print for debugging
for obj in decereal:
print(obj)
# bad test case but not getting this far
self.assertNotIn(p2, decereal)
The view that is called:
def ajax_call(request):
data = serializers.serialize('json', Post.objects.all())
# this was just for testing
uncereal = serializers.deserialize('json', data)
return JsonResponse(data, safe=False)
I can iter through uncereal
in ajax_call
without issue, the problem comes in my test case when I try it the same way that they do in the docs https://docs.djangoproject.com/en/4.2/topics/serialization/#deserializing-data, I get an error. Any help would be appreciated. Thank you
Suggested fix:
def ajax_call(request):
serialized = serializers.serialize('json', Post.objects.all())
return HttpResponse(serialized)
Explanation expanded from my comments:
I think the basic problem is that JsonResponse
expects to receive a dict instance, but you are passing it a string of already serialized json. With safe=False
it will accept other JSON-serializable types besides dict (e.g. list, string, number, None).
So if you pass a string to serialize
it will escape it and wrap in double quotes to make it a JSON string. So you have ended up with a JSON string of a JSON value serialized to string.
Then when you come to deserialize the response it's trying to inflate that back into model instances e.g. expects d
to be a dict but it's just a string.
So the simplest fix is just to stop double-encoding things in your view function.
You want to use serializers.serialize
rather than JsonResponse
to do the serialization, because the former understands how to serialize model instances. Having serialized to a string of JSON you can then return that string with an ordinary HttpResponse
.