Dispatcharr/apps/channels/managers.py

55 lines
1.9 KiB
Python

"""
Queryset helpers that resolve effective Channel field values.
Each Channel can optionally have a related ChannelOverride row carrying user
edits to any subset of its user-facing fields. Sync never touches the override
row; provider metadata flows directly into Channel.* and the override table
sits alongside with a nullable value per field. The helpers here coalesce the
two sources into `effective_*` annotations so output querysets can sort,
filter, and emit values correctly at SQL level (avoiding 23+ Python-side
resolutions across the codebase).
"""
from django.db.models.functions import Coalesce
OVERRIDABLE_FIELDS = (
"name",
"channel_number",
"channel_group_id",
"logo_id",
"tvg_id",
"tvc_guide_stationid",
"epg_data_id",
"stream_profile_id",
)
def with_effective_values(queryset, select_related_fks=False):
"""
Annotate the channels queryset with `effective_*` columns that resolve to
the override value when set, otherwise fall back to the channel's own
value. Always eagerly loads the override one-to-one to avoid N+1 when the
caller reads annotated attributes and then the related override.
Pass `select_related_fks=True` when the output path will access FK objects
through the `effective_*_obj` Channel properties; this pulls the override's
logo, channel_group, epg_data, and stream_profile in the same query so
those accessors do not trigger per-row lookups.
"""
annotations = {
f"effective_{field}": Coalesce(
f"override__{field}",
field,
)
for field in OVERRIDABLE_FIELDS
}
qs = queryset.select_related("override").annotate(**annotations)
if select_related_fks:
qs = qs.select_related(
"override__logo",
"override__channel_group",
"override__epg_data",
"override__stream_profile",
)
return qs