From 19d909228c2e2b1494bfd526e430eda0864a8f82 Mon Sep 17 00:00:00 2001 From: Zacharis278 Date: Tue, 6 Aug 2024 15:12:10 -0400 Subject: [PATCH] fix: bulk create on mysql --- edx_exams/apps/api/v1/views.py | 7 +------ edx_exams/apps/core/models.py | 22 +++++++++++++++++++++- edx_exams/apps/core/tests/test_models.py | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/edx_exams/apps/api/v1/views.py b/edx_exams/apps/api/v1/views.py index 20451c05..346c316c 100644 --- a/edx_exams/apps/api/v1/views.py +++ b/edx_exams/apps/api/v1/views.py @@ -873,12 +873,7 @@ def post(self, request, course_id): data={'detail': 'Exam does not exist'} ) - StudentAllowance.objects.bulk_create( - allowance_objects, - update_conflicts=True, - unique_fields=['user', 'exam'], - update_fields=['extra_time_mins'] - ) + StudentAllowance.bulk_create_or_update(allowance_objects) return Response(status=status.HTTP_200_OK) else: diff --git a/edx_exams/apps/core/models.py b/edx_exams/apps/core/models.py index dd8d805d..0477d376 100644 --- a/edx_exams/apps/core/models.py +++ b/edx_exams/apps/core/models.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import AbstractUser from django.core.exceptions import ObjectDoesNotExist -from django.db import models, transaction +from django.db import connection, models, transaction from django.db.models import Q from django.utils.translation import gettext_lazy as _ from model_utils.models import TimeStampedModel @@ -454,6 +454,26 @@ class Meta: verbose_name = 'student allowance' unique_together = ('user', 'exam') + @classmethod + def bulk_create_or_update(cls, allowances): + """ + Create or update multiple allowances. + + SQLite and Postgres have an additional requirement for bulk_create + when using update_conflicts=True. This app expects to run on MySQL + however our tests run on SQLite where 'unique_fields' is needed. + """ + unique_fields = None + if connection.features.supports_update_conflicts_with_target: + unique_fields = ['user', 'exam'] + + cls.objects.bulk_create( + allowances, + update_conflicts=True, + unique_fields=unique_fields, + update_fields=['extra_time_mins'], + ) + @classmethod def get_allowances_for_course(cls, course_id): """ diff --git a/edx_exams/apps/core/tests/test_models.py b/edx_exams/apps/core/tests/test_models.py index 9836f2cc..342963b8 100644 --- a/edx_exams/apps/core/tests/test_models.py +++ b/edx_exams/apps/core/tests/test_models.py @@ -148,6 +148,24 @@ def setUp(self): self.course_id = 'course-v1:edX+Test+Test_Course' self.exam = ExamFactory(course_id=self.course_id) + def test_bulk_create_or_update(self): + """ + Test bulk_create_or_update can create new allowances and update existing ones. + """ + conflict_user = UserFactory() + conflict_allowance = StudentAllowanceFactory(user=conflict_user, exam=self.exam, extra_time_mins=66) + allowances = [ + StudentAllowance(user=conflict_user, exam=self.exam, extra_time_mins=19), + StudentAllowance(user=UserFactory(), exam=self.exam, extra_time_mins=29), + StudentAllowance(user=UserFactory(), exam=self.exam, extra_time_mins=39), + ] + + self.assertEqual(StudentAllowance.objects.count(), 1) + StudentAllowance.bulk_create_or_update(allowances) + self.assertEqual(StudentAllowance.objects.count(), 3) + conflict_allowance.refresh_from_db() + self.assertEqual(conflict_allowance.extra_time_mins, 19) + def test_get_allowance_for_user(self): user = UserFactory() user_2 = UserFactory()