Search code examples
djangomenudjango-queryset

How to obtain a queryset from unrelated models in Django


I am developing a website on car spare parts. The main model is Part, which has different parent models, such as Category and Brand.

I want to automatically generate the menu from my database. If I had one level menu, "Category", this is pretty straightforward. I just select all categories, which have at least one part in them, and pass them to the template in the context:

category_list = Category.objects.annotate(num_parts=Count('parts')).filter(num_parts__gt=0)
context['category_list'] = category_list
return context

But now I want my menu to be two-level, consisting of categories and brands:

  • Wheels
    • Toyota
    • Ford
    • Renault
  • Radiators
    • Ford
    • Mersedes Or course, I don't want to obtain empty menu options, where are no parts. Please give me a hint, how can I do this!

My models code is following:

class Category(models.Model):
    slug_name = models.CharField(
        _('Short name of the category'),
        unique = True, 
        max_length=255, 
        db_index=True
    )
    slug = AutoSlugField(
        _('Slug'),
        always_update=False,
        populate_from="slug_name",
        max_length=255,
    )
    name = models.CharField(
        _('Name'),
        max_length=255,
        db_index=True,
    )
    ...


class Brand(models.Model):
    slug_name = models.CharField(
        _('Short name of the brand'),
        max_length=255,
    )
    slug = AutoSlugField(
        _('Slug'),
        always_update=False,
        populate_from="slug_name",
        unique=True,
        max_length=255,
    )
    name = models.CharField(
        _('Brand name'),
        max_length=255,
        db_index=True,
    )
   ...


class Part(models.Model):
    slug_name = models.CharField(
        _('Short name of the part'),
        unique = True, 
        max_length=255, 
        db_index=True
    )
    slug = AutoSlugField(
        _('Slug'),
        always_update=False,
        populate_from="slug_name",
        max_length=255,
    )
    name = models.CharField(
        _('Name'),
        max_length=255,
        db_index=True,
    )
    price = models.DecimalField(
        _('Price'),
        help_text=_('Price of the part in ₽'),
        max_digits=10,
        decimal_places=2,
        db_index=True,
        blank=True,
        null=True,
    )
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        related_name='parts',
        related_query_name='parts',
        null=True,
    )
    brand = models.ForeignKey(
        Brand,
        on_delete=models.SET_NULL,
        null=True,
        related_name='parts',
        related_query_name='parts'
    )
    ...

Solution

  • Maybe something like this is what you need:

        category_list = Category.objects.annotate(num_parts=Count('parts')).filter(num_parts__gt=0)
        
        categories = []
        for category in category_list:
            categories += [{
                "category": category, //category.name if you only need the name
                "brands": Brand.objects.filter(parts__category=category) //add .values_list('name', flat=True) if you only need the name
            }]
        
        context['category_list'] = categories
        return context