Reputation: 2709
I'm trying to add a vote functionality to the code found in tutorial of Django Rest Framework. On top of Snippet model, I added a Vote model:
class Vote(models.Model):
created = models.DateTimeField(auto_now_add=True)
voter = models.ForeignKey(User, on_delete=models.CASCADE)
snippet = models.ForeignKey(Snippet, related_name='votes', on_delete=models.CASCADE)
class Meta:
ordering = ('created',)
After posting and validating a user vote to a snippet, I now want to update the number of votes received by the snippet (I added to the Snippet model a number_of_votes field).
I'm doing it in the create method of my VoteSerializer like that:
class VoteSerializer(serializers.HyperlinkedModelSerializer):
voter = serializers.ReadOnlyField(source='voter.username',validators=[UniqueValidator(queryset=VoteUp.objects.all(), message=already_voted)])
snippet = serializers.PrimaryKeyRelatedField(queryset=Snippet.objects.all())
def validate(self, data):
# ... my validation function
def create(self, validated_data):
obj = Vote.objects.create(**validated_data)
obj.snippet.number_of_votes += 1
obj.snippet.save()
return obj
It works well but I'm not sure if it's the good way or not to do it. Is there a better way?
Upvotes: 0
Views: 214
Reputation: 1480
Couple of possible "better" ways to try:
Overwrite Save Method
class Vote(models.Model):
created = models.DateTimeField(auto_now_add=True)
voter = models.ForeignKey(User, on_delete=models.CASCADE)
snippet = models.ForeignKey(Snippet, related_name='votes', on_delete=models.CASCADE)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
# ensure model is being created and not just modified:
if not self.pk:
# increment snippet vote counter
self.snippet.number_of_votes += 1
self.snippet.save()
# call base save method to ensure proper handling
super().save(*args, **kwargs)
Use a Post Save signal
Once you have signals setup for your Django project following the documentation you can create a signal that will look something like this:
from django.dispatch import receiver
from django.db.models.signals import post_save
# make sure you import Vote here. I'm not sure of your project
# setup so I can't write the import for you.
@receiver(post_save, sender=Vote)
def update(sender, instance, created, **kwargs):
# only increment on model creation
if created:
instance.snippet.number_of_votes += 1
instance.snippet.save()
Although in all honesty I'm pretty sure snippet.vote_set.count()
is a pretty fast call and shouldn't interfere with your run time any if you wanted to start by using that instead of a counter. Because remember if you increment the counter you're going to have to decrement it too or your count will be off if someone deletes a record.
Upvotes: 1