2012-06-17 35 views
5

是否可以使用Django的ORM以编程方式连接两个表?我有两个模型:主题和投票。在我的模板上,我有一个主题列表,用户可以像Reddit一样投票/投票。除了对结果进行排序以外,一切都可以正常工作。我无法弄清楚如何根据每个对象的投票计数总和得分来对对象列表进行排序。我可以检索的Postgres所需要的数据没有任何问题:如何在不使用原始sql的情况下使用Django连接两个表?

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; 

它返回所期望的结果:

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 

的问题是,我不知道如何检索这是一个查询集。目前,我正在使用以下内容来显示对象:

topic_list = Topic.objects.all() 

除了排序顺序外,所有内容都显示为我想要的内容。我希望最高分显示在第一位。

资源我已经看了看: https://docs.djangoproject.com/en/dev/topics/db/managers/#adding-extra-manager-methods
How to query as GROUP BY in django?

更多的人,但作为一个新的用户,防垃圾邮件阻止我加入他们。

件事是我尝试过:

链:

listed_links = list(chain(topic, score)) 

不幸的是,如果我尝试添加一个排序值,这打破了。

合并对象名单:

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) 

这导致我在一个列表中想要的所有对象,但我无法弄清楚如何topic.id链接到score.object_id

我也尝试插入原始SQL,但我不觉得我做得正确,并可能导致第三方SQL注入。

我很想将这个结果分享给django-voting项目。就像我说的,除了我不知道如何按照分数desc排序之外,一切都按照它应该的方式工作。

============= 投票 ========================

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 

============= 主题模型的 ========================

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

模型是...? –

+0

我添加了模型。感谢您的快速反馈。 – user1462141

回答

3

我能找出使用贴片的溶液如下所述:

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

不同的是,我所提取的下列行:

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

并将它们添加到voting/managers.py文件如下:

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

然后以我topic.views.py添加以下:

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

最后,在我的index.html我添加下列行从提供给用户的最初的例子稍微偏离:

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

编辑

我差点忘了!如果您希望列表按照2,1,0,-1,-2的顺序排序,请确保在提交您创建的任何对象时设置投票对象值。以下示例来自我的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)) 

我真的希望这可以帮助别人。对不起,不要提早发布解决方案。希望有人可以通过从VoteManager中一起摆脱SQL来改善这一点,但我需要前进。

0

你可以尝试对Topic queryset进行注释以包含总票数:

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

注意:没有看到您的模型,我不知道要在Sum()函数中放什么。它应该是儿童模型名称(我假设是投票),然后是模型上的字段名称。

+0

嗨Nathan! 感谢您的反馈意见。我希望我没有限制我可以添加到主题的链接数量。不幸的是,我已经尝试过了,结果如下: FieldError:无法将关键字'vote'解析为字段。选择是:date_created,date_updated,id,所有者,slug,标签,标题 – user1462141

+0

我怀疑它与使用GenericForeignKey有关。关于主题,我没有看到[GenericeRelation](https://docs.djangoproject.com/zh/dev/ref/contrib/contenttypes/#reverse-generic-relations) - 您是否需要添加以便您可以访问该主题的投票? – Nathan

+0

我相信如此。 Django-voting应用程序使用通用视图来管理投票。 https://code.google.com/p/django-voting/ – user1462141

相关问题