2012-06-17 8 views
5

È possibile unire programmaticamente due tabelle utilizzando l'ORM di Django? Ho due modelli: argomenti e voti. Sul mio modello ho un elenco di argomenti che gli utenti possono votare su e giù come Reddit. Tutto funziona come dovrebbe, tranne per ordinare i risultati. Non riesco a capire come ordinare un elenco di oggetti in base al punteggio, che è una somma del conteggio dei voti di ciascun oggetto. Posso recuperare i dati desiderati da Postgres senza alcun problema:Come si uniscono due tabelle usando Django senza usare raw sql?

select i.id, i.title, i.date_created, s.object_id, s.vote, Sum(vote) 
from topic_topic i, votes s 
where i.id = s.object_id 
group by 1, 2, 3, 4, 5 
order by sum DESC; 

Esso restituisce i risultati desiderati:

id | title |   date_created   | object_id | vote | sum 

11 | sdfg | 2012-06-04 23:30:17.805671-07 |  11 | 1 | 2 

1 | test | 2012-05-13 17:03:24.206092-07 |   1 | 1 | 2 

3 | asdf | 2012-05-13 19:23:15.059135-07 |   3 | 1 | 2 

2 | adsf | 2012-05-13 19:21:34.180905-07 |   2 | 1 | 2 

12 | 11  | 2012-06-04 23:30:54.759158-07 |  12 | 1 | 2 

9 | asfd | 2012-05-24 00:26:26.705843-07 |   9 | -1 | -1 

4 | asdf | 2012-05-14 19:59:52.450693-07 |   4 | -1 | -2 

Il problema è che io non sono sicuro di come recuperare questo come un set di query. Al momento sto usando la seguente per visualizzare gli oggetti:

topic_list = Topic.objects.all() 

Tutto display come vorrei a, tranne che per il tipo di ordinamento. Vorrei il punteggio più alto da visualizzare per primo.

Risorse ho già guardato: https://docs.djangoproject.com/en/dev/topics/db/managers/#adding-extra-manager-methods
How to query as GROUP BY in django?

E molti di più, ma come un nuovo utente, anti-spam mi impedisce di aggiungere loro.

di cosa ho provato:

Catena:

listed_links = list(chain(topic, score)) 

Purtroppo, se ho cercato di aggiungere un valore ordinata questo si è rotto.

Combinare elenchi di oggetti:

topic = Topic.objects.all().values_list('user','id', 'title','slug', 'date_created', 'date_updated',) 

score = Vote.objects.values('object_id').annotate(total=Sum('vote')).order_by('-total') 

results = [] 

for topic in topic: 
results.append(topic) 

for score in score: 
results.append(topic) 

Questo ha portato tutti gli oggetti che ho voluto in una sola lista, ma non riuscivo a capire come collegare topic.id-score.object_id.

Ho anche provato a inserire SQL raw, ma non mi sembra di farlo correttamente e potrei causare l'SQL injection da parte di terzi.

Mi piacerebbe condividere i risultati di questo ritorno al progetto di voto di Django. Come ho detto, tutto funziona come dovrebbe, tranne che non riesco a capire come ordinare per punteggio.

============= Le votazioni ========================

from django.contrib.contenttypes import generic 

from django.contrib.contenttypes.models import ContentType 

from django.contrib.auth.models import User 

from django.db import models 

from voting.managers import VoteManager 

from voting.VotedObjectsManager import VotedObjectsManager 

    SCORES = (
    (+1, u'+1'), 
    (-1, u'-1'), 
) 

class Vote(models.Model): 

    """ 
    A vote on an object by a User. 
    """ 

    user   = models.ForeignKey(User) 

    content_type = models.ForeignKey(ContentType) 

    object_id = models.PositiveIntegerField() 

    object  = generic.GenericForeignKey('content_type', 'object_id') 

    vote   = models.SmallIntegerField(choices=SCORES) 

    objects  = VoteManager() 


    class Meta: 
     db_table = 'votes' 
     # One vote per user per object 
     unique_together = (('user', 'content_type', 'object_id'),) 

    def __unicode__(self): 
     return u'%s: %s on %s' % (self.user, self.vote, self.object) 

    def is_upvote(self): 
     return self.vote == 1 

    def is_downvote(self): 
     return self.vote == -1 

============= topic Modello ========================

from django.db import models 

from datetime import datetime 

from tinymce import models as tinymce_models 

from django.forms import ModelForm 

from django.template.defaultfilters import slugify 

from tagging.fields import TagField 

from tagging.models import Tag 

from django.contrib.auth.models import User 

from django.utils.translation import ugettext_lazy as _ 

from django.contrib.contenttypes.models import ContentType 

from django.contrib.contenttypes import generic 

from django.core import urlresolvers 

    class Topic(models.Model): 

    title   = models.CharField(max_length=50) 

    slug   = models.SlugField(max_length=50, editable=False) 

    topic   = tinymce_models.HTMLField() 

    date_created = models.DateTimeField(editable=False) 

    date_updated = models.DateTimeField(editable=False) 

    tags   = TagField() 


    def set_tags(self, tags): 
     Tag.objects.update_tags(self, tags)  

    def __unicode__(self): 
     return self.tags 

    def __unicode__(self): 
     return self.id 

    def __unicode__(self): 
     return self.title 
+0

I modelli sono ...? –

+0

Ho aggiunto i modelli. Grazie per il rapido feedback. – user1462141

risposta

3

ero in grado di capire la soluzione con un patch come descritto qui:

http://code.google.com/p/django-voting/issues/detail?id=10

La differenza è che ho estratto le seguenti linee:

def select_score(self): 
    """ Add vote scores for objects in resoultset """ 
    from django.contrib.contenttypes.models import ContentType 
    model_type = ContentType.objects.get_for_model(self.model) 
    table_name = self.model._meta.db_table 
    print type(model_type) 
    print model_type.id 
    return self.extra(select={'score': 'SELECT SUM(vote) FROM votes WHERE content_type_id=%i AND object_id=%s.id' % (int(model_type.id), table_name)}) 

e aggiunti alla voting/managers.py file in questo modo:

class VoteManager(models.Manager): 
def get_score(self, obj): 
    """ 
    Get a dictionary containing the total score for ``obj`` and 
    the number of votes it's received. 
    """ 
    ctype = ContentType.objects.get_for_model(obj) 
    result = self.filter(object_id=obj._get_pk_val(), 
         content_type=ctype).extra(
     select={ 
      'score': 'COALESCE(SUM(vote), 0)', 
      'num_votes': 'COALESCE(COUNT(vote), 0)', 
    }).values_list('score', 'num_votes')[0] 

    return { 
     'score': int(result[0]), 
     'num_votes': int(result[1]), 
    } 

poi nella mia topic.views.py ho aggiunto quanto segue:

from voting.managers import VoteManager 
def index(request): 
queryset = Topic.objects.select_score().order_by('-score') 
paginator = Paginator(queryset, 3) # Show 25 contacts per page 

page = request.GET.get('page') 
try: 
    topic_list = paginator.page(page) 
except PageNotAnInteger: 
    # If page is not an integer, deliver first page. 
    topic_list = paginator.page(1) 
except EmptyPage: 
    #If page is out of range (e.g. 9999), deliver last page of results. 
    topic_list = paginator.page(paginator.num_pages) 

c = Context({ 
'topic_list': topic_list, 
'request': request 
}) 
return render_to_response('idea/index.html', c, context_instance=RequestContext(request)) 

Infine, nel mio index.html ho aggiunto le seguenti righe un po 'deviando dall'esempio originale fornito per gli utenti:

{% load voting_tags %} 
{% votes_by_user user on topic_list as vote_dict %} 
{% scores_for_objects topic_list as score_dict %} 

<table id="voting_table" class="list"> 
<tbody> 
    {% for link in topic_list %} 
<td class="vote"> 

{% dict_entry_for_item link from vote_dict as vote %} 
{% dict_entry_for_item link from score_dict as score %} 

<div>  
<form class="linkvote" id="linkup{{ link.id }}"{% if vote and vote.is_upvote %} action="{% url link_vote object_id=link.id, direction="clear" %}"{% else %} action="{% url link_vote object_id=link.id, direction="up" %}"{% endif %} method="POST"> 
    <input type="image" id="linkuparrow{{ link.id }}" src="{{ STATIC_URL }}images/aup{% if vote and vote.is_upvote %}mod{% else %}grey{% endif %}.png"> 
    {% csrf_token %} 
    <input type="hidden" name="next" value="{{ request.get_full_path }}"/> 
    {% else %} 

    </form> 

     <div id="link_score">{{ score.score|default:0 }}</div> 

    <form class="linkvote" id="linkdown{{ link.id }}" {% if vote and vote.is_downvote %} action="{% url link_vote object_id=link.id, direction="clear" %}"{% else %} action="{% url link_vote object_id=link.id, direction="down" %}"{% endif %} method="POST"> 
    {% csrf_token %} 
    <input type="image" id="linkdownarrow{{ link.id }}" src="{{ STATIC_URL }}images/adown{% if vote and vote.is_downvote %}mod{% else %}grey{% endif %}.png"> 
    <input type="hidden" name="next" value="{{ request.get_full_path }}"/> 

</td> 
<td class="item"> 
    <a id="link_title" href="{{ link.id }}">{{ link.title|escape }}</a></h2> 
    <p class="details"> 
    <span class="score" id="linkscore{{ link.id }}" 
      title="after {{ score.num_votes|default:0 }} vote{{ score.num_votes|default:0|pluralize }}"> 
    </span> 
    posted {{ link.date_created|timesince }} ago by 
    <span class="user"><a href="../users/{{ link.user.id }}/">{{ link.owner|escape }}</a></span> 
{% get_comment_count for link as comment_count %} 
    <span id="comment_score" class="comment_details"> {{ comment_count }} comment{{ comment_count|pluralize }}</span> 
    </p> 
</td> 
</tr>{% endfor %} 
</tbody> 
    <td> 
    <div id="paginator" class="pagination"> 
    <span class="step-links"> 
     {% if topic_list.has_previous %} 
      <a href="?page={{ topic_list.previous_page_number }}">previous</a> 
    {% endif %} 
    {% if topic_list.has_next %} 
     <a href="?page={{ topic_list.next_page_number }}">next</a> 
    {% endif %} 
    </span> 
    </div> 
    </td> 
</table> 

EDIT

quasi dimenticavo! Se desideri che la lista si divida in un ordine come 2,1,0, -1, -2, assicurati di impostare il valore dell'oggetto voto quando invii l'oggetto che stai creando. L'esempio che segue è tratto dal mio topic.views.py.

def submit_topic(request): 

if request.method == 'POST': 
    post_topic = PosttopicForm(request.POST) 
    owner = request.user 
    if post_topic.is_valid(): 
     topic = post_topic.save(commit=False) 
     topic.owner = request.user 
     topic.save() 
     vote = Vote(vote='0', user = request.user, content_type_id=10, object_id=topic.pk) 
     vote.save() 
     url = reverse('topic', args=[topic.pk, topic.slug]) 
     return HttpResponseRedirect(url) 
else: 
    post_topic = PosttopicForm() 

c = Context({ 
    'form': post_topic, 
    'user': request.user, 
    'request': request, 

})

return render_to_response('topic/submit.html', c, context_instance=RequestContext(request)) 

davvero spera che questo aiuta qualcun altro. Ci scusiamo per non aver pubblicato la soluzione prima. Speriamo che qualcuno possa migliorare questo eliminando tutti gli SQL dal VoteManager, ma ho bisogno di andare avanti.

0

È Potresti provare ad annotare il queryset dell'argomento per contenere la somma dei voti:

topic_list = Topic.objects.all().annotate(total=Sum('vote__vote')).order_by('-total') 

Nota: senza visualizzare i modelli, non sono sicuro di cosa inserire nella funzione Sum(). Dovrebbe essere il nome del modello figlio (che presumo sia il voto) seguito dal nome del campo sul modello.

+0

Ciao Nathan! Grazie per il feedback. Vorrei non essere limitato al numero di link che potrei aggiungere all'argomento. Sfortunatamente, l'ho già provato e il risultato è: FieldError: Impossibile risolvere la parola chiave 'vote' nel campo. Le scelte sono: date_created, date_updated, id, owner, slug, tag, titolo – user1462141

+0

Sospetto che sia correlato all'uso di GenericForeignKey. Non vedo [GenericeRelation] (https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#reverse-generic-relations) sull'argomento - devi aggiungerlo per poterlo accedere ai voti dall'argomento? – Nathan

+0

Credo di si. L'app per il voto di Django utilizza una vista generica per la gestione dei voti. https://code.google.com/p/django-voting/ – user1462141

Problemi correlati