2017-10-19 5 views
1

J'ai 3 tables:Django requête ForeignKey Count() zéro

Camion avec les champs: id, name ....

Menu avec les champs: id, itemname, id_foodtype, id_truck ...

Foodtype avec les champs: id, type ...

Je veux obtenir un résumé comme:

id name    total 
10 Alcoholic drink 0 
5 Appetizer   11 

Mon problème est de retour les résultats avec 0 éléments.

J'ai essayé une requête SQL comme ceci:

SELECT 
    ft.id, ft.name, COUNT(me.id) total 
FROM 
    foodtype ft LEFT JOIN menu me 
     ON ft.id = me.id_foodtype 
    LEFT JOIN truck tr 
     ON tr.id = me.id_truck AND tr.id = 3 
GROUP BY ft.id, ft.name 
ORDER BY ft.name 

ou une requête dans Django

Menu.objects.filter(id_truck=3).values("id_foodtype").annotate(cnt=Count("id_foodtype")) 

Mais, ni affiche les résultats avec Zero éléments.

Au moment de convertir cette requête en code Python, toutes mes requêtes renvoient le résultat exact auquel je m'attendais. Comment puis-je retourner des résultats avec le Left Join, y compris les foodtypes avec zéro éléments du menu?

+0

c'est plus une question de sql-postgres que django –

+0

J'ai édité votre question pour être plus facile à chercher, dans l'espoir que c'est acceptable. J'ai également édité ma réponse un peu. – hynekcer

Répondre

0

La direction de LEFT JOIN dépend de l'objet, où vous commencez la requête. Si elle commence sur Menu vous ne verrez jamais un FoodType utilisé par des éléments de menu sélectionnés. Puis est important de filtrer (par camion dans votre cas) de telle sorte que même valeur nulle Menu.id est autorisée pour peut obtenir Count == 0.

from django.db.models import Q 
qs = (
    FoodType.objects 
    .filter(Q(menu_set__id_truck=3) | Q(menu_set__id__isnull=True)) 
    .values() # not necessary, but useful if you want a dict, not a Model object 
    .annotate(cnt=models.Count("menu_set__id")) 
) 

Vérifier:

>>> print(str(qs.query)) 
SELECT foodtype.id, foodtype..., COUNT(menu.id) AS cnt 
FROM foodtype 
LEFT OUTER JOIN menu ON (foodtype.id = menu.id_foodtype) 
WHERE _menu.id_truck = 3 OR menu.id IS NULL) 
GROUP BY foodtype.id 

Il fonctionne avec le plus récent et le plus ancien en cours Django 2.0b1 et 1,8.

La requête est la même avec ou sans la ligne .values(). Les résultats sont des dictionnaires ou des objets FoodType avec un attribut cnt.


Notes:
Le nom menu_set devrait être remplacé par le vrai related_name de clé étrangère id_foodtype si vous avez défini le related_name.

class Menu(models.Model): 
    id_foodtype = models.ForeignKey('FoodType', on_delete=models.DO_NOTHING, 
            db_column='id_foodtype', related_name='menu_set')) 
    ... 

Si vous démarrez un nouveau projet, je recommande de renommer la clé étrangère à un nom sans « id » et le champ db_column est avec « id ».Puis menu_item.foodtype est un objet Food et menu_item.id_foodtype son id.