4

I am using Django 2.0.

I make an AJAX call to a function in Django that should allow a user to vote on a post or remove their vote from a post. When I call the .delete() method on a model, the model returns a result as if it was being deleted, but the record is not removed from the database. Here is the Django function:

if request.method != 'POST':
    raise Http404

post_id = request.POST.get('postId')
vote_type = request.POST.get('voteType')

# If vote_type isn't valid, 404
if (not any(vote_type in allowed_vote_type
        for allowed_vote_type in Vote.ALLOWED_VOTE_TYPES)
        or not 'null'):
    raise Http404

post_queryset = Post.objects.filter(pk=post_id)
post = get_object_or_404(post_queryset)

try:
    vote = Vote.objects.get(
        post=post,
        user=request.user
    )
    print('Vote object found')
    # User has already voted on this post.
    # Update user's vote.
    if vote_type == 'upvote':
        print('vote_type is upvote')
        if vote.vote_type == 'upvote':
            print('Deleting vote.')
            # User just upvoted twice. Expected behavior: Remove their vote

            ##################
            ## THIS IS MY PROBLEM
            ##################
            vote_delete_result = vote.delete()
            print(vote_delete_result)
            if vote_delete_result[0]:
                # If upvote was successfully deleted, reduce post score
                post.score -= 1
            ##################
            ## END PROBLEM
            ##################
        elif vote.vote_type == 'downvote':
            print('Changing from downvote')
            # User is changing from downvote to upvote
            vote.vote_type = vote_type
            # Add 2 to post.score
            post.score += 2
        else:
            print('Vote type was not downvote, so we did nothing.')
    elif vote_type == 'downvote':
        # Repeat all the stuff above, but for downvote...
except ObjectDoesNotExist:
    print('Vote object NOT found')
    # User has not yet voted on this post.
    # Create a new vote.
    vote = Vote(
        user=request.user,
        post=post,
        vote_type=vote_type,
    )

    # Update post score.
    if vote_type == 'upvote':
        post.score += 1
    elif vote_type == 'downvote':
        post.score -= 1

if vote:
    vote.save()
post.save()
data = serializers.serialize(
    'json',
    post_queryset,
    fields=('id','score')
)
return HttpResponse(data, content_type='application/json')

When calling vote_delete_result = vote.delete() the value of vote_delete_result is (1, {'posts.Vote': 1}) as is expected according to the docs.

Unlike this question where the model.delete() method is not triggered, we know the model.delete() method is being called since vote_delete_result returns a tuple that confirms it.

One Google Group discussed the idea that .delete() only worked on QuerySets but that post was back in 2012. The Django 2.0 docs suggest that model.delete() should work just fine. Even so, I have tested my issue using a QuerySet instead like this (still getting the same issue):

vote_delete_result = Vote.objects.filter(pk=vote.id).delete()
print(vote_delete_result)
if vote_delete_result[0]:
    post.score -= 1

How do I delete my Vote record from the database?

Jacob
  • 1,560
  • 4
  • 19
  • 41
  • How are you verifying that the vote is not deleted? – Daniel Roseman Dec 30 '17 at 11:48
  • 1
    Through Django admin before and after the function is run. – Jacob Dec 30 '17 at 11:49
  • 3
    Later on you have: `if vote: vote.save()` - that's just saving the extact model instance back again after you've deleted it, no? You're not taking into account whether you want it deleted or not... – Jon Clements Dec 30 '17 at 11:52
  • 1
    Also - since you don't have a default `vote` - it's only set within the `try` - where it's not found, you'll get a `NameError` upon reaching the above anyway - looks like you need to do some refactoring there :) – Jon Clements Dec 30 '17 at 11:55

1 Answers1

3

Well, I'm going to make a few suggestions, maybe you'll like them.

1. Don't create a separate model to record votes

You can create two many-to-many fields to record the upvoters and downvoters:

class Post:
    upvoters = ManyToManyField(User)
    downvoters = ManyToManyField(User)

Now, if a user upvotes a post, all you have to do is add that user to the upvoters field:

if vote_type == upvote:
    if already upvoted:
        # undo vote
        post.upvoters.remove(user)
    else:
        # add vote
        post.upvoters.add(user)

Same goes for downvoting.


2. Move the upvote/downvote logic in your model methods

I once created a project where most of my model related logic was in views, and I regretted it very much later. A better way to organize the code is to keep the database related logic in the models, as much as possible, and frontend/template related logic in the views.

So, to move all that upvote/downvote logic to your models, you can create special methods for that:

class Post:
    ...
    def vote(self, user, vote_type):
        if vote_type == upvote:
            self.upvoters.add(user)

        # and so on

And then, you can call Post.vote method to do upvote/downvote:

post.vote(request.user, vote_type)
xyres
  • 20,487
  • 3
  • 56
  • 85