Search code examples
python-3.xdjangodjango-viewsmany-to-manyrelationship

How to pull data from a related model in a many-to-many relationship?


So, I have two applications that I want to link together in a many-to-many relationship in my project.

The first application is described by the following model.

model.py:

class ChannelCategory(models.Model):
    name = models.CharField(max_length=200, db_index=True)
    def __str__(self):
        return '%s' % self.name

class Channel(models.Model):
    category = models.ForeignKey(ChannelCategory, on_delete=models.CASCADE)
    name = models.CharField(max_length=200, db_index=True)

    class Meta:
        ordering = ['category']

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

The second application is described by the following model

class Tariff(models.Model):
    channels_list = models.ManyToManyField(Channel, blank=True, db_index=True, symmetrical=False)

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_objects(self):
    return ([str(p.name) for p in self.channels_list.all()])

Now what do I want to do? Suppose that the tariff object contains 4 channels, which have different categories, and we get approximately the following picture: tariff A has 4 channels from 2 different channel categories, for example, the "mega" tariff has

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

I do not understand how to display information on the interface correctly. I need to get this kind of information on my template:

['ChannelCategory_1: 'Channel_1', 'Channel_3'']

['ChannelCategory_2: 'Channel_2', 'Channel_4'']

I will be glad for any help, thanks in advance!

UPDATED

Why 'QuerySet' object has no attribute 'channels_list' in?

tariff = Tariff.objects.prefetch_related('channels_list')
category_channel_dict = defaultdict(list)
for channel in tariff.channels_list.all():
     category_channel_dict[channel.category.name].append(channel.name)

enter image description here


Solution

  • It worked. I don't know how it looks nice or not, but inside in the class Tariff, you need to describe the function get_channels:

    from itertools import groupby
    
    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):

    ChannelCategory_1: ChannelName_1; ChannelName_2; ChannelName_3; ChannelName_4.

    ChannelCategory_2: ChannelName_5; ChannelName_6.

    ChannelCategory_3: ChannelName_6 etc.