rpkilby / jsonfield

A reusable Django model field for storing ad-hoc JSON data
MIT License
1.16k stars 271 forks source link

Django Rest Framework support #188

Closed NBajanca closed 7 years ago

NBajanca commented 7 years ago

When I use Model Serializer from DRF I end up sending through the API invalid JSON.

For example:

{"test":"hello"}

Is sent like:

"{'test':'hello'}"

This makes it harder to process the data received. Is there any way around it?

dmkoch commented 7 years ago

Can you post example code that reproduces the issue?

On May 4, 2017, at 8:51 AM, Nuno Bajanca notifications@github.com wrote:

When I use Model Serializer from DRF I end up sending through the API invalid JSON.

For example:

{"test":"hello"} Is sent like:

"{'test':'hello'}" This makes it harder to process the data received. Is there any way around it?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

NBajanca commented 7 years ago

models.py:

class ExampleModel(models.Model):
    additional_data = JSONField(blank=True)

serializars.py:

class ExampleSerializer(serializers.ModelSerializer):
    class Meta:
        model = ExampleModel
        fields = ('additional_data')

views.py:

class ExampleViewSet(viewsets.ModelViewSet):
    queryset = ExampleModel.objects.all()
    serializer_class = ExampleSerializer

urls.py:

router = SimpleRouter(trailing_slash=False)
router.register(r'example', ExampleViewSet)

After adding (in the Admin) an example model with {"test":"hello"} Doing GET /example I get:

[
    {
        "additional_data": "{'test':'hello'}"
    }
]
NBajanca commented 7 years ago

By the way, I've solved this for now using this line of code in the client calling the API:

json.loads(response[0]['additional_data'].replace("\'", "\""))
dmkoch commented 7 years ago

Thanks, I'll take a look.

NBajanca commented 7 years ago

Thanks @dmkoch.

I've starting inputting valid JSON through the API and it returns the same valid JSON. But for this, I'm sending and receiving JSON as a String ("{\"test\": \"hello\"}"), not as a JSON object so I'm not gaining anything with this library.

The ideal would be to send and receive as JSON, meaning I would do only one dump (request data), not two (json field and request data).

francbartoli commented 7 years ago

@NBajanca I'm not facing this issue while serializing read/write data within the json field.

My situation:

models.py

   input_data = JSONField(null=True,
                           blank=True,
                           default={},
                           load_kwargs={'object_pairs_hook': collections.OrderedDict}
                           )
    output_data = JSONField(null=True,
                            blank=True,
                            default={},
                            load_kwargs={'object_pairs_hook': collections.OrderedDict}
                            )
# ...signal
    output_data = cmd_result
    if created:
        Process.objects.filter(id=instance.id
                               ).update(output_data=output_data
                                        )

post_save.connect(run_process, sender=Process)

serializers.py

input_data = serializers.JSONField()
output_data = serializers.JSONField()

views.py

@api_view(['GET', 'POST'])
@permission_classes((permissions.IsAuthenticated, IsOwner, ))
def process_list(request):
    if request.method == 'GET':
        processes = models.Process.objects.all()
        serializer = serializers.ProcessSerializer(processes, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = serializers.ProcessSerializer(data=request.data)
        if serializer.is_valid():  # TODO add more validation
        # see https://richardtier.com/2014/03/24/json-schema-validation-with-django-rest-framework/
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET', 'PUT', 'DELETE'])
@permission_classes((permissions.IsAuthenticated, IsOwner, ))
def process_detail(request, id):
    process = get_object_or_404(models.Process, pk=id)

    if request.method == 'GET':
        serializer = serializers.ProcessSerializer(process)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = serializers.ProcessSerializer(process, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        process.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Hope this helps

NBajanca commented 7 years ago

@francbartoli thanks, it helped.

I used Model Serializer and so I didn't specify the serializer to use for the JSON field. As soon as I changed the serializer to it started working:

class ExampleSerializer(serializers.ModelSerializer):
    additional_data = serializers.JSONField()

    class Meta:
        model = ExampleModel
        fields = ('additional_data')

I've checked the implementation of ModelSerializer and it maps directly the fields so I don't think it is possible to be more DRY then this.

I would suggest that you add a note to readme about this.

nguyendv commented 6 years ago

I got the same issue today. I can make a PR for updating the README file if it's welcomed.