views:

98

answers:

2

I am trying to have this function limit a user to only one vote per image. However it currently lets all votes through. If I change "if existing_vote != 0:" to "if existing_vote == 0:" it lets no votes through. Thoughts?

class VoteHandler(webapp.RequestHandler):

def get(self):
    #See if logged in
    self.Session = Session()
    if not 'userkey' in self.Session:
        doRender(
            self,
            'base/index.html',
            {'error' : 'Please login to vote'})
        return

    #If user hasn't voted - if user doesn't have a vote on that image object
    key = self.request.get('photo_id')
    vurl = models.Image.get_by_id(int(key))

    #pull current site vote total & add 1

    existing_vote = models.Vote.all().filter('user=', self.Session['userkey']).filter('photo=',vurl).count()

    if existing_vote != 0:
        self.redirect('/', { })
    else:    
        newvote = models.Vote(user=self.Session['userkey'], url=vurl)
        vurl.votes += 1
        vurl.put()
        logging.info('Adding a vote')

        #Create a new Vote object
        newvote = models.Vote(user=self.Session['userkey'], url=vurl)
        newvote.put()    
        self.redirect('/', { })

For the Models:

class User(db.Model):

account = db.StringProperty()

password = db.StringProperty()

name = db.StringProperty()

created = db.DateTimeProperty(auto_now=True)

class Image(db.Model):

user = db.ReferenceProperty(User)

photo_key = db.BlobProperty()

website = db.StringProperty()

text = db.StringProperty()

created = db.DateTimeProperty(auto_now=True)

votes = db.IntegerProperty(default=1)

class Vote(db.Model):

user = db.ReferenceProperty(User) #See if voted on this site yet

photo = db.ReferenceProperty(Image) #To apply vote to right URL

upvote = db.IntegerProperty(default=1)

created = db.DateTimeProperty(auto_now=True)

+1  A: 

Looks like your filter on user is wiping out every existing vote, i.e., the equality there is never satisfied. And indeed I'm not sure how I'd satistfy an equality check on a reference propertly. Why not change

user = db.ReferenceProperty(User) #See if voted on this site yet

to, e.g.,

useraccount = db.StringProperty()  # account of user who cast this vote

Then the comparison becomes a simple equality check between strings and is sure to work without any complication -- simplicity is generally preferable, when feasible.

Alex Martelli
Shouldn't a sharded counter be used? Am I missing something?
Matt H
@Matt, a sharded counter that only counts to 0 or 1 and is keyed by every pair of user and image...? Seems weird to me -- care to post an answer clarifying, because I just can't make sense of the comment and comments are too limited for you to expand on it enough, I fear.
Alex Martelli
@Matt @Alex -thanks for the answers. Here is what I got working last night: http://gist.github.com/562252 . And I am not familiar with a sharded counter , checking that out now
Emile Petrone
+1  A: 

On this line here:

existing_vote = models.Vote.all().filter('user=', self.Session['userkey']).filter('photo=',vurl).count()

You need to put a space between the 'photo' and the '=' in the filters - otherwise, it's attempting to filter for a property called 'photo='. This should work:

existing_vote = models.Vote.all().filter('user =', self.Session['userkey']).filter('photo =',vurl).count()
Nick Johnson
@Nick that was definitely part of it. Surprising, but yes the spaces were part of the problem
Emile Petrone
It's definitely a gotcha, but we can't change it without potentially breaking existing apps. It makes sense once you realise that 'foo=' is a valid property name. You can continue to use Reference properties, though - <, = and > are all valid operations on a reference property.
Nick Johnson