Django REST Framework: Read-only and Write-only Fields in Serializers

Last updated 1 month, 2 weeks ago | 130 views 75     5

Tags:- Python Django DRF

When building APIs using Django REST Framework (DRF), it's important to control how data is exposed and accepted. You may want certain fields to be visible in the response but not accepted in a request — or vice versa.

That’s where read-only and write-only fields come in.


What Are Read-only and Write-only Fields?

  • Read-only fields:
    These appear in serialized output (e.g., GET responses) but are ignored in input (e.g., POST/PUT requests).

  • Write-only fields:
    These are accepted in incoming data (POST/PUT requests) but are excluded from responses (GET responses).


Why Use Them?

Here are some common use cases:

Scenario Field Type
Auto-generated fields (like id, created_at) read_only=True
Password fields write_only=True
Metadata like author, created_by that shouldn’t be set manually read_only=True
Sensitive input (e.g., token, security_answer) write_only=True

How to Define Read-only/Write-only Fields

Method 1: Field arguments

from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField(write_only=True)
    date_joined = serializers.DateTimeField(read_only=True)

Method 2: extra_kwargs in ModelSerializer

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'password', 'date_joined']
        extra_kwargs = {
            'password': {'write_only': True},
            'date_joined': {'read_only': True}
        }

Example: Register Serializer with Password

class RegisterSerializer(serializers.Serializer):
    username = serializers.CharField()
    email = serializers.EmailField()
    password = serializers.CharField(write_only=True)
    token = serializers.CharField(read_only=True)

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password']
        )
        user.token = "generated-token"
        return user

Response:

{
  "username": "john",
  "email": "[email protected]",
  "token": "generated-token"
}

Request:

{
  "username": "john",
  "email": "[email protected]",
  "password": "securepassword"
}

Note: The password is accepted in the request but not returned. The token is returned but not required in the request.


Tips & Best Practices

  • ✅ Use write_only=True for any sensitive input (like passwords, PINs, etc.).

  • ✅ Use read_only=True for fields the client should not modify (like id, timestamps).

  • ✅ Combine extra_kwargs and direct field definitions as needed.

  • ✅ Don't return sensitive information — validate but keep it private.


⚠️ Common Pitfalls

Problem Solution
Trying to write to a read-only field Remove it from the incoming data
Expecting write-only fields to appear in responses Don’t — they’re intentionally hidden
Forgetting to return the field in create() or update() Ensure values like tokens are set and returned manually

Final Thoughts

Using read_only and write_only fields in DRF serializers gives you precise control over how data flows in and out of your API. It helps protect sensitive information, prevents misuse of API endpoints, and keeps your application logic clean and secure.

Whether you're handling user registrations, passwords, tokens, or metadata — mastering these serializer options is essential for robust API design.