Enhancement: Optimize channel filtering in StreamViewSet using Count annotation for improved performance on large datasets.

This commit is contained in:
SergeantPanda 2026-01-17 17:55:12 -06:00
parent 1afb15fb63
commit f810142493
2 changed files with 67 additions and 62 deletions

View file

@ -8,6 +8,7 @@ from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from django.shortcuts import get_object_or_404, get_list_or_404
from django.db import transaction
from django.db.models import Count
from django.db.models import Q
import os, json, requests, logging
from urllib.parse import unquote
@ -148,7 +149,8 @@ class StreamViewSet(viewsets.ModelViewSet):
unassigned = self.request.query_params.get("unassigned")
if unassigned == "1":
qs = qs.filter(channels__isnull=True)
# Use annotation with Count for better performance on large datasets
qs = qs.annotate(channel_count=Count('channels')).filter(channel_count=0)
channel_group = self.request.query_params.get("channel_group")
if channel_group:

View file

@ -397,70 +397,73 @@ const StreamsTable = ({ onReady }) => {
}));
};
const fetchData = useCallback(async ({ showLoader = true } = {}) => {
if (showLoader) {
setIsLoading(true);
}
const params = new URLSearchParams();
params.append('page', pagination.pageIndex + 1);
params.append('page_size', pagination.pageSize);
// Apply sorting
if (sorting.length > 0) {
const columnId = sorting[0].id;
// Map frontend column IDs to backend field names
const fieldMapping = {
name: 'name',
group: 'channel_group__name',
m3u: 'm3u_account__name',
};
const sortField = fieldMapping[columnId] || columnId;
const sortDirection = sorting[0].desc ? '-' : '';
params.append('ordering', `${sortDirection}${sortField}`);
}
// Apply debounced filters
Object.entries(debouncedFilters).forEach(([key, value]) => {
if (value) params.append(key, value);
});
try {
const [result, ids, filterOptions] = await Promise.all([
API.queryStreamsTable(params),
API.getAllStreamIds(params),
API.getStreamFilterOptions(params),
]);
setAllRowIds(ids);
// Set filtered options based on current filters
setGroupOptions(filterOptions.groups);
setM3uOptions(
filterOptions.m3u_accounts.map((m3u) => ({
label: m3u.name,
value: `${m3u.id}`,
}))
);
if (initialDataCount === null) {
setInitialDataCount(result.count);
const fetchData = useCallback(
async ({ showLoader = true } = {}) => {
if (showLoader) {
setIsLoading(true);
}
// Signal that initial data load is complete
if (!hasSignaledReady.current && onReady) {
hasSignaledReady.current = true;
onReady();
}
} catch (error) {
console.error('Error fetching data:', error);
}
const params = new URLSearchParams();
params.append('page', pagination.pageIndex + 1);
params.append('page_size', pagination.pageSize);
hasFetchedOnce.current = true;
if (showLoader) {
setIsLoading(false);
}
}, [pagination, sorting, debouncedFilters, onReady]);
// Apply sorting
if (sorting.length > 0) {
const columnId = sorting[0].id;
// Map frontend column IDs to backend field names
const fieldMapping = {
name: 'name',
group: 'channel_group__name',
m3u: 'm3u_account__name',
};
const sortField = fieldMapping[columnId] || columnId;
const sortDirection = sorting[0].desc ? '-' : '';
params.append('ordering', `${sortDirection}${sortField}`);
}
// Apply debounced filters
Object.entries(debouncedFilters).forEach(([key, value]) => {
if (value) params.append(key, value);
});
try {
const [result, ids, filterOptions] = await Promise.all([
API.queryStreamsTable(params),
API.getAllStreamIds(params),
API.getStreamFilterOptions(params),
]);
setAllRowIds(ids);
// Set filtered options based on current filters
setGroupOptions(filterOptions.groups);
setM3uOptions(
filterOptions.m3u_accounts.map((m3u) => ({
label: m3u.name,
value: `${m3u.id}`,
}))
);
if (initialDataCount === null) {
setInitialDataCount(result.count);
}
// Signal that initial data load is complete
if (!hasSignaledReady.current && onReady) {
hasSignaledReady.current = true;
onReady();
}
} catch (error) {
console.error('Error fetching data:', error);
}
hasFetchedOnce.current = true;
if (showLoader) {
setIsLoading(false);
}
},
[pagination, sorting, debouncedFilters, onReady]
);
// Bulk creation: create channels from selected streams asynchronously
const createChannelsFromStreams = async () => {