|
5 | 5 | from django.contrib.auth.models import Group |
6 | 6 | from django.core.validators import RegexValidator |
7 | 7 | from django.db import models |
8 | | -from django.db.models import Case, JSONField, When, deletion |
| 8 | +from django.db.models import Case, JSONField, Prefetch, When, deletion |
9 | 9 | from django.db.models.functions import Concat |
10 | 10 | from imagekit.models import ImageSpecField, ProcessedImageField |
11 | 11 | from imagekit.processors import ResizeToFit |
@@ -47,6 +47,40 @@ def annotate_channel_url(self): |
47 | 47 | ) |
48 | 48 | ) |
49 | 49 |
|
| 50 | + def with_detail_relations(self) -> "ChannelQuerySet": |
| 51 | + """ |
| 52 | + Apply the prefetch/select_related/annotate chain needed to serialize |
| 53 | + a full ChannelSerializer without N+1 queries. |
| 54 | + """ |
| 55 | + return ( |
| 56 | + self.prefetch_related( |
| 57 | + Prefetch( |
| 58 | + "lists", |
| 59 | + queryset=ChannelList.objects.select_related( |
| 60 | + "channel_list" |
| 61 | + ).order_by("position"), |
| 62 | + ), |
| 63 | + Prefetch( |
| 64 | + "sub_channels", |
| 65 | + queryset=SubChannel.objects.select_related( |
| 66 | + "parent_channel" |
| 67 | + ).order_by("position"), |
| 68 | + ), |
| 69 | + Prefetch( |
| 70 | + "sub_channels__channel", |
| 71 | + queryset=Channel.objects.annotate_channel_url(), |
| 72 | + ), |
| 73 | + ) |
| 74 | + .annotate_channel_url() |
| 75 | + .select_related( |
| 76 | + "featured_list", |
| 77 | + "topic_detail", |
| 78 | + "department_detail", |
| 79 | + "unit_detail", |
| 80 | + "pathway_detail", |
| 81 | + ) |
| 82 | + ) |
| 83 | + |
50 | 84 |
|
51 | 85 | class Channel(TimestampedModel): |
52 | 86 | """Channel for any field/subject""" |
@@ -132,6 +166,16 @@ def channel_url(self) -> str | None: |
132 | 166 | return frontend_absolute_url(f"/c/{self.channel_type}/{self.name}/") |
133 | 167 | return None |
134 | 168 |
|
| 169 | + @property |
| 170 | + def ordered_lists(self) -> list[LearningResource]: |
| 171 | + """The channel's LearningPaths, ordered by position. |
| 172 | +
|
| 173 | + Resolves through the prefetched ``lists`` relation when available |
| 174 | + (see ``ChannelQuerySet.with_detail_relations``) so serializing a |
| 175 | + channel does not trigger an extra query per list. |
| 176 | + """ |
| 177 | + return [channel_list.channel_list for channel_list in self.lists.all()] |
| 178 | + |
135 | 179 | class Meta: |
136 | 180 | unique_together = ("name", "channel_type") |
137 | 181 |
|
|
0 commit comments