Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework 3 required field behaviour

I have read a changelog for drf3 but its still unclear for me. Previously i have following serializer:

class TestSerializer(serializers.Serializer):
   att1= serializers.CharField()
   att2= serializers.CharField()
   att3= serializers.CharField(required=False) 

And when i was passing object with only att1 and att2 values in it - it was working fine, no errors, no attribute in the output. But now if i don't pas att3 i got error

Got KeyError when attempting to get a value for field att3 on serializer TestSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the EasyDict instance.
Original exception text was: att3'.

But according to release notes:

required=False: The value does not need to be present in the input

So the code seems valid for me or i don't understand something.

 data = TestSerializer(s.get_results()).data

Where get_results instance of EasyDict with missing att3:

class EasyDict(dict):

  def __init__(self, d=None, **kwargs):
    if d is None:
        d = {}
    if kwargs:
        d.update(**kwargs)
    for k, v in d.items():
        setattr(self, k, v)
    # Class attributes
    for k in self.__class__.__dict__.keys():
        if not (k.startswith('__') and k.endswith('__')):
            setattr(self, k, getattr(self, k))

  def __setattr__(self, name, value):
    if isinstance(value, (list, tuple)):
        value = [self.__class__(x) if isinstance(x, dict) else x for x in value]
    else:
        value = self.__class__(value) if isinstance(value, dict) else value
    super(EasyDict, self).__setattr__(name, value)
    self[name] = value

It was working perfectly fine in drf2, but got this error after upgrading to drf3.

like image 502
Aldarund Avatar asked Sep 30 '22 03:09

Aldarund


1 Answers

Django REST Framework uses serializers for both serializing (output) and deserializing (input) data.

It's difficult to tell if you are serializing or deserializing your data in your case, but you will find issues with both, so I'll give you an answer for both cases.


When deserializing data, you should pass in the data to deserialize in using the data keyword argument, and (optionally) the instance to update with the instance keyword argument.

serializer = TestSerializer(data=s.get_results())
if serializer.is_valid():
    data = serializer.data

This should work as expected, up until you call .data on the serializer. This is because calling .data will then serialize the data object again, based on the temporary instance, which you'll learn below is still an issue.

It is important to note that when deserializing data, the required=False parameter will prevent Django REST Framework from requiring that the field is present.


When serializing data, you need to pass a full object representation in using the instance keyword argument, which is also the first positional argument. Django REST Framework expects that the object that is passed in will have all of the fields that you are requesting, even if they are null (None) or blank. The required=False argument does nothing here because we are serialzing data. So you need to ensure that the dictionary (or comparable object) is passed in with all of the keys that the serializer requires.

There is a special case though, when an object is being deserialized and then serialized using the same object. In this case, the underlying object that has been created must still have the optional field, even if the input did not provide it.

like image 79
Kevin Brown-Silva Avatar answered Oct 05 '22 08:10

Kevin Brown-Silva