Skip to content

Fix: Preserve stream order in ChannelSerializer PATCH/PUT responses#659

Merged
SergeantPanda merged 1 commit intoDispatcharr:devfrom
FiveBoroughs:fix/channel-stream-order-serialization
Nov 19, 2025
Merged

Fix: Preserve stream order in ChannelSerializer PATCH/PUT responses#659
SergeantPanda merged 1 commit intoDispatcharr:devfrom
FiveBoroughs:fix/channel-stream-order-serialization

Conversation

@FiveBoroughs
Copy link
Contributor

Problem

The ChannelSerializer.to_representation() method was not respecting the ChannelStream.order field when serializing PATCH/PUT responses. This caused streams to be returned in an arbitrary order rather than the order specified in the request.

While the update() method correctly saves the stream order to the database using the ChannelStream.order field, and GET requests (with include_streams=True) correctly return ordered streams via get_streams(), standard PATCH/PUT responses were using PrimaryKeyRelatedField which doesn't respect the ordering.

Solution

This PR modifies the to_representation() method to ensure that all API responses (GET, PATCH, PUT) return streams ordered by the channelstream__order field.

Changes

  • Modified ChannelSerializer.to_representation() to query streams with .order_by('channelstream__order') for standard responses
  • Preserved existing behavior for include_streams=True cases

Impact

  • PATCH/PUT responses now correctly reflect the stream order as saved
  • Clients can trust the response data without needing a follow-up GET request
  • No breaking changes - only fixes inconsistent behavior

Testing

Tested with:

  • PATCH request with ordered stream IDs
  • Verified response matches request order
  • Verified GET request confirms order persisted to database
  • Production testing with StreamFlow integration confirms fix works correctly

The ChannelSerializer.to_representation() method was not respecting the
ChannelStream.order field when serializing PATCH/PUT responses. This
caused streams to be returned in an arbitrary order rather than the
order specified in the request.

The update() method correctly saves the stream order to the database
using the ChannelStream.order field, and GET requests (with
include_streams=True) correctly return ordered streams via
get_streams(). However, standard PATCH/PUT responses were using
PrimaryKeyRelatedField which doesn't respect the ordering.

This fix ensures that all representations (GET, PATCH, PUT) return
streams ordered by the channelstream__order field.

Impact:
- PATCH/PUT responses now correctly reflect the stream order saved
- Clients can trust the response data without needing a follow-up GET
- No breaking changes - only fixes inconsistent behavior

Tested with:
- PATCH request with ordered stream IDs
- Verified response matches request order
- Verified GET request confirms order persisted to database
@SergeantPanda
Copy link
Collaborator

I'm curious, what is the use-case for the api you're fixing? Or are you fixing a bug with this change?

@FiveBoroughs
Copy link
Contributor Author

Error Details

Discovery

This issue was discovered when using StreamFlow to automatically analyze and order channel streams by quality. StreamFlow sends PATCH requests to reorder streams based on quality metrics, then verifies the order was applied correctly.

Actual Error Messages from StreamFlow

2025-11-15 23:18:47,290 - INFO - 🔍 DEBUG PATCH: Sending to http://localhost:9191/api/channels/channels/22/
2025-11-15 23:18:47,290 - INFO - 🔍 DEBUG PATCH: Payload streams order: [4088, 4077, 9427, 4066]...
2025-11-15 23:18:47,400 - INFO - 🔍 DEBUG PATCH: Response streams order: [4066, 9427, 4088, 4077]...
2025-11-15 23:18:48,009 - WARNING - ⚠ Verification failed: Stream order mismatch for channel
2025-11-15 23:18:48,009 - WARNING - Expected: [4088, 4077, 9427, 4066]... Got: [4066, 9427, 4088, 4077]...

What Was Happening

  1. StreamFlow sends PATCH with ordered stream IDs: [4088, 4077, 9427, 4066]
  2. Dispatcharr PATCH response returns: [4066, 9427, 4088, 4077] (scrambled)
  3. StreamFlow's verification GET request returns: [4066, 9427, 4088, 4077] (confirms scrambled)
  4. Result: Verification fails because response doesn't match the requested order

Root Cause

The to_representation() method was using PrimaryKeyRelatedField which serializes streams without respecting the ChannelStream.order field. While the update() method correctly saves the order to the database, the PATCH response would return stream IDs in whatever order Django's ORM fetched them (likely by primary key).

After the Fix

2025-11-16 22:15:48,465 - INFO - 🔍 DEBUG PATCH: Payload streams order: [4077, 4088, 9427, 4066]...
2025-11-16 22:15:48,747 - INFO - 🔍 DEBUG PATCH: Response streams order: [4077, 4088, 9427, 4066]...
2025-11-16 22:15:49,489 - INFO - ✓ Verified: Channel streams reordered correctly

The PATCH response now matches the payload exactly, and verification passes.

@SergeantPanda
Copy link
Collaborator

Ok thank you for explaining it. That makes sense to me and assumed it was something like that but was curious for sure.

@SergeantPanda SergeantPanda changed the base branch from main to dev November 16, 2025 23:34
@SergeantPanda
Copy link
Collaborator

I made some changes to the code to avoid slowness during bulk channel actions and querying channels (which is used in the channel table)
Please test this and see if it solves the issue you're having.

@FiveBoroughs
Copy link
Contributor Author

Test Results

I tested the updated code with the following API calls:

  1. GET /api/channels/channels/22/?include_streams=true - Returns: [4088, 4077, 9427, 4066]
  2. GET /api/channels/channels/22/ (without include_streams) - Returns: [9427, 4088, 4077, 4066]

Both calls made within seconds of each other on the same channel. The first query (with include_streams=true) uses the get_streams() method which explicitly queries with .order_by("channelstream__order"). The second query uses to_representation() with the prefetch check.

The issue is that cached_streams (populated from _prefetched_objects_cache) doesn't contain streams ordered by channelstream__order. When the cache exists, the code returns those unordered streams directly.

This also affects PATCH operations:

  • Send: PATCH /api/channels/channels/22/ with {"streams": [4088, 4077, 9427, 4066]}
  • PATCH response correctly shows: [4088, 4077, 9427, 4066]
  • Subsequent GET /api/channels/channels/22/ returns: [9427, 4088, 4077, 4066] (wrong order)

The database IS storing the correct order (confirmed by the include_streams=true query), but the cached streams don't reflect the channelstream__order relationship.

@FiveBoroughs
Copy link
Contributor Author

Additional Testing & Thoughts

I've been testing different approaches and wanted to share some observations that might be useful.

The prefetch cache optimization makes sense for bulk operations, but in practice the cache doesn't contain streams ordered by channelstream__order. This means the cached data returns incorrect ordering even though the database has the right order.

I've implemented a workaround in StreamFlow by using ?include_streams=true which bypasses this issue entirely, so this isn't blocking me anymore. But for the long-term health of the API, I'm wondering if the simpler approach (always querying with .order_by()) might be more maintainable.

The performance difference is probably minimal - it's one additional query per channel serialization, which in most use cases (individual channel updates, single channel retrievals) won't be noticeable. And it guarantees correctness without needing to manage prefetch ordering.

Just food for thought - happy to test whatever approach you think is best for Dispatcharr's architecture.

@SergeantPanda
Copy link
Collaborator

Perfect yes thank you, I figured using ?include_stream=true would be best but I do agree I think this will strike on OK balance of performance and usability.

I always want to make sure we aren't causing code that would slow down the web ui or other functions.

If you could verify the latest changes I'd appreciate it.

@SergeantPanda
Copy link
Collaborator

Actually, mind just resubmitting since we are just going to use your original code anyway? Sorry I way over thought this one and sent you down a rabbit hole.

@FiveBoroughs FiveBoroughs force-pushed the fix/channel-stream-order-serialization branch from 9c4ae6a to bbe1f63 Compare November 19, 2025 21:13
@FiveBoroughs
Copy link
Contributor Author

Done - reverted to the original simple fix. Ready for review.

@SergeantPanda SergeantPanda merged commit 204a5a0 into Dispatcharr:dev Nov 19, 2025
@SergeantPanda
Copy link
Collaborator

Perfect. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants