Search code examples
djangoforeign-keysdjango-queryset

How to return all the distinct foreign keys in a Django view?


Here is my model.py:

class Category(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=255, default='')

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


class SubCategory(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=255, default='')
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']

And my view.py:

def home(request):
    context = {
        'home_page': "active",
        'categories': SubCategory.objects.order_by('category').distinct('category').values_list('category'),
    }
    return render(request, 'candidates/home.html', context)

and my template:

<ul>
        {% for category in categories %}
            <li>{{ category }}</li>
        {% endfor %}
    </ul>

and instead of seing the Category names, I see their ID

enter image description here

How can I get their names?

Thanks


Solution

  • You should use the name of the category, so:

    def home(request):
        context = {
            'home_page': "active",
            'categories': SubCategory.objects.order_by('category__name').distinct('category__name').values_list('category__name', flat=True),
        }
        return render(request, 'candidates/home.html', context)

    That being said, querying the Subcategory does not make much sense. You can get a list of Categorys with:

    def home(request):
        context = {
            'home_page': "active",
            'categories': Category.objects.order_by('name'),
        }
        return render(request, 'candidates/home.html', context)

    Or if you want only Categorys with at least one subcategory, you can work with:

    def home(request):
        context = {
            'home_page': "active",
            'categories': Category.objects.filter(subcategory__isnull=False).order_by('name').distinct(),
        }
        return render(request, 'candidates/home.html', context)

    You can simplify the __str__ of the Category with:

    class Category(models.Model):
        name = models.CharField(max_length=50, unique=True)
        description = models.CharField(max_length=255, default='')
    
        def __str__(self):
            return f'{self.name}'

    EDIT: You can order the Categorys by the number of subcategories (in descending order) with:

    def home(request):
        context = {
            'home_page': "active",
            'categories': Category.objects.annotate(
                nsub=Count('subcategory')
            ).order_by('-nsub')
        }
        return render(request, 'candidates/home.html', context)