Backend / Web frameworks / Django / 05_serializers_actions.md

Django serializers vs DRF serializers

Updated 3 interview angles 2 min read source
On this page4
  1. Django’s own serializers: fixtures, not APIs
  2. DRF serializers and viewset actions
  3. Related
  4. Interview angle

Django serializers vs DRF serializers

“Serializer” means two different things in a Django codebase, and mixing them up is a common interview stumble. Django’s own serializers (django.core.serializers) dump and load model instances for fixtures and data migration. DRF serializers validate API input and render API output. They share a name and nothing else.

Django’s own serializers: fixtures, not APIs

python
from django.core import serializers

data = serializers.serialize("json", Order.objects.filter(status="paid"))
for obj in serializers.deserialize("json", data):
    obj.save()                      # bypasses model validation and save() logic
bash
python manage.py dumpdata shop.Order --indent 2 > orders.json
python manage.py loaddata orders.json

The output is model-shaped ({"model": "shop.order", "pk": 7, "fields": {...}}), includes every field, and loading it calls save_base directly: no full_clean(), no custom save(), and signals receive raw=True. That is right for fixtures and test data, and wrong for an API, where you need validation, a public shape that is not your schema, and control over which fields leave the server.

Natural keys

Fixtures that refer to rows by primary key break when loaded into a database whose ids differ. Natural keys refer to rows by a unique business value instead:

python
class CountryManager(models.Manager):
    def get_by_natural_key(self, code):
        return self.get(code=code)

class Country(models.Model):
    code = models.CharField(max_length=2, unique=True)
    objects = CountryManager()

    def natural_key(self):
        return (self.code,)

dumpdata --natural-foreign --natural-primary then writes "country": ["DE"] instead of "country": 3.

DRF serializers and viewset actions

For APIs, DRF serializers do validation and conversion in both directions, and viewset actions map HTTP methods and extra endpoints onto them. They are covered in the DRF notes:

Interview angle 3

  • “What does a DRF serializer do?” - validation and conversion in both directions: parsing and validating incoming data, and rendering model instances to primitives. It is the API boundary, and where input validation belongs.
    • Follow-up: and django.core.serializers? Fixture dumping and loading: model-shaped, every field, and loading skips full_clean() and custom save(). Never an API layer.
  • “Where do you put validation?” - validate_<field> for one field, validate for cross-field rules. Business invariants that outlive the API belong in the model or a service, not only in the serializer.
  • “How do you avoid N+1 in a list endpoint?” - select_related for forward foreign keys, prefetch_related for reverse and many-to-many, applied in the viewset’s get_queryset. Nested serializers without them are the classic cause.