Common Patterns¶
Mobile clients requesting a minimal payload¶
GET /articles/?fields=id,title,thumbnail_url
Pair this with ALWAYS_INCLUDE to guarantee clients always get whatever
identifier your mobile app's local cache keys on, even if they forget to
ask for it:
PARTIAL_RESPONSE_FIELDS = {"ALWAYS_INCLUDE": ["id", "updated_at"]}
BFF (Backend-for-Frontend) aggregation layers¶
A BFF that stitches together several backend calls often only needs a few fields from each. Rather than maintaining a slim serializer per BFF consumer, let each consumer ask for what it needs from one full-featured serializer:
GET /internal/articles/?fields=id,title,author(id)
GraphQL-style nested resolution without GraphQL¶
If you want field-selection ergonomics but don't want to adopt a GraphQL schema and resolver layer, this package gets you most of the way there for read-heavy REST endpoints, with far less operational surface area.
Feature-flagged fields¶
Combine with a custom get_fields() override after this mixin (later in
to_representation, or by overriding get_queryset) to hide fields behind
a feature flag, independent of what the client requested:
class ArticleSerializer(PartialFieldsModelSerializer):
class Meta:
model = Article
fields = ["id", "title", "beta_summary"]
def get_fields(self):
fields = super().get_fields()
if not self.context["request"].user.has_perm("articles.beta_features"):
fields.pop("beta_summary", None)
return fields
Because PartialFieldsSerializerMixin.get_fields() already filtered by the
fields parameter, this runs on the already-narrowed field set — a
client cannot request beta_summary back into existence by asking for it
explicitly if the permission check removes it here.
Versioned field renaming during a migration¶
When renaming a model field, use aliasing to support both the old and new public names during a deprecation window without duplicating serializers:
class ArticleSerializer(PartialFieldsModelSerializer):
class Meta:
model = Article
fields = ["id", "headline"] # renamed from `title`
# Old clients, unaware of the rename:
GET /articles/?fields=title:headline
# New clients:
GET /articles/?fields=headline
Read-heavy dashboards with aggregates¶
See docs/examples.md#a-dashboard-endpoint-requesting-only-aggregate-fields
for combining annotate() with sparse fieldsets — annotated values are
recognized by the optimizer and never mistaken for relations.
Defense against overly broad requests¶
For public-facing APIs, cap nesting depth and payload size so a client can't force pathological amounts of work:
PARTIAL_RESPONSE_FIELDS = {"MAX_DEPTH": 3, "MAX_FIELDS_LENGTH": 300}
See Security for the full threat model.