Sentry Developer Contribution Guide: Using Django REST Framework Serializers
This guide explains Sentry's two serializer types—generic DRF Serializer and ModelSerializer—showing how to define fields, perform built‑in and custom validation, use them in endpoints, save data, and leverage the @register decorator and get_attrs method for efficient batch processing.
Overview
Sentry provides two serializer classes for handling data: the generic Django Rest Framework Serializer for input validation and conversion, and the ModelSerializer used primarily for outbound data.
Django Rest Framework Serializer
A serializer extracts data from complex Python models and converts it to JSON; it can also deserialize validated JSON back into Python objects. In Sentry, a typical serializer defines fields that enforce type and format constraints.
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 attrsField checks: name and type must be strings, age must be an integer. By default fields are required; setting required=False makes a field optional while still allowing the serializer to be considered valid.
Custom validation methods follow the pattern validate_<em>field_name</em>. For a field named typeName the method would be validate_typeName; for type_name it would be validate_type_name. If validation fails, a ValidationError is raised.
Typical endpoint usage:
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
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)After validation, data can be saved either by accessing serializer.object (the validated dict) and calling the model's .objects.create, or by using the serializer’s save() method when a ModelSerializer is employed.
Model Serializer
Model serializers are used only for outbound data. They are registered with the @register decorator so that Sentry can locate the appropriate serializer for a given model.
@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,
}The get_attrs method performs a batch query (e.g., fetching all related ExampleTypes in one query) instead of issuing a separate query per item, reducing database load. It builds a dictionary where each key is an item and the value is another dictionary of attribute names and their values.
The serialize method returns a JSON‑serializable dictionary that will be included in the HTTP response.
Why Use get_attrs
When the DRF framework provides similar functionality, get_attrs is still valuable because it allows bulk retrieval of related data, avoiding repeated .get(...) calls for each item and improving performance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
