2010-05-13 6 views
1

J'ai une longue liste de liens que je crache en utilisant le code ci-dessous, les votes totaux, soumis par, les trucs habituels mais je ne suis pas 100% sur la façon de déterminer si le utilisateur actuellement connecté a voté sur un lien ou non. Je sais comment le faire de mon point de vue, mais dois-je modifier mon code de vue ci-dessous ou puis-je utiliser la façon dont les modèles fonctionnent pour le déterminer?Django: Déterminer si un utilisateur a voté

J'ai lu Django Vote Up/Down method mais je ne comprends pas très bien ce qui se passe (et je n'ai pas besoin de javascriptery).

Modèles (snippet):

class Link(models.Model): 
    category = models.ForeignKey(Category, blank=False, default=1) 
    user = models.ForeignKey(User) 
    created = models.DateTimeField(auto_now_add=True) 
    modified = models.DateTimeField(auto_now=True) 
    url = models.URLField(max_length=1024, unique=True, verify_exists=True) 
    name = models.CharField(max_length=512) 

    def __unicode__(self): 
     return u'%s (%s)' % (self.name, self.url) 

class Vote(models.Model): 
    link = models.ForeignKey(Link) 
    user = models.ForeignKey(User) 
    created = models.DateTimeField(auto_now_add=True) 

    def __unicode__(self): 
     return u'%s vote for %s' % (self.user, self.link) 

Vues (extrait):

def hot(request): 
    links = Link.objects.select_related().annotate(votes=Count('vote')).order_by('-created') 
    for link in links: 
     delta_in_hours = (int(datetime.now().strftime("%s")) - int(link.created.strftime("%s")))/3600 
     link.popularity = ((link.votes - 1)/(delta_in_hours + 2)**1.5) 
     if request.user.is_authenticated(): 
      try: 
       link.voted = Vote.objects.get(link=link, user=request.user) 
      except Vote.DoesNotExist: 
       link.voted = None 

    links = sorted(links, key=lambda x: x.popularity, reverse=True) 

    links = paginate(request, links, 15) 

    return direct_to_template(
     request, 
     template = 'links/link_list.html', 
     extra_context = { 
      'links': links, 
     }) 

La vue ci-dessus accomplit en fait ce que je dois, mais dans ce que je crois être un moyen terriblement inefficace. Cela provoque les requêtes n + 1 redoutées, en l'état, ce sont 33 requêtes pour une page contenant seulement 29 liens alors qu'à l'origine je m'en tirais avec seulement 4 requêtes. Je préférerais vraiment faire cela en utilisant ORM de Django ou au moins .extra().

Un conseil?

EDIT

@Gabriel Hurley

Je suis en train de recréer votre réponse et je suis ont des résultats mitigés, laissez-moi montrer ya ce que je suis.

views.py

links = Link.objects.select_related().extra(
    select={ 
     'votes': 'COUNT(links_vote.id)', 
     'voted': 'SELECT COUNT(links_vote.id) FROM links_vote WHERE links_vote.user_id = 1 AND links_vote.link_id = links_link.id', 
    }, 
    tables = ['links_vote'] 
) 

models.py

class Vote(models.Model): 
    link = models.ForeignKey(Link) 
    user = models.ForeignKey(User) 
    created = models.DateTimeField(auto_now_add=True) 

    class Meta: 
     unique_together = ('link', 'user') 

    def __unicode__(self): 
     return u'%s vote for %s' % (self.user, self.link) 

Mais il retourne une erreur:

subquery uses ungrouped column "links_link.id" from outer query 

LIGNE 1: ... E = 1 links_vote.user_id AND links_vote.link_id = links_link ...

La requête qui est généré ressemble à quelque chose (exactement) comme ceci:

SELECT (SELECT COUNT(links_vote.id) FROM links_vote WHERE links_vote.user_id = 1 AND links_vote.link_id = links_link.id) AS "voted", "links_link"."id", "links_link"."category_id", "links_link"."user_id", "links_link"."created", "links_link"."modified", "links_link"."url", "links_link"."name", "links_category"."id", "links_category"."name", "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "links_link" INNER JOIN "links_category" ON ("links_link"."category_id" = "links_category"."id") INNER JOIN "auth_user" ON ("links_link"."user_id" = "auth_user"."id") , "links_vote" 

J'utilise PostgreSQL que je connais aime GROUP BY, mais je ne suis pas 100% sur la façon de corriger cette situation. ..

EDIT 2 (Major Progress)

liens = Link.objects.select_related() annoter (votes = Count ('vote')) supplémentaire ( select = {# 'voté': 'SELECT COUNT () FROM liens_vote O WH links_vote.user_id =% s AND liens_vote.link_id = liens_link.id'% (request.user.id), # 'voté': ''% (request.user.id), # 'voté': 'SELECT CASE WHEN links_vote.user_id =% s ALORS 1 ELSE 0 END'% (request.user.id), # 'voté': 'SELECT COUNT () FROM links_vote WHERE links_vote.link_id = links_link.id ET links_vote.u ser_id =% s '% (request.user.id), }, où = [' links_link.id = links_vote.link_id '], ).order_by (« - créé »)

* Cela ne fonctionne après l'application d'un correctif pour un bug d'ici (http://code.djangoproject.com/ticket/11916)

Je suis si près de trouver la dernière pièce que je dois déterminer si un utilisateur a voté. ..

Répondre

2

J'ai eu affaire à cela avant et résolu avec extra plus ou moins comme ceci:

# annotate whether you've already voted on this item 
table = Vote._meta.db_table 
select = 'SELECT COUNT(id) FROM %s' %table 
where1 = 'WHERE ' + table + '.user_id = %s' 
where2 = 'AND ' + table + '.item_id = appname_item.id' 
items = items.extra(
     select={'votes':" ".join((select, where1, where2,))}, 
     select_params=(request.user.id,) 
    ) 

Effectivement, cela prend un QuerySet d'objets et annote chacun avec un 0 ou un certain nombre de votes. Dans mon système, j'utilise unique_together = ('link', 'user') sur Vote pour m'assurer que chaque utilisateur ne peut voter qu'une seule fois, de sorte que les données annotées sont soit 0 soit 1 (en réalité booléen). Cela fonctionne plutôt bien et évite le problème n + 1.

+0

J'ai quelques questions qui ne me conviendront pas ici, voir ma modification ci-dessus. Merci quand même! Je pense que cela me met sur la bonne voie. – TheLizardKing

Questions connexes