0

I am trying to create a basic up down voting system for anonymous users that do not need to be signed in so any visitor can push once and a vote is entered. Obviously we have to restrict people to one vote. After some research and no luck with the djangoratings module, I found that evercookie for django is the most promising approach to this ability. However, I need a bit of help writing the part of the code that compares the evercookies of the votes currently cast on an object with a possible incoming code. I have a lot of the other things figured out I think.

My basic django model object at its core is a submitted URL with an IntegerField for keeping track of the likes:

 class newlink(models.Model):

    linktag = models.ForeignKey(‘pagename’) #the page the link belongs in
    linkcomment = models.CharField(max_length=128) #comment to go along with post
    postlinkdate = models.DateTimeField(auto_now_add=True) #submission datestamp
    url = models.URLField(max_length = 1024) 
    linklikescounter = models.IntegerField(null=False, default=0) #this is what will be changed up or down per vote
    # Do I need another field(s) in this model to store evercookie data? Or maybe a new "likevote" class that has a ForeignKey relationship to the newlink class? 


def __unicode__(self):
    return self.url

I have this simple button/form in my template:

<form action="/{{pagename_param}}" method="post">
     {% csrf_token %}
     <input type="hidden" name="linkset_likeid" value="{{ linkset.id }}">
     <input type="submit" class="btn" value="like" name="linklikebtn"/>
     </form>

And my views for getting this going:

if (request.POST.get('linklikebtn')):
        linkid = request.POST[‘linkset_likeid’] #retrieve the ID from the form
        url = newlink.objects.get(id=commentid) #get an instance of the desired url
        url.linklikescounter += 1 #increase the IntegerField by 1
        url.save() #save to the db
EazyC
  • 809
  • 1
  • 10
  • 30
  • 1
    Note that your code introduces a race condition: if two people vote at the same time, the second vote will override the change of the first vote. To prevent this, use `from django.db.models import F` `newlink.objects.filter(id=commentid).update(linklikescounter=F('linklikescounter') + 1)`. This will change the counter in a single statement on the database level. – knbk Nov 30 '14 at 16:45
  • Hmm, I didn't even notice that. Can you explain just a bit further why this introduces a race condition? Is it because the time it takes to call the linklikescounter +=1 line and then save it to the database could be too long and another person could have increased the sum by that time? Thanks for bringing this to my attention. – EazyC Dec 01 '14 at 03:04
  • 1
    Yes, that's exactly what happens. Say the counter is 10. You get the object from the database and increase it, so it is 11. Before you save it another person votes and gets the 'old' object where the counter is 10. Then both save the object with a counter of 11, and a vote is lost. – knbk Dec 01 '14 at 14:36
  • Ah, great catch, thank you s much. I didn't even think of this when writing the original code. Is there documentation for the F function? – EazyC Dec 01 '14 at 22:42
  • Yes, see https://docs.djangoproject.com/en/dev/ref/models/expressions/#f-expressions. – knbk Dec 02 '14 at 00:10

1 Answers1

0

If the question is still valid, you might wanna look on Django-Evercookie

Basing, on your question: you can reject repeating votes while processing your request (e.g. form validation - call evercookie's get method and if it returns something - put it into hidden field) or make a DB / model level validation which might be an overkill in this case.

alexeyhaidamaka
  • 168
  • 1
  • 9