2012-04-16 3 views
3

J'ai quelques modèles Django que les habitudes d'écoute des gens de disques (un peu comme Last.fm), comme suit:Django: Regroupement et commande à travers les clés étrangères avec des conditions

class Artist(models.Model): 
    name = models.CharField() 

class Song(models.Model): 
    artist = models.ForeignKey(Artist) 
    title = models.CharField() 

class SongPlay(models.Model): 
    song = models.ForeignKey(Song) 
    user = models.ForeignKey(User) 
    time = models.DateTimeField() 

class User(models.Model): 
    # doesn't really matter! 

Je voudrais avoir un utilisateur page où je peux montrer les meilleures chansons qu'ils ont écouté au cours du dernier mois. Quelle est la meilleure façon de faire cela?

Le meilleur que je suis venu avec à ce jour est:

SongPlay.past_month 
    .filter(user=user) 
    .values('song__title', 'song__id', 'song__artist__name') 
    .annotate(plays=Count('song')) 
    .order_by('-plays')[:20] 

Ci-dessus, past_month est un gestionnaire qui filtre juste des pièces du dernier mois. Supposons que nous ayons déjà le bon objet user à filtrer.

Je suppose que mes deux questions sont les suivantes:

  • Comment puis-je avoir accès à l'objet d'origine, ainsi que l'annotation plays?
    Cela me donne juste certaines valeurs, en fonction de ce que je passe à values. Je préférerais avoir accès à l'objet original - le modèle a des méthodes que j'aimerais appeler.

  • Comment puis-je regrouper de SongPlay à Artist? Je voudrais montrer une carte d'artistes, ainsi qu'un tableau des chansons.

Répondre

3

Vous pouvez utiliser le même champ dans les deux values et annotate.

Vous avez la clé primaire de l'objet Song (vous pouvez simplement utiliser song au lieu de song__id), donc utiliser

Song.objects.get(id=...) 

Pour votre deuxième question, une requête séparée, avec song__artist comme le champ dans values et annotate:

from django.db.models import Count 

SongPlay.past_month 
    .filter(user=user) 
    .values('song__artist') 
    .annotate(plays=Count('song__artist')) 
    .order_by('-plays')[:20] 
+0

Je pensais déjà à l'option 1, mais je me demandais s'il y avait une façon plus efficace de le faire que d'utiliser 'Song.objects.get' pour chaque ligne du résultat. –

+0

Pouvez-vous donner un exemple? Je ne peux pas obtenir 'values ​​('chanson')' et 'select_related (depth = 1)' pour me donner autre chose qu'un dictionnaire d'identification de chanson et un nombre de lectures. –

+0

@Sam Wow, je suis désolé, j'ai oublié ça. Vous pouvez seulement obtenir les champs db réels avec des valeurs, pas des objets. Lorsque vous groupez, vous n'accédez pas à la table associée, seulement au champ de clé étrangère. – agf

0

faa vous a déjà montré comment le groupe par song_artist. Ce que je ferais pour obtenir l'objet Song réel est le stocker dans memcached, ou si la méthode que vous appelez est plutôt simpliste en faire une méthode statique ou une méthode de classe. Vous pourriez également initialiser un objet Song avec les données de la requête et ne pas l'enregistrer pour accéder à cette méthode. Peut aider à connaître les détails des méthodes que vous souhaitez appeler à partir de l'objet Song.

Questions connexes