Package: django-vote-base
Installation$[sudo]pipinstalldjango-vote-basesettings.pyINSTALLED_APPS+=['django_vote_base']models.pyfromdjango.dbimportmodelsfromdjango_vote_base.modelsimportVoteModelclassCollectionVote(VoteModel):obj=models.ForeignKey('Collection',related_name="vote_set",on_delete=models.CASCADE)classMeta:unique_together=[('obj','created_by')]classCollection(models.Model):votes_up_count=models.IntegerField(null=True)votes_down_count=models.IntegerField(null=True)votes_score=models.IntegerField(null=True)defget_vote_down_url(self):return'/vote/collection/down/%s'%(self.pk,)defget_vote_up_url(self):return'/vote/collection/up/%s'%(self.pk,)urls.pyfromdjango.urlsimportinclude,pathimportviewsurlpatterns=[path('vote/collection/down/<int:pk>',views.VoteDownView.as_view(),name='down'),path('vote/collection/up/<int:pk>',views.VoteUpView.as_view(),name='up')]views.pyDetailView- context variables{{ vote_obj }}and{{ vote }}fromapps.collections.modelsimportCollection,CollectionVotefromdjango_vote_base.viewsimportVoteViewMixinclassVoteDetailView(BookmarkViewMixin):vote_model=CollectionVoteXMLHttpRequestviewfromdjango_vote_base.viewsimportVoteDownView,VoteUpViewfromapps.core.collections.modelsimportCollection,CollectionVoteclassCollectionVoteViewMixin:vote_model=CollectionVotedefget_data(self):collection=Collection.objects.get(pk=self.kwargs['pk'])return{'vote':self.vote,'votes_up_count':collection.votes_up_count,'votes_down_count':collection.votes_down_count,'votes_score':collection.votes_score}classVoteDownView(CollectionVoteViewMixin,VoteDownView):passclassVoteUpView(CollectionVoteViewMixin,VoteUpView):passListViewprefetch user bookmarksfromdjango.db.modelsimportPrefetchfromdjango.views.generic.listimportListViewfromapps.collections.modelsimportCollection,CollectionVoteclassCollectionListView(ListView):defget_queryset(self,**kwargs):qs=Collection.objects.all()ifself.request.user.is_authenticated:qs=qs.prefetch_related(Prefetch("vote_set",queryset=CollectionVote.objects.filter(created_by=self.request.user),to_attr='votes'))returnqsTemplates<adata-id="{{ vote_obj.pk }}"class="vote-up-btn {% if vote and vote.vote > 0 %}selected{% endif %}"{%ifrequest.user.is_authenticated%}data-href="{{ vote_obj.get_vote_up_url }}"{%else%}href="{% url 'login' %}?next={{ request.path }}"{%endif%}></a><adata-id="{{ vote_obj.pk }}"class="votes-score">{{ vote_obj.votes_score|default:"0" }}</a><adata-id="{{ vote_obj.pk }}"class="vote-down-btn {% if vote and vote.vote < 0 %}selected{% endif %}"{%ifrequest.user.is_authenticated%}data-href="{{ vote_obj.get_vote_down_url }}"{%else%}href="{% url 'login' %}?next={{ request.path }}"{%endif%}></a>JavaScriptconstvote_buttons=document.querySelectorAll("a.vote-up-btn, a.vote-down-btn");for(constbuttonofvote_buttons){button.addEventListener('click',function(event){event.preventDefault();data_id=button.getAttribute('data-id');varvote_down_btn=document.querySelector(".vote-down-btn[data-id='"+data_id+"']");varvote_up_btn=document.querySelector(".vote-up-btn[data-id='"+data_id+"']");varvotes_score=document.querySelector(".votes-score[data-id='"+data_id+"']");varxhr=newXMLHttpRequest();xhr.open('GET',button.getAttribute('data-href'));xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');xhr.responseType='json';xhr.onload=function(){if(xhr.status!=200){console.error(`Error${xhr.status}:${xhr.statusText}`);}if(xhr.status==200){votes_score.innerHTML=xhr.response.votes_score;}};xhr.send();})}readme42.com