2010-08-25 8 views
2

J'essaie de générer les données suivantes dans mes modèles django.Django: agrégation en sortie de l'agrégation ordonnée par les comptages

Les pays seraient classés en ordre décroissant par nombre d'étages. Les villes seraient ordonnées par ordre décroissant Nombre d'étages (dans ce pays)

Country A(# of stories) 
    City A (# of stories) 
    City B (# of stories) 

Country B(# of stories) 
    City A (# of stories) 
    City B (# of stories) 

Mes modèles sont les suivants:

# Create your models here. 
class Country(models.Model): 
    name = models.CharField(max_length=50) 

class City(models.Model): 
    name = models.CharField(max_length=50) 
    country = models.ForeignKey(Country) 

class Story(models.Model): 
    city = models.ForeignKey(City) 
    country = models.ForeignKey(Country) 
    name = models.CharField(max_length=255) 

Quelle est la meilleure façon de le faire?

+0

Je sais qu'il sommes plusieurs façons de le faire, mais me demandais s'il y a un moyen de tirer parti ORM de Django, comme cette réponse ici, http://stackoverflow.com/questions/ 1010848/traversing-multidimensionnel-dictionnaire-en-django/1011145 # 1011145 – Maverick

Répondre

0

Cette solution a fonctionné pour moi. Vous aurez besoin de le modifier pour le passer à un modèle si.

from django.db.models import Count 
all_countries = Country.objects.annotate(Count('story')).order_by('-story__count') 

for country in all_countries: 
    print "Country %s (%s)" % (country.name, country.story__count) 
    all_cities = City.objects.filter(country = country).annotate(Count('story')).order_by('-story__count') 
    for city in all_cities: 
     print "\tCity %s (%s)" % (city.name, city.story__count) 

Mise à jour

est une façon d'envoyer ici ces informations au modèle. Celui-ci implique l'utilisation d'un filtre personnalisé.

@register.filter 
def get_cities_and_counts(country): 
    all_cities = City.objects.filter(country = country).annotate(Count('story')).order_by('-story__count') 
    return all_cities 

Vue:

def story_counts(request, *args, **kwargs): 
    all_countries = Country.objects.annotate(Count('story')).order_by('-story__count') 
    context = dict(all_countries = all_countries) 
    return render_to_response(..., context) 

Et dans votre modèle:

{% for country in all_countries %} 
    <h3>{{ country.name }} ({{ country.story__count }})</h3> 
    {% for city in country|get_cities_and_counts %} 
     <p>{{ city.name }} ({{ city.story__count }})</p> 
    {% endfor %} 
{% endfor %} 

Mise à jour 2

variante avec une méthode personnalisée dans le modèle.

class Country(models.Model): 
    name = models.CharField(max_length=50) 

    def _get_cities_and_story_counts(self): 
     retrun City.objects.filter(country = self).annotate(Count('story')).order_by('-story__count') 
    city_story_counts = property(_get_cities_and_story_counts) 

Cela vous permet d'éviter de définir un filtre. Le code du modèle passe à:

{% for country in all_countries %} 
    <h3>{{ country.name }} ({{ country.story__count }})</h3> 
    {% for city in country.city_story_counts %} 
     <p>{{ city.name }} ({{ city.story__count }})</p> 
    {% endfor %} 
{% endfor %} 
+0

Merci, mais existe-t-il un moyen naturel de passer cela dans une structure de données que je peux travailler dans les modèles? Ou dois-je créer un dict personnalisé à ce stade? – Maverick

+0

Ajouté une façon de le faire. Cela implique un filtre. –

+0

J'aime cette réponse, mais je me demandais s'il y avait une façon plus "django" de le faire. Quelque chose comme cette réponse, http://stackoverflow.com/questions/1010848/traversing-multi-dimensional-dictionary-in-django/1011145#1011145 – Maverick