Serializers Reference
Serializers handle conversion between Django model instances and JSON-LD representations for ActivityPub federation. They work in conjunction with the framing system to produce context-appropriate output.
Core Serializer Classes
ContextModelSerializer
activitypub.serializers.linked_data.ContextModelSerializer
Bases: Serializer
Generic serializer that converts any context model to expanded JSON-LD.
Automatically uses LINKED_DATA_FIELDS from the context model.
Handles access control via optional show_
to_representation(instance)
Convert context model instance to expanded JSON-LD.
Returns dict with full predicate URIs as keys.
Serializes individual context model instances to expanded JSON-LD.
Uses the model's LINKED_DATA_FIELDS mapping to convert Django fields
to RDF predicates.
Access Control:
Serializers support optional show_<field_name>() methods for
field-level access control:
class ActorContextSerializer(ContextModelSerializer):
def show_followers(self, instance, viewer):
# Only show followers to the actor themselves
return viewer and viewer.uri == instance.reference.uri
LinkedDataSerializer
activitypub.serializers.linked_data.LinkedDataSerializer
Bases: BaseSerializer
Serializer for linked data models. Given a reference, find all the associated context models that have data and produces the merged JSON-LD.
get_compact_context(instance)
Build the @context array for JSON-LD compaction.
Collects context URLs and EXTRA_CONTEXT from context models that have data. Orders contexts as: AS2 first, other contexts, then extensions dict.
Returns:
| Type | Description |
|---|---|
|
List representing the @context array |
Main serializer that coordinates multiple context models for a reference. Automatically discovers which context models have data and merges their output.
Usage:
from activitypub.serializers import LinkedDataSerializer
serializer = LinkedDataSerializer(
instance=reference,
context={'viewer': viewer, 'request': request}
)
expanded_data = serializer.data
Integration with Framing:
The serializer produces expanded JSON-LD, which the framing system then shapes based on context:
from activitypub.frames import FrameRegistry
serializer = LinkedDataSerializer(instance=reference, context={'viewer': viewer})
frame = FrameRegistry.auto_frame(serializer) # Automatic frame selection
document = frame.to_framed_document() # Apply context-aware shaping
Collection Serializers
CollectionContextSerializer
activitypub.serializers.collections.CollectionContextSerializer
Bases: ContextModelSerializer
Specialized serializer for collection contexts. Handles pagination and item serialization.
NodeInfo Serializer
activitypub.serializers.nodeinfo.NodeInfoSerializer
Bases: Serializer
Serializes server metadata for the NodeInfo protocol.
Custom Serialization
The toolkit supports custom serializers for specific context models:
FEDERATION = {
'CUSTOM_SERIALIZERS': {
ObjectContext: CustomObjectSerializer,
}
}
Custom serializers must inherit from ContextModelSerializer and
implement the same interface:
from activitypub.serializers import ContextModelSerializer
class CustomObjectSerializer(ContextModelSerializer):
def to_representation(self, instance):
data = super().to_representation(instance)
# Add custom fields or transformations
return data
def show_sensitive_field(self, instance, viewer):
# Custom access control
return viewer and self.can_view(viewer, instance)
Serialization Pipeline
The complete serialization pipeline involves three stages:
- Serialization -
LinkedDataSerializerproduces expanded JSON-LD with full predicate URIs - Framing - Frames shape the structure based on context (omit/reference/embed decisions)
- Compaction - JSON-LD compaction produces readable output with
short keys and
@context
# In a view
serializer = LinkedDataSerializer(instance=reference, context={'viewer': viewer})
frame = FrameRegistry.auto_frame(serializer)
expanded_framed = frame.to_framed_document()
# Get compact context and compact the document
context = serializer.get_compact_context(reference)
compacted = jsonld.compact(expanded_framed, context)
This separation of concerns allows each stage to focus on its responsibility: - Serializers extract and convert data - Frames shape structure based on context - Compaction provides readability
Access Control Patterns
Field-Level Control
def show_inbox(self, instance, viewer):
# Only show inbox URL to owner
return viewer and viewer.uri == instance.reference.uri
Viewer-Aware Serialization
The viewer parameter in the context represents the authenticated
actor viewing the resource:
serializer = LinkedDataSerializer(
instance=actor_ref,
context={'viewer': requesting_actor_ref}
)
Serializers can use this to filter fields:
def show_followers(self, instance, viewer):
if not viewer:
return False # Anonymous viewers can't see followers
if viewer.uri == instance.reference.uri:
return True # Actor can see their own followers
return False # Others can't see followers
Combined with Framing
Access control happens at serialization time. Framing happens afterward and only sees fields that passed access control. This means sensitive fields are completely excluded from the document, not just hidden by framing.
Performance Considerations
Serialization involves:
- Querying context models for a reference
- Walking through LINKED_DATA_FIELDS mappings
- Resolving references for foreign keys
- Potentially recursing for embedded objects (via framing)
For high-traffic endpoints, consider:
- Caching - Cache serialized output for public resources
- Selective Loading - Use
select_relatedandprefetch_relatedwhen querying references - Depth Limits - Frame configuration controls embedding depth to prevent expensive recursion
- Lazy Evaluation - Serializers evaluate lazily; access
.dataonly when needed