Reputation: 7056
So, I have a small blog app(django newbie here) with blog articles, and I am looking to include a "rating" to each of these items (similar to up/down on stackoflow). So, what I have is a ajax based GET in a ListView - but I now realize I need POST to "modify" this ratings data. I have been looking to post data from a django ListView, but I am unable to see how this can be done. The current GET code, which is a listview of all blog articles (paginated) looks as follows:
#views.py
class AwesomeDisplayListView(JSONResponseMixin,ListView):
model = blogposts
template_name = "awesome_list.html"
paginate_by = '15'
context_object_name = "searchres"
def get_context_data(self, **kwargs):
print "this is get_context_data"
context = super(SearchDisplayListView, self).get_context_data(**kwargs)
q = self.request.GET.get('q')
context['searchq'] = q
return context
def get_queryset(self):
print "this is get_queryset"
# get some queryset
return queryset
def render_to_response(self, context):
if self.request.is_ajax():
obj = {'name':'ajax', 'birthday':'may'}
return JSONResponseMixin.render_to_response(self, obj)
else:
return ListView.render_to_response(self, context)
The above code works fine to get data ("obj") into the template and I am able to render both the blogposts and the GET ajax, but, what I would like to do is post data so that: [1] the rating is increased from its current value by 1 [2] the particular action (clicking to vote), requires login_required.
I was wondering if someone could point me in the right direction to do this. I have googled the problem, and people seem to suggest combining Mixins - but, unsure if this should be the case.
Thanks.
Upvotes: 1
Views: 2115
Reputation: 6328
I can't tell you how much I learned by looking at the class based view source code. Once you've seen it, it becomes very easy to modify your class object to do what you want. In your case, if you add a post
method to your class, it will be called in the event of a post to the url mapped to the view.
def post( self, request, *args **kwargs ):
# receive the post, process the rating, return your JSON response
In response to your comments
Since you're wanting users to be able to upvote/downvote from a ListView, you will need some way of identifying the post being voted on to the POST view which handles the request.
So if you were to send the id of the blogpost along with the vote, you could grab that model and update it accordingly.
assuming a model like this
class BlogPost( models.Model ):
upvotes=models.IntegerField( default=0 )
downvotes=models.IntegerField( default=0 )
def get_score( self ):
return self.upvotes - self.downvotes
score=property( get_score )
yourtemplate.html
{% for post in object_list %}
<div class="blogpost" data-pk="{{post.pk}}">
<span class="score">{{post.score}}</span>
<a class="upvote" href="#">Upvote</a>
<a class="downvote" href="#">Downvote</a>
</div>
{% endfor %}
yourjavascript.js
$('.blogpost').on( 'click', '.upvote,.downvote', function( e ){
e.preventDefault();
var id=$( this ).closest('.blogpost').data('pk'),
upvote=$( this ).is('.upvote'),
xhr=$.ajax({
url: location.href,
type: 'post',
data:{ id: id, upvote: upvote ? 1 : 0 },
dataType: 'json'
});
xhr.done( function( data ){
// successful submission
});
});
views.py
class AwesomeDisplayListView(JSONResponseMixin,ListView):
model = BlogPost
template_name = "awesome_list.html"
paginate_by = '15'
context_object_name = "searchres"
def post( self, request, *args, **kwargs ):
id=request.POST.get('id')
upvote=int( request.POST.get('upvote') )
blogpost=BlogPost.objects.get( pk=id )
if upvote:
blogpost.upvotes=blogpost.upvotes + 1
else:
blogpost.downvotes=blogpost.downvotes + 1
blogpost.save()
return http.HttpResponse( json.dumps({ 'score': blogpost.score }) )
To expand further on my last comment, a Rating model might make this easier.
class BlogPost( models.Model ):
title=models.CharField( max_length=100 )
def get_score( self ):
return self.ratings.filter( up=True ).count() - self.ratings.filter( up=False ).count()
score=property( get_score )
class Rating( models.Model ):
blogpost=models.ForeignKey( BlogPost, related_name="ratings" )
user=models.ForeignKey( User )
up=models.BooleanField( default=True )
class Meta:
unique_together=('blogpost','user',)
Then in your post view
def post( self, request, *args, **kwargs ):
id=request.POST.get('id')
upvote=bool( int( request.POST.get('upvote') ) )
blogpost=BlogPost.objects.get( pk=id )
rating, is_new=Rating.objects.get_or_create( user=request.user, blogpost=blogpost )
if rating.up != upvote:
rating.up=upvote
rating.save()
Upvotes: 2