Serializer is used to take complex Python models and convert them to JSON. The serializer can also be used to deserialize JSON back into the Python model after validating incoming data.

At Sentry, we have two different types of Serializer: Django Rest Framework Serializer and Model Serializer.

Django Rest Framework

The Django Rest Framework serializer handles input validation and conversion of data entering a Sentry.

  • www.django-rest-framework.org/

The sample

In a typical Serializer, fields are specified so that they validate the type and format of the data against your specification. The Django Rest Framework serializer can also save information to the database if writing is appropriate for the Model.

from rest_framework import serializers
from sentry.api.serializers.rest_framework import ValidationError

class ExampleSerializer(serializers.Serializer) :
    name = serializers.CharField()
    age = serializers.IntegerField(required=False)
    type = serializers.CharField()

    def validate_type(self, attrs, source) :
        type = attrs[source]
        if type not in ['bear'.'rabbit'.'puppy'] :raise ValidationError('%s is not a valid type' % type)
	return attrs
Copy the code

Field inspection

In the above example, serializer will accept and validate JSON containing three fields: Name, age, and Type. Where name and type must be strings, and age must be the suggested INTEGER. By default, the field is required, and serializer will be marked as invalid if it is not provided. Note that the INTEGER field age,required is set to False. Therefore, Serializer may not be included and will still be considered valid.

Custom authentication

For values that require custom validation (other than simple type checking),

def validate_<variable_name>(self, attrs, source)

You can create where

is replaced with the exact variable name for the given field. So, for example, if I have a field name typeName, the validation method name will be validate_typeName, and if I have a field named type_name, the validation method name will be validate_type_name. In the example given above, the type is checked and must be some string. If a field does not match what your validation method expects, a ValidationError is raised.

usage

In endpoint, this is a typical use of Django Rest Framework Serializer

class ExampleEndpoint(Endpoint) :
    def post(self, request) :
        serializer = ExampleSerializer(request.DATA)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        result = serializer.object

        #Assuming Example is a model with the same fields 
        try:
            with transaction.atomic():
                Example.objects.create(
                    name=result['name'],
                    age=result.get('age'),
                    type=result['type'],)except IntegrityError:
            return Response('This example already exists', status=409)

        return Response(serialize(result, request.user), status=201)
Copy the code

Validation data

The Serializer from the Django Rest Framework will be used for methods that need to validate incoming data (that is, the PUT and POST methods). Once the serializer is instantiated, you can call serializer.is_Valid () to validate the data. Serializer. Errors will give specific feedback about the invalidity of a given data.

For example, given input

{
	'age':5.'type':'puppy'
}
Copy the code

Serializer will return an error indicating that the required field name was not provided.

Save the data

After confirming that the data is valid, you can save the data in either of the following ways. The example given above is the most common in Sentry. Take serializer. Object, which is just the validated data (if serializer.is_valid() returns False, None) and use

.objects.create to save the data directly in the model.

Another approach uses one more feature of the Django Rest Framework, ModelSerializer

from rest_framework import serializers
from sentry.api.serializers.rest_framework import ValidationError

class ExampleSerializer(serializer.ModelSerializer) :
    name = serializers.CharField()
    age = serializers.IntegerField(required=False)
    type = serializers.CharField()

    class Meta:
        model = Example
 
    def validate_type(self, attrs, source) :
        type = attrs[source]
        if type not in ['bear'.'rabbit'.'puppy'] :raise ValidationError('%s is not a valid type' % type)
        return attrs

class ExampleEndpoint(Endpoint) :
    def post(self, request) :
        serializer = ExampleSerializer(request.DATA)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        example = serializer.save()
        return Response(serialize(example, request.user), status=201)
Copy the code

Model Serializer

Sentry’s Model Serializers are only used for outgoing data. A typical Model Serializer looks like this:

  • Github.com/getsentry/s…
@register(Example)
class ExampleSerializer(Serializer) :
    def get_attrs(self, item_list, user) :
        attrs = {}
        types = ExampleTypes.objects.filter(
            type_name__in=[i.type for i in item_list]
        )

        for item in item_list:
            attrs[item] = {}
            attrs[item]['type'] = [t for t in types if t.name == item.type_name]
	    return attrs

    def serialize(self, obj, attrs, user) :
        return {
            'name':obj.name,
            'type':attrs['type'].'age': obj.age,
        }
Copy the code

The registered Model Serializer

Decorator @register is required in order to

`return Response(serialize(example, request.user), status=201) `Copy the code

Works. In this case, it searches in the background for the matching model Example, given the model type of the variable Example. To match the Model Serializer with the Model, you simply execute

@register(<ModelName>)
class ModelSerializer(Serializer) :.Copy the code

Get_attrs method

Why do this when Django Rest Framework has similar functionality? The get_attrs method is the reason. It allows you to perform batch queries instead of multiple queries. In our example, INSTEAD of calling exampletypes.objects.get (…), I can filter the items I want and assign them to the related items using Python. More than one item. In the case of the ATTR dictionary, the key is the item itself. And value is a dictionary that contains the name of the attribute to be added and its value.

attrs[item] = {'attribute_name': attribute}
Copy the code

Serialize method

Finally, you return a dictionary with JSON-serializable information, which is returned with response.

More and more

  • Sentry Enterprise level Data security solution – Relay getting started