darkhorse
darkhorse

Reputation: 8782

Handling a model's save method using an atomic transaction in Django

The question is honestly pretty self-explanatory. I have 2 models which are related. It goes like this:

from django.db import IntegrityError, transaction

class Item(models.Model):
    total_score = models.IntegerField()

    def set_score(self):
         ...

class Review(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    score = models.IntegerField()

    def save(self, *args, **kwargs):
        try:
            with transaction.atomic():
                super(Review, self).save(*args, **kwargs)
                self.item.set_score()
        except IntegrityError:
            handle_exception()

Basically, whenever a review is saved, its item's score is updated using the set_score() method. I put the whole thing in an atomic transaction because obviously, I don't want a review to update and the item's score to remain un-updated. Thats like a breeding ground for potential bugs. Anyway, is the right way to do it? I feel like I have no way of testing this in my local server as it is impossible to create an error which saves the review but not update the score. Thanks.

Note: I know there are better ways to handle the score of an item based on its reviews. That is not the point of the question. It is a made up scenario for the sake of explaining what I want to do. So, please, no answers mentioning that.

Upvotes: 1

Views: 1760

Answers (1)

Devang Padhiyar
Devang Padhiyar

Reputation: 3707

Yes, you could do it as you have mentioned but it is better to use business logic/ transaction logic outside of save() method. This would provide you better integrity and also abstraction.

Writing this logic inside save function is shorthand to do (pretty easy).

other way to do this

utils.py

def update_score(*args, **kwargs):
    with transaction.atomic():
        review = Review(*args, **kwargs)
        review.item.set_score()
        review.save()

If this wouldn't helpful for you than writing that transaction block inside save() method or simply pick your code into other method and wrap that with exception but handle that try-catch block with proper exception because it makes side effects / Buggy code. Rest of the things would be fine and work.

Just remember that do not handle exceptions inside the transaction atomic block.

Upvotes: 3

Related Questions