Django - Задержка репликации
Пререквизиты:
class Task(models.Model):
class TaskStatus(models.TextChoices):
HIDDEN = 'hidden', _('hidden')
NEW = 'new', _('new')
IN_PROGRESS = 'in_progress', _('in progress')
DONE = 'done', _('done')
FAILED = 'failed', _('failed')
PLANNED = 'planned', _('planned')
REVOKED = 'revoked', _('revoked')
title = models.CharField(max_length=500)
performer = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.CharField(choices=TaskStatus.choices, default=TaskStatus.HIDDEN, max_length=25)
class TaskTransition(models.Model):
task = models.ForeignKey('tasks.Task', on_delete=models.CASCADE, related_name='transitions')
actor = models.ForeignKey(User, on_delete=models.RESTRICT, related_name='transitions')
old_status = models.CharField(choices=TaskStatus.choices, max_length=25)
new_status = models.CharField(choices=TaskStatus.choices, max_length=25)
class TaskView(ModelViewSet):
queryset = Task.objects.all()
serializer = TaskSerializer
class CreateTaskTransitionView(mixins.RetrieveModelMixin, generics.CreateAPIView):
serializer_class = CreateTaskTransitionSerializer
class CreateTaskTransitionSerializer(serializers.ModelSerializer):
task = serializers.HiddenField(default=HiddenTaskField())
actor = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = TaskTransition
fields = '__all__'
def validate(self, attrs):
# allowed status transitions:
# hidden > new
# new > in_progress, failed
# in_progress > done, failed
return attrs
def save(self, **kwargs):
task = self.validated_data['task']
new_status = self.validated_data['new_status']
TaskTransition.objects.create(
task=task,
new_status=new_status,
old_status=task.status,
actor=self.validated_data['actor']
)
task.status = new_status
task.save()
return {}
Мы используем две базы данных. Первая default для записи и replica для чтения.
Из-за валидации, как вы видите, может быть выполнен только один уникальный переход задачи. Но иногда случается, что может быть несколько одинаковых переходов. Например, 5 переходов in_progress > done.
При получении экземпляра задачи задача имеет неактуальный статус.
Очевидно, что это происходит из-за задержки репликации: в CreateTaskTransitionSerializer задача читается из реплики с не обновленным (актуальным) статусом.
Так что я подумал, что для решения этой проблемы retrieve действие TaskView должно читаться из default, верно? Если да, то как я могу это сделать? Примерно так:
class TaskView(models.Model):
def get_queryset(self):
qs = super().get_queryset(request)
if self.action == 'retrieve':
qs = qs.using('default')
return qs
?