2012-06-17 17 views
5

Có thể lập trình tham gia hai bảng bằng ORM của Django không? Tôi có hai mô hình: Chủ đề và phiếu bầu. Trên mẫu của tôi, tôi có một danh sách các chủ đề mà người dùng có thể lên/xuống bỏ phiếu như Reddit. Tất cả mọi thứ hoạt động như nó nên ngoại trừ việc phân loại kết quả. Tôi không thể tìm ra cách sắp xếp một danh sách các đối tượng dựa trên số điểm đó là tổng số phiếu bầu của mỗi đối tượng. Tôi có thể lấy dữ liệu mong muốn từ Postgres mà không cần bất kỳ vấn đề:Làm thế nào để bạn tham gia hai bảng bằng cách sử dụng Django mà không cần sử dụng sql thô?

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; 

Nó trả về kết quả mong muốn:

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 

Vấn đề là, tôi không chắc chắn làm thế nào để lấy lại điều này như một queryset. Hiện tại, tôi đang sử dụng các mục sau để hiển thị các đối tượng:

topic_list = Topic.objects.all() 

Mọi thứ hiển thị như tôi muốn, ngoại trừ thứ tự sắp xếp. Tôi muốn số điểm cao nhất để hiển thị đầu tiên.

Tài nguyên Tôi đã xem xét: https://docs.djangoproject.com/en/dev/topics/db/managers/#adding-extra-manager-methods
How to query as GROUP BY in django?

Và nhiều hơn nữa, nhưng như một người dùng mới, chống thư rác ngăn cản tôi từ thêm chúng.

Thing của tôi đã cố gắng:

Chain:

listed_links = list(chain(topic, score)) 

Thật không may, nếu tôi cố gắng thêm một giá trị được sắp xếp này đã phá vỡ.

Kết hợp danh sách đối tượng:

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) 

Điều này dẫn đến tất cả các đối tượng tôi muốn trong một danh sách, nhưng tôi không thể tìm ra cách để liên kết topic.id-score.object_id.

Tôi cũng đã thử chèn SQL thô, nhưng tôi không cảm thấy như tôi đang thực hiện nó một cách chính xác và có thể dẫn đến việc SQL injection bởi bên thứ ba.

Tôi rất muốn chia sẻ kết quả của dự án này với dự án bỏ phiếu django. Như tôi đã nói, mọi thứ hoạt động như nó cần, ngoại trừ tôi không thể tìm ra cách sắp xếp theo điểm số desc.

============= Bỏ phiếu ========================

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 mẫu ========================

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

Các mô hình là ...? –

+0

Tôi đã thêm các mô hình. Cảm ơn vì đã phản hồi nhanh. – user1462141

Trả lời

3

tôi đã có thể tìm ra các giải pháp sử dụng một bản vá như đã mô tả ở đây:

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

Sự khác biệt là tôi trích ra những dòng sau:

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

Và thêm chúng vào bỏ phiếu/managers.py tệp như sau:

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

Sau đó Trong topic.views.py tôi tôi thêm như sau:

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

Cuối cùng, trong index.html của tôi, tôi thêm vào các dòng sau hơi sai lệch từ ví dụ ban đầu được cung cấp cho người dùng:

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

tôi gần như quên! Nếu bạn muốn danh sách sắp xếp theo thứ tự như 2,1,0, -1, -2 hãy đảm bảo đặt giá trị đối tượng biểu quyết khi gửi bất kỳ đối tượng nào bạn đang tạo. Ví dụ bên dưới là từ số topic.views.py của tôi.

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

Tôi thực sự hy vọng điều này giúp người khác. Xin lỗi vì không đăng giải pháp sớm hơn. Hy vọng rằng ai đó có thể cải thiện điều này bằng cách loại bỏ tất cả các câu lệnh SQL với nhau từ Trình quản lý bỏ phiếu, nhưng tôi cần phải tiến lên phía trước.

0

Bạn có thể thử chú thích bộ truy vấn Chủ đề để chứa tổng số phiếu bầu:

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

Lưu ý: Nếu không thấy mô hình của bạn, tôi không chắc chắn nên đặt gì vào hàm Sum(). Nó phải là tên mô hình con (mà tôi giả định là bỏ phiếu) theo sau là tên trường trên mô hình.

+0

Xin chào Nathan! Cảm ơn phản hồi. Tôi ước rằng tôi không bị giới hạn số lượng liên kết mà tôi có thể thêm vào chủ đề. Không may, tôi đã thử điều đó rồi và kết quả là: FieldError: Không thể giải quyết từ khóa 'bỏ phiếu' vào trường. Các lựa chọn là: date_created, date_updated, id, owner, slug, tags, title – user1462141

+0

Tôi nghi ngờ nó liên quan đến việc sử dụng GenericForeignKey. Tôi không thấy [GenericeRelation] (https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#reverse-generic-relations) về Chủ đề - bạn có cần thêm nó để bạn có thể truy cập phiếu bầu từ chủ đề này? – Nathan

+0

Tôi tin như vậy. Ứng dụng biểu quyết django sử dụng chế độ xem chung để quản lý phiếu bầu. https://code.google.com/p/django-voting/ – user1462141

Các vấn đề liên quan