Django Query ForeignKeys to return a list “grouped by”

Django Query ForeignKeys to return a list “grouped by”



Something is tricking my mind here.
My mission is to get a list of Categories vs Brands, querying over Products models.



Say we got something like this:


class Category(models.Model):
name = models.CharField(max_length=100) #examples: TVs, Monitors, HomeTheaters, Fridges

class Brand(models.Model):
name = models.CharField(max_length=100) #examples: Sony, LG, Apple

class Product(models.Model):
name = models.CharField(db_index=True, max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)



What I am looking for is a query that could return me something like this:
TVs: [Sony, LG, Apple], Monitors:[Sony, Apple]
Of course it could return a queryset that I could transform into a dict later.



Since I couldn't get this done in a clean way, I did this:


categories = Category.objects.all()
products = Product.objects.select_related('category', 'brand').all()

categories_brands =
for category in categories:
categories_brands[category.name] =
for product in products:
if product.brand.name not in categories_brands[product.category.name]:
category_brands[product.category.name].append(product.brand.name)



It does work. But feels silly iterating like that. I tried "annotate" and "regroup" solutions in templatetags, but I couldn't make it work.



In addition to this question... would you have one Model class for all the products or best would be have Fridge class, TV's class and so on. I am using tags to add content like volume of a fridge and size of a monitor to filter it later. Maybe you experts can give me some advise on this as well.



Thanks in advance.



Felipe.




2 Answers
2



We can perform this in a single query, and use itertools.groupby [doc] for this:


itertools.groupby


from django.db.models import F
from itertools import groupby
from operator import itemgetter

qs = Product.objects.annotate(
category_name=F('category__name')
brand_name=F('brand__name')
).values('category_name', 'brand_name').order_by('category_name').distinct()

result =
k: [v['brand_name'] for v in vs]
for k, vs in groupby(qs, itemgetter('category_name'))



We thus first query the database for categorys and brands, and we obtain a QuerySet that contains a 'category_name' key, and a 'brand_name' key, for your sample data it would look like:


category


brand


QuerySet


'category_name'


'brand_name'


<QuerySet [
'category_name': 'Monitors', 'brand_name': 'Sony',
'category_name': 'Monitors', 'brand_name': 'Apple',
'category_name': 'TVs', 'brand_name': 'Sony',
'category_name': 'TVs', 'brand_name': 'Apple',
'category_name': 'TVs', 'brand_name': 'LG'
]>



Then we can perform a single pass over the queryset, and populate the dictionary.






Queryset worked. Also learned about F(), which is great. Transpose queryset into groupby dict failed. Returned proper keys but all of them got a list with the same values. Something like this: 'TVs': ['Sony', 'Sony', 'Consul', 'Apple', 'LG'], 'Monitors': ['Sony', 'Sony', 'Consul', 'Apple', 'LG'], 'HomeTheaters': ['Sony', 'Sony', 'Consul', 'Apple', 'LG'], Fixing it using groupby and itemgetter was still far too advance for me, so I simply iterated over the queryset to make it it work as I needed. Thanks.

– Felipe de Abreu Prazeres
Sep 14 '18 at 17:17






Sorry I made a typo, it should now be fixed.

– Willem Van Onsem
Sep 14 '18 at 17:21






Works perfect now. Thanks.

– Felipe de Abreu Prazeres
Sep 14 '18 at 17:33



Something like this ...


query = Product.objects.select_related('category', 'brand').all().query
query.group_by = ['category']
results = QuerySet(query=query, model=Product)






can you explain how this should work?

– Chiefir
Sep 14 '18 at 7:39



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)