Custom Serializers¶
Every serializer in the package can be replaced via the SERIALIZERS setting.
The most common reasons to create a custom serializer are:
Adding fields from a custom model (required when swapping any model).
Customizing the
UserSerializerto expose additional user fields likeprofile_pictureordisplay_name.Changing validation logic.
Controlling which fields are read-only or write-only.
The Serializer Mixin Pattern¶
The package separates serializer logic into two layers:
Mixin (
realtime_chat_messaging.mixins.serializers) — providesSerializerMethodFielddeclarations, write-only ID fields, and shared validation (e.g. XSS sanitisation). These are reusable building blocks.Concrete serializer (
realtime_chat_messaging.serializers) — combines the mixin withModelSerializerand provides theMetaclass.
To create a custom serializer, inherit from the mixin and ModelSerializer:
from rest_framework import serializers
from realtime_chat_messaging.mixins.serializers import MessageSerializerMixin
from .models import CustomMessage
class CustomMessageSerializer(MessageSerializerMixin, serializers.ModelSerializer):
class Meta:
model = CustomMessage
fields = "__all__"
Available Mixins¶
Mixin |
Provides |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Customizing UserSerializer¶
The default UserSerializer exposes only id, username, email,
first_name, and last_name. This is intentionally minimal. To expose
additional fields (e.g. a profile_picture from a related Profile model):
# myapp/serializers.py
from django.contrib.auth import get_user_model
from rest_framework import serializers
User = get_user_model()
class ExtendedUserSerializer(serializers.ModelSerializer):
profile_picture = serializers.SerializerMethodField()
class Meta:
model = User
fields = ["id", "username", "email", "first_name", "last_name",
"profile_picture"]
def get_profile_picture(self, obj):
if hasattr(obj, "profile") and obj.profile.picture:
return obj.profile.picture.url
return None
Register it:
REALTIME_CHAT_MESSAGING = {
"SERIALIZERS": {
"UserSerializer": "myapp.serializers.ExtendedUserSerializer",
}
}
The ExtendedUserSerializer will now be used everywhere a user is nested in
the output — messages, room participants, reactions, read receipts, etc.
Customizing MessageSerializer¶
The MessageSerializer is the most commonly customized serializer when using
custom message models. Key points:
Keep the
MessageSerializerMixinin the MRO — it provides XSS sanitisation and correct relationship serialization.The
update()method in the default serializer prevents modification of immutable fields. If you override it, replicate this protection or callsuper().update().The
parent_messageandforwarded_fromfields useRecursiveFieldfor nested serialization. Include them infieldsif you need reply/forward data in responses.
from rest_framework import serializers
from django_rest_framework_recursive.fields import RecursiveField
from realtime_chat_messaging.mixins.serializers import MessageSerializerMixin
from realtime_chat_messaging.utils.loader import get_model
from .models import CustomMessage
class CustomMessageSerializer(MessageSerializerMixin, serializers.ModelSerializer):
# Preserve recursive fields from the default serializer
parent_message_id = serializers.PrimaryKeyRelatedField(
queryset=get_model("Message").objects.all(),
source="parent_message",
write_only=True,
required=False,
)
forwarded_from_id = serializers.PrimaryKeyRelatedField(
queryset=get_model("Message").objects.all(),
source="forwarded_from",
write_only=True,
required=False,
)
parent_message = RecursiveField(allow_null=True, read_only=True)
forwarded_from = RecursiveField(allow_null=True, read_only=True)
delivered_to = serializers.SerializerMethodField()
# Custom fields
priority = serializers.ChoiceField(
choices=CustomMessage.PRIORITY_CHOICES,
default="normal",
)
class Meta:
model = CustomMessage
fields = "__all__"
def get_delivered_to(self, instance):
return list(instance.delivered_to.values_list("username", flat=True))
Customizing Room Serializers¶
For custom room models with extra fields, extend the room serializer mixin:
from rest_framework import serializers
from realtime_chat_messaging.mixins.serializers import GroupChatSerializerMixin
from .models import CustomGroupChat
class CustomGroupChatSerializer(GroupChatSerializerMixin, serializers.ModelSerializer):
welcome_message = serializers.CharField(required=False, allow_blank=True)
class Meta:
model = CustomGroupChat
exclude = ["last_message"]
Register the serializer under all relevant keys (both the detail and list serializer if you also want a custom list view):
REALTIME_CHAT_MESSAGING = {
"MODELS": {
"GroupChat": "myapp.CustomGroupChat",
},
"SERIALIZERS": {
"GroupChatSerializer": "myapp.serializers.CustomGroupChatSerializer",
"GroupChatListSerializer": "myapp.serializers.CustomGroupChatListSerializer",
},
}
Validation and extra_fields¶
The room.create and message.send events support an extra_fields
dictionary that is merged with the main payload before serializer validation.
Any custom fields you add to your model and serializer can be passed via
extra_fields from the client, and they will be validated by your serializer
automatically.
{
"event_type": "message.send",
"data": {
"room_id": "<uuid>",
"content": "Urgent issue!",
"extra_fields": {
"priority": "urgent",
"is_pinned": false
}
}
}
Warning
Be careful with message.modify (update action). The default
MessageSerializer.update() only allows content to be updated —
all other fields are explicitly discarded regardless of what the client sends.
If you add custom fields to your serializer that should be updatable, they will only take effect if they are not among the default non-updatable fields. These are:
sender, room, parent_message, forwarded_from,
is_forwarded, is_deleted, created_at, updated_at
Note that relational fields (sender, room, parent_message,
forwarded_from) are represented via PrimaryKeyRelatedField in the
default serializer, meaning they arrive in request data as sender_id,
room_id, parent_message_id, and forwarded_from_id respectively.
Keep this in mind when deciding which field names to guard against in a
custom update() override.
Override update() in your custom serializer to explicitly handle any
additional updatable fields, and be careful not to expose sensitive or
structurally immutable fields to client modification.