Python – How do I use the Django REST serializer to validate reserved keys?

How do I use the Django REST serializer to validate reserved keys?… here is a solution to the problem.

How do I use the Django REST serializer to validate reserved keys?

The blink workaround can be a simple problem. But I can’t finish it.
I’m now creating the webhook endpoint. And stay in the serializer class
My class can’t use from as a class attribute

python 3.6.4
Django==1.11.9
djangorest==3.7.7

@pytest.fixture
def like_object():
    """LIKE object response from Facebook"""
    return {
        "object": "page",
        "entry": [{
            "changes": [{
                "field": "feed",
                "value": {
                    "item": "reaction",
                    "verb": "add",
                    "reaction_type": "like",
                    "created_time": 1516183830,
                    "post_id": "1331351323541869_1844740022202994",
                    "from": {
                        "name": "Elcoie Sieve",
                        "id": "1639217166122728"
                    },
                    "parent_id": "1331351323541869_1844740022202994"
                }
            }],
            "time": 1516183830, "id": "1331351323541869"
        }]
    }

serializers.py

class FacebookReactionSerializer(serializers. Serializer):
    """
    value serializer the inner most of the payload
    """
    item = serializers. CharField()
    verb = serializers. CharField()
    reaction_type = serializers. CharField()
    created_time = serializers. IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(4086831600)]
    )  # Limit the maximum epoch to 2099 July 4th 7:00AM
    post_id = serializers. CharField(max_length=40)
    from = FromSerializer()
    parent_id = serializers. CharField()

def validate(self, attrs):
        """
        `from` is a python reserved word the add _ to distinguish it from them
        :param attrs:
        :return:
        """
        from_ = attrs.get('from')
        pass

def create(self, validated_data):
        pass

def update(self, instance, validated_data):
        pass

Try:

ffrom = FromSerializer(source='from')

Does not work

Try 2:

class FacebookReactionSerializer(serializers. Serializer):
    """
    value serializer the inner most of the payload
    """
    item = serializers. CharField()
    verb = serializers. CharField()
    reaction_type = serializers. CharField()
    created_time = serializers. IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(4086831600)]
    )  # Limit the maximum epoch to 2099 July 4th 7:00AM
    post_id = serializers. CharField(max_length=40)
    from_key = FromSerializer()
    parent_id = serializers. CharField()

def to_representation(self, instance):
        """
        https://stackoverflow.com/questions/47630356/using-the-reserved-word-class-as-field-name-in-django-and-django-rest-framewor
        :param instance:
        :return:
        """
        data = super().to_representation(instance)
        keys = list(data.keys())
        keys.insert(keys.index('from_key'), 'from')
        keys.remove('from_key')
        from_key = data.pop('from_key')
        data.update({'from': from_key})
        response = OrderedDict((k, data[k]) for k in keys)
        return response

def create(self, validated_data):
        pass

def update(self, instance, validated_data):
        pass

Test case failure, here is the breakpoint

(Pdb) serializer
FacebookReactionSerializer(data={'item': 'reaction', 'verb': 'add', 'reaction_type': 'like', 'created_time': 1516183830, 'post_id': '1331351323541869_1844740022202994', 'from': {'name': 'Krittuch Onnom', 'id': '1639217166122728'}, 'parent_id': '1331351323541869_1844740022202994'}):
    item = CharField()
    verb = CharField()
    reaction_type = CharField()
    created_time = IntegerField(validators=[<rest_framework.compat.MinValueValidator object>, <rest_framework.compat.MaxValueValidator object>])
    post_id = CharField(max_length=40)
    from_key = FromSerializer():
        name = CharField()
        id = CharField()
    parent_id = CharField()
(Pdb) serializer.errors
 AssertionError: You must call `.is_valid()` before accessing `.errors`.
(Pdb) serializer.is_valid()
False
(Pdb) serializer.errors
{'from_key': ['This field is required.']}

Try 3:
Also add ‘._declared_fields[‘from_key’].

class FacebookReactionSerializer(serializers. Serializer):
    item = serializers. CharField()
    verb = serializers. CharField()
    reaction_type = serializers. CharField()
    created_time = serializers. IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(4086831600)]
    )  # Limit the maximum epoch to 2099 July 4th 7:00AM
    post_id = serializers. CharField(max_length=40)
    from_key = FromSerializer()
    parent_id = serializers. CharField()

def to_representation(self, instance):
        data = super().to_representation(instance)
        keys = list(data.keys())
        keys.insert(keys.index('from_key'), 'from')
        keys.remove('from_key')
        from_key = data.pop('from_key')
        data.update({'from': from_key})
        response = OrderedDict((k, data[k]) for k in keys)
        return response

def create(self, validated_data):
        pass

def update(self, instance, validated_data):
        pass

FacebookReactionSerializer._declared_fields["from"] = serializers. CharField(source="from_key")

Debug line:

(Pdb) serializer.is_valid()
False
(Pdb) serializer.errors
{'from_key': ['This field is required.'], 'from': ['Not a valid string.']}

I’m close, but not enough

Try 4.1:

Not working. Keep the same error. from_key remains.
I wrote from the child serializer. I have to use FromSerializer instead of CharField.

    class FacebookReactionSerializer(serializers. Serializer):
    """
    value serializer the inner most of the payload
    """
    item = serializers. CharField()
    verb = serializers. CharField()
    reaction_type = serializers. CharField()
    created_time = serializers. IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(4086831600)]
    )  # Limit the maximum epoch to 2099 July 4th 7:00AM
    post_id = serializers. CharField(max_length=40)
    from_key = FromSerializer()
    parent_id = serializers. CharField()

def create(self, validated_data):
        pass

def update(self, instance, validated_data):
        pass

FacebookReactionSerializer._declared_fields["from"] = FromSerializer(source="from_key")

I can now add from as a key. However, it does not remove the from_key from the validation logic

(Pdb) serializer
FacebookReactionSerializer(data={'item': 'reaction', 'verb': 'add', 'reaction_type': 'like', 'created_time': 1516183830, 'post_id': '1331351323541869_1844740022202994', 'from': {'name': 'Krittuch Onnom', 'id': '1639217166122728'}, 'parent_id': '1331351323541869_1844740022202994'}):
    item = CharField()
    verb = CharField()
    reaction_type = CharField()
    created_time = IntegerField(validators=[<rest_framework.compat.MinValueValidator object>, <rest_framework.compat.MaxValueValidator object>])
    post_id = CharField(max_length=40)
    from_key = FromSerializer():
        name = CharField()
        id = CharField()
    parent_id = CharField()
    from = FromSerializer(source='from_key'):
        name = CharField()
        id = CharField()
(Pdb) serializer.is_valid()
False
(Pdb) serializer.errors
{'from_key': ['This field is required.']}

Question:
What is your workaround when from (reserved word) is a key and that key is a python class property?

Solution

@cezar Thank you very much!
The solution is very simple! I was confused at first. Because the given solution is using ModelSerializer, you must place fields and couple model properties.

In my example is simple Serializer and I don’t need any of them.

From now on I will use the Serializer class to solve this kind of problem

class FacebookReactionSerializer(serializers. Serializer):
    """
    value serializer the inner most of the payload
    """
    item = serializers. CharField()
    verb = serializers. CharField()
    reaction_type = serializers. CharField()
    created_time = serializers. IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(4086831600)]
    )  # Limit the maximum epoch to 2099 July 4th 7:00AM
    post_id = serializers. CharField(max_length=40)
    parent_id = serializers. CharField()

def create(self, validated_data):
        pass

def update(self, instance, validated_data):
        pass

FacebookReactionSerializer._declared_fields["from"] = FromSerializer()

Here is my debug line

(Pdb) serializer
FacebookReactionSerializer(data={'item': 'reaction', 'verb': 'add', 'reaction_type': 'like', 'created_time': 1516183830, 'post_id': '1331351323541869_1844740022202994', 'from': {'name': 'Krittuch Onnom', 'id': '1639217166122728'}, 'parent_id': '1331351323541869_1844740022202994'}):
    item = CharField()
    verb = CharField()
    reaction_type = CharField()
    created_time = IntegerField(validators=[<rest_framework.compat.MinValueValidator object>, <rest_framework.compat.MaxValueValidator object>])
    post_id = CharField(max_length=40)
    parent_id = CharField()
    from = FromSerializer():
        name = CharField()
        id = CharField()
(Pdb) serializer.is_valid()
True

Related Problems and Solutions