Echofoe
@Echofoe
Веб-разработчик

Как из отношения many-to-many вытянуть объекты, предварительно отфильтровав их по отношению foreignkey?

Добрый день! У меня возникла проблема, которую никак не получается решить, но ответ лежит где-то близко. Я очень надеюсь, что здесь мне помогут. Итак, у меня есть первая модель, которая описывает каналы телевидения channels/models.py (буду краток, лишнего не показываю):
class ChannelCategory(models.Model):
    name = models.CharField(max_length=200, db_index=True, verbose_name='Наименование категории канала',
                            help_text='Например, "Федеральные каналы", "Детские каналы", "Спорт" и т.д.')
    slug = models.SlugField(max_length=200, db_index=True, unique=True, verbose_name='Уникальная строка')

    def __str__(self):
        return '%s' % self.name


class Channel(models.Model):
   category = models.ForeignKey(ChannelCategory, on_delete=models.CASCADE, verbose_name='Категория канала')
    name = models.CharField(max_length=200, db_index=True, verbose_name='Наименование канала')
    
    class Meta:
        ordering = ['category']

    def __str__(self):
        return '%s (%s)' % (self.name, self.category)


Также есть вторая модель, которая описывает сущность тарифа. У тарифа есть определенный список каналов и даем возможность присвоить какому-либо тарифу сколько угодно каналов из модели каналов телевидения (для этого очевидно нужно использовать отношение многие-ко-многим).
Итак, вторая модель описывается следующим кодом:
from channels.models import Channel, ChannelCategory

class Tariff(models.Model):
    name = models.CharField(max_length=200, db_index=True, verbose_name='Наименование тарифа',
                            help_text='Укажите наименование тарифа (поле обязательное)')
    slug = models.SlugField(max_length=200, db_index=True, verbose_name='Уникальная строка',)
    channels_list = models.ManyToManyField(Channel, blank=True, db_index=True, symmetrical=False,
                                           verbose_name='Список каналов')

    class Meta:
        verbose_name = 'Тариф'
        verbose_name_plural = 'Тарифы'

    def __str__(self):
        return '%s' % self.name

    def get_channels_categories(self):
        return ([str(p.category) for p in self.channels_list.all()])

    def get_channels(self):
        pp = [str(p.category) for p in self.channels_list.all()]
        return (OrderedDict.fromkeys(pp))

    def get_channels_objects(self):
        return ([str(p.name) for p in self.channels_list.all()])


Я думал, что такая конструкция сможет помочь (привет со stackoverflow):
from collections import defaultdict

tariff = Tariff.objects.prefetch_related('channels_list').filter(id=1)
category_channel_dict = defaultdict(list)

for channel in tariff.channels_list.all():
     category_channel_dict[channel.category.name].append(channel.name)


Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'channel_list'


Грубо говоря, я хочу внутри объекта тариф получить на интерфейсе такую картину:

['ChannelCategory_1: 'Channel_1', 'Channel_3'']
['ChannelCategory_2: 'Channel_2', 'Channel_4''] и т.д. и т.п.

Но этот код не работает, потому что tariff почему-то не может вызвать channel_list. Может кто-нибудь сталкивался с данной проблемой? Я буду рад любой помощи.
  • Вопрос задан
  • 1252 просмотра
Решения вопроса 1
Echofoe
@Echofoe Автор вопроса
Веб-разработчик
Ура, получилось. Не знаю, как это выглядит красиво или нет, но в внутри класса Tariff нужно описать функцию:
def get_channels(self):
    channels = list(str(p).split(':') for p in self.channels_list.all())
    channel_list = []
    for category, name in groupby(channels, lambda x: x[0]):
        channels_list_by_group = ";".join([channel[1] for channel in name])
        channels_list_by_category = (category + ":" + channels_list_by_group + ".")
        print(channels_list_by_category)
        channel_list.append(channels_list_by_category)
    return ''.join(channel_list)

Таким образом, мы получим следующий channel_list (пример того, что тянется из моей БД обработанной данной функцией):
Эфирные каналы: Карусель; ОТР; ТВ Центр; РЕН ТВ; Спас; СТС; Домашний; ТВ 3; ПЯТНИЦА!; Звезда; Мир; ТНТ; Муз ТВ; Первый канал; Россия; Матч ТВ; НТВ; 5 канал; Россия К; Россия 24. Развлекательные: 8 канал; 7TV. Детские: Da Vinci. Кино и сериалы: НТВ Хит. Познавательные: Загородная жизнь.

Дальше остается только поработать над этим списком и форматировать как вздумается. Надеюсь кому-нибудь это поможет. Если кто-то предложит вариант получше, буду очень рад ознакомиться с предложением.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Mi11er
@Mi11er
A human...
Да вроде каналы вы можете вытащить, а с канала можете уже и группы
>>> from core.models import *
>>> t1 = Tariff.objects.get(pk=1)
>>> t1.channels_list
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x000002C0AA1D7580>
>>> t1.channels_list.all()
<QuerySet [<Channel: Первый канал (Основная)>, <Channel: Второй канал (Основная)>]>
>>>
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы