Search code examples
pythonflaskpeewee

How can I detect if a user tries to upvote once again?


I'm currently in the process of implementing an upvoting system ( no down voting system will be used in the app). I managed to create an upvote property to the Post model in my app. The default for that property is 0 as shown here:

models.py

class User(UserMixin, Model):
    username = CharField(unique= True)
    email = CharField(unique= True)
    password = CharField(max_length = 100)
    joined_at = DateTimeField(default = datetime.datetime.now)
    is_admin = BooleanField(default = False)
    confirmed = BooleanField(default = False)
    confirmed_on = DateTimeField(null=True)

    class Meta:
        database = DATABASE
        order_by = ('-joined_at',)

    def get_posts(self):
        return Post.select().where(Post.user == self)

    def get_stream(self):
        return Post.select().where(
            (Post.user == self)
        )

    @classmethod
    def create_user(cls, username, email, password, is_admin= False, confirmed = False, confirmed_on = None):
        try:
            with DATABASE.transaction():
                cls.create(
                username = username,
                email = email,
                password = generate_password_hash(password),
                is_admin = is_admin,
                confirmed = confirmed,
                confirmed_on = confirmed_on)
        except IntegrityError:
            raise ValueError("User already exists")

class Post(Model):
    timestamp = DateTimeField(default=datetime.datetime.now)
    user = ForeignKeyField(
        rel_model = User,
        related_name = 'posts'
    )
    name = TextField()
    content = TextField()
    upvotes = IntegerField(default=0)
    url = TextField()
    category = TextField()

    class Meta:
        database = DATABASE
        order_by = ('-timestamp',)

I managed to increment the value by making the user follow a link:

stream.html

 <div class="voting_bar">
        <a href="/vote/{{post.id}}"><img src="/static/img/upvote.png"></a>
        <p>{{post.upvotes}}</p>
      </div>

This will activate a function with the associated route:

app.py

@app.route('/vote/<int:post_id>')
def upvote(post_id):
    posts = models.Post.select().where(models.Post.id == post_id)
    if posts.count() == 0:
        abort(404)
    post = models.Post.select().where(models.Post.id == post_id).get()
    query = models.Post.update(upvotes = (post.upvotes+1)).where(models.Post.id == post_id)
    query.execute()
    return redirect(url_for('index'))

My question is, how can I detect if the user had already voted? I'm not sure what I should do to accomplish this. My plan was that if I identify if the user tried to upvote again, what I would do is simply decrement their previous vote.


Solution

  • I think the best approach here would be to create a separate table called Votes which will have two columns. One will store the id of the Post and the other will store the id of the User. Each entry or row inside the table would count as one vote. If a user tries to vote on a particular post, you would first query the Votes table if a row with that user id exists. If it doesn't exist, you add the vote. If it does exist however, then you simply remove the vote. To get the total vote count of a particular post, you would again query the Votes table and count the number of rows with the given post id. This would also make your application scalable if in case you would like to add a downvote functionality in the future.