2012-06-17 13 views
5

Czy można programowo połączyć dwie tabele za pomocą ORM Django? Mam dwa modele: tematy i głosy. W moim szablonie mam listę tematów, które użytkownicy mogą głosować w górę/w dół jak Reddit. Wszystko działa tak, jak powinno, z wyjątkiem sortowania wyników. Nie mogę wymyślić sposobu sortowania listy obiektów na podstawie wyniku, który jest sumą liczby głosów każdego obiektu. mogę pobierać żądane dane z PostgreSQL bez problemu:Jak połączyć dwie tabele za pomocą Django bez używania surowego 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; 

Zwraca pożądanych rezultatów:

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 

Problem polega na tym, że nie jestem pewien, w jaki sposób odzyskać to jako queryset. W tej chwili używam następujących obiektów do wyświetlenia:

topic_list = Topic.objects.all() 

Wszystko jest wyświetlane tak jakbym chciała, oprócz kolejności sortowania. Chciałbym, aby najwyższy wynik był wyświetlany jako pierwszy.

Zasoby już spojrzał na: https://docs.djangoproject.com/en/dev/topics/db/managers/#adding-extra-manager-methods
How to query as GROUP BY in django?

i wiele innych, ale jako nowy użytkownik, anty-spam nie pozwala mi ich dodanie.

rzecz jest Próbowałem:

Łańcuch:

listed_links = list(chain(topic, score)) 

Niestety, jeśli próbowałem dodać posortowaną wartość ta wybuchła.

łącząc listy obiektów:

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) 

Spowodowało wszystkich obiektów chciałem w jednej listy, ale nie mogłem dowiedzieć się, jak połączyć topic.id do score.object_id.

Próbowałem również wstawiania surowego SQL, ale nie mam ochoty robię to poprawnie i może doprowadzić do wstrzyknięcia SQL przez stronę trzecią.

Chciałbym podzielić się wynikami tego z powrotem z projektem głosowania w django. Jak już mówiłem, wszystko działa tak, jak powinno, z wyjątkiem tego, że nie mogę wymyślić sposobu sortowania według oceny punktowej.

============= Głosowanie ========================

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 

============= temat modelu ========================

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

Modele są ...? –

+0

Dodałem modele. Dzięki za szybką reakcję. – user1462141

Odpowiedz

3

że w stanie zorientować się w roztwór przy użyciu plastra, jak tutaj opisano:

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

Różnica polegała na tym, że ekstrahuje się następujące linie:

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)}) 

i dodano je do voting/managers.py plik jako taki:

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]), 
    } 

potem w moim topic.views.py dodałem następujące:

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)) 

Wreszcie w moim index.html I dodaje się następujące wiersze nieznacznie odbiegające od pierwotnego przykład przewidzianej użytkowników:

{% 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

zapomniałbym! Jeśli chcesz, aby lista sortowała się w kolejności 21,0, -1, -2, upewnij się, że ustawiłeś wartość obiektu głosowania po przesłaniu dowolnego obiektu, który tworzysz. Poniższy przykład jest z mojego 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)) 

I naprawdę nadzieję, że to pomaga kogoś innego. Przepraszamy za nie opublikowanie rozwiązania wcześniej. Mam nadzieję, że ktoś może to poprawić, pozbywając się SQL razem z VoteManager, ale muszę iść naprzód.

0

You można spróbować dodać adnotację do kwerendy Temat, aby zawierała ona sumę głosów:

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

Uwaga: Nie widząc twoich modeli, nie jestem pewien, co dodać do funkcji Sum(). Powinna to być nazwa modelu podrzędnego (którą uważam za głos), po której następuje nazwa pola w modelu.

+0

Cześć Nathan! Dzięki za opinię. Szkoda, że ​​nie byłem ograniczony ilością linków, które mogłem dodać do tematu. Niestety, próbowałem już tego i powoduje: FieldError: Nie można rozwiązać słowa kluczowego "głosuj" w polu. Dostępne opcje: date_created, date_updated, id, owner, slug, tags, title – user1462141

+0

Podejrzewam, że jest to związane z użyciem GenericForeignKey. Nie widzę [GenericeRelation] (https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#reverse-generic-relations) w temacie - czy musisz dodać to, abyś mógł uzyskać dostęp do głosów z tego tematu? – Nathan

+0

Tak sądzę. Aplikacja do głosowania w Django używa ogólnego widoku do zarządzania głosami. https://code.google.com/p/django-voting/ – user1462141

Powiązane problemy