Let's say I have two Models in Django:
Book:
class Book(models.Model):
title = models.CharField(max_length=100, blank=False)
number_of_readers = models.PositiveIntegerField(default=0)
Reader:
class Reader(models.Model):
book = models.ForeignKey(Book)
name_of_reader = models.CharField(max_length=100, blank=False)
Everytime I add a new Reader to the database I want to increase number_of_readers in the Book model by 1. I do not want to dynamically count number of rows Reader rows, related to a particular Book, for performance reasons.
Where would be the best place to increase the number_of_readers field? In the Serializer or in the Model? And what method shall I use? Should I override .save in the Model? Or something else in the Serializer?
Even better if someone could provide a full blown example on how to modify the Book table when doing a post of a new Reader.
Thanks.
I wouldn't do this on the REST API level, I'd do it on the model level, because then the +1 increase will always happen, regardless of where it happened (not only when you hit a particular REST view/serializer)
Everytime I add a new Reader to the database I want to increase number_of_readers in the Book model by 1
I'd implement a post_save signal that triggers when a model (Reader) is created
There is a parameter to that signal called created, that is True when the model is created, which makes more convenient than the Model.save() override
Example outline
from django.db.models.signals import post_save
def my_callback(sender, instance, created, **kwargs):
if created:
reader = instance
book = reader.book
book.number_of_readers += 1 # prone to race condition, more on that below
book.save(update_fields='number_of_readers') # save the counter field only
post_save.connect(my_callback, sender=your.models.Reader)
https://docs.djangoproject.com/en/1.8/ref/signals/#django.db.models.signals.post_save
Race Conditions
In the above code snippet, if you'd like to avoid a race condition (can happen when many threads updating the same counter), you can also replace the book.number_of_readers += 1 part with an F expression F('number_of_readers') + 1, which makes the read/write on the DB level instead of Python,
book.number_of_readers = F('number_of_readers') + 1
book.save(update_fields='number_of_readers')
more on that here: https://docs.djangoproject.com/en/1.8/ref/models/expressions/#avoiding-race-conditions-using-f
There is a post_delete signal too, to reverse the counter, if you ever think of supporting "unreading" a book :)
If you wish to have batch imports of readers, or need to periodically update (or "reflow") the reader counts (e.g. once a week), you can in addition of the above, implement a function that recounts the readers and update the Book.number_of_readers
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With