I have the following models (I’m leaving out all fields but the ones relevant to the issue):
class Exercise(models.Model):
# ...
class Choice(models.Model):
# ...
class AssignedExercise(models.Model):
quiz_instance = models.ForeignKey(...)
exercise = models.ForeignKey(Exercise)
answered_at = models.DateTimeField(null=True, blank=True)
selected_choices = models.ManyToManyField(
Choice,
blank=True,
)
When a user participates into a quiz, some AssignedExercise
s are created and assigned to that instance of the quiz.
For each AssignedExercise
, the selected choices for that exercise are stored in the selected_choice
field. I’m looking for a way to update answered_at
with the timestamp of the first time the user selects a choice. The selected_choice
field gets updated via a PATCH request to the relative AssignedExercise
(I’m using DRF), and atomic requests are active.
This works in local (sqlite):
# save method of AssignedExercise
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
# run on transaction commit because this checks whether a
# selected choice exists but the record won't be visible yet
# as this method is executed while still inside a transaction
def update_answered_at_if_answer_exists():
if self.selected_choices.exists():
now = timezone.localtime(timezone.now())
self.answered_at = now
self.save(update_fields=["answered_at"])
if self.answered_at is None:
transaction.on_commit(update_answered_at_if_answer_exists)
However, in production (postgres), this doesn’t work as expected. The first time an answer is selected, none is detected. The field answered_at
only ever gets a value if the user changes their mind and selects another choice (I’m assuming this time exists
will correclty detect the previous selected choice).
I have a feeling this has something to do with transactions, and I was confident running the method on commit would solve the issue, but this isn’t the case.
Any ideas?