Flat Cookbook List API
Table of Contents
1. Context
V3 cookbook list endpoint currently returns deeply nested data — sections (containing
items with pairing_id~/~product_id), a full author object, and collections objects.
CookbookSection and CookbookCollection data models are deprecated. Frontend only needs
flat ID lists from the list view.
Goal: remove nesting from the list endpoint. Detail endpoint and underlying data models stay unchanged.
2. Response Shape Change
2.1. Before
{
"id": 1,
"slug": "...",
"author": {"id": 42, "name": "...", "slug": "...", ...},
"collections": [{"id": 1, "name": "...", ...}],
"sections": [
{
"id": 10,
"name": "...",
"position": 0,
"items": [
{"id": "pairing-101", "item_type": "pairing", "pairing_id": 101, "product_id": null},
{"id": "product-201", "item_type": "product", "pairing_id": null, "product_id": 201}
]
}
],
"pairing_count": 1,
"product_count": 1
}
2.2. After
2.2.1. Public/Thematic cookbook:
{
"id": 1,
"slug": "summer-grilling",
"name": "Summer Grilling",
"description": "Great recipes for the grill.",
"hero_image": "https://cdn.example.com/images/summer-grilling.jpg",
"is_featured": false,
"is_active": true,
"sort_order": 0,
"cookbook_type": "THEMATIC",
"visibility": "SHARED",
"owner_id": null,
"author_id": 42,
"dietary_tag_ids": [3, 7],
"pairing_ids": [101, 102],
"product_ids": [201],
"pairing_count": 2,
"product_count": 1,
"referral_code": "",
"count": 50,
"next": "https://api.example.com/api/v3/cookbooks/?page=2",
"previous": null
}
2.2.2. Shared/Favorites cookbook:
{
"id": 1,
"slug": "xjK32jal",
"name": "Colin's Favorites",
"description": "",
"hero_image": "https://cdn.example.com/images/summer-grilling.jpg",
"is_featured": false,
"is_active": true,
"sort_order": 0,
"cookbook_type": "FAVORITES",
"visibility": "SHARED",
"owner_id": null,
"author_id": 1402942,
"dietary_tag_ids": [3, 7],
"pairing_ids": [101, 102],
"product_ids": [201],
"pairing_count": 2,
"product_count": 1,
"referral_code": "customer-referral-code",
"count": 50,
"next": "https://api.example.com/api/v3/cookbooks/?page=2",
"previous": null
}
Fields removed: author (nested), sections (nested), collections (nested).
Fields added: author_id (nullable int), pairing_ids (flat list), product_ids (flat list).
3. Implementation Plan
3.1. 1. New serializer — cookbooks/serializers.py
Add CookbookListSerializer after existing CookbookSerializer.
List view gets new serializer; detail view keeps CookbookSerializer unchanged.
class CookbookListSerializer(serializers.Serializer):
# all scalar fields from CookbookSerializer
# (id, slug, name, description, hero_image, is_featured, is_active,
# sort_order, cookbook_type, visibility, kind, owner_id,
# dietary_tag_ids, pairing_count, product_count, referral_code,
# count, next, previous)
author_id = serializers.IntegerField(source="author.id", allow_null=True, default=None)
pairing_ids = serializers.SerializerMethodField()
product_ids = serializers.SerializerMethodField()
def get_pairing_ids(self, obj) -> list[int]:
return [
item.pairing_id
for section in obj.sections.all()
for item in section.items.all()
if item.pairing_id is not None
]
def get_product_ids(self, obj) -> list[int]:
return [
item.product_id
for section in obj.sections.all()
for item in section.items.all()
if item.product_id is not None
]
Uses already-prefetched sections__items — no extra queries.
3.2. 2. Wire up list view — cookbooks/views.py
In CookbookListView:
serializer_class = CookbookListSerializer(wasCookbookSerializer)- Drop
collectionsfromprefetch_relatedin the list queryset (no longer serialized). - Keep
sections__itemsprefetch (still needed forpairing_ids~/~product_ids).
3.3. 3. Update tests — cookbooks/tests/test_api.py
Update CookbookV3ReadAPITestCase list tests to assert new shape:
sectionskey absentauthor_idpresent (int or null),authorkey absentcollectionskey absentpairing_idsis a flat list of intsproduct_idsis a flat list of ints- Existing filter/count tests that don’t inspect shape still pass
4. Verification
python manage.py test cookbooks.tests.test_api.CookbookV3ReadAPITestCase
Manual check:
curl http://localhost:8000/api/v3/cookbooks/ | python -m json.tool | grep -E 'author|section|collection|pairing_ids|product_ids'
Expect: author_id, pairing_ids, product_ids present; author, sections, collections absent.