P
P
Pavel Echo2021-07-26 14:53:19
Django
Pavel Echo, 2021-07-26 14:53:19

How to extract objects from a many-to-many relationship after filtering them by foreignkey?

Good afternoon! I have a problem that I can't solve, but the answer lies somewhere close. I really hope you can help me here. So, I have the first model , which describes the TV channels channels/models.py (I'll be brief, I won't show too much):

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)


There is also a second model that describes the essence of the tariff. The tariff has a certain list of channels and we give the opportunity to assign to any tariff as many channels from the TV channel model as you like (for this, obviously, you need to use the many-to-many relationship).
So, the second model is described by the following code:
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()])


I thought that such a construction could help (hello from 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'


Roughly speaking, I want to get the following picture on the interface inside the tariff object

: ['ChannelCategory_1: 'Channel_1', 'Channel_3'']
['ChannelCategory_2: 'Channel_2', 'Channel_4''], etc. etc.

But this code doesn't work, because for some reason tariff cannot call channel_list. Maybe someone faced this problem? I will be glad for any help.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
P
Pavel Echo, 2021-07-29
@Echofoe

Hooray, it worked. I don’t know how beautiful it looks or not, but inside the Tariff class, you need to describe the function:

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)

Thus, we will get the following channel_list (an example of what is pulled from my database processed by this function): On-air
channels: Carousel; OTR; TV Center; REN TV; Saved; STS; Home; TV 3; FRIDAY!; Star; World; TNT; Muz TV; First channel; Russia; Match TV; NTV; Channel 5; Russia K; Russia 24. Entertainment: Channel 8; 7TV. Children: Da Vinci. Movies and series: NTV Hit. Cognitive : Country life.
Then it remains only to work on this list and format it as you like. Hope this helps someone. If someone offers a better option, I will be very glad to get acquainted with the proposal.

D
Denis Melnikov, 2021-07-26
@Mi11er

Yes, you can pull out the channels, but you can already have groups from the channel

>>> 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: Второй канал (Основная)>]>
>>>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question