Cache Enrollment state for (user, course) in request cache.
This commit is contained in:
@@ -10,7 +10,7 @@ file and check it in at the same time as your model changes. To do that,
|
||||
2. ./manage.py lms schemamigration student --auto description_of_your_change
|
||||
3. Add the migration file created in edx-platform/common/djangoapps/student/migrations/
|
||||
"""
|
||||
from collections import defaultdict, OrderedDict
|
||||
from collections import defaultdict, OrderedDict, namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
from functools import total_ordering
|
||||
import hashlib
|
||||
@@ -54,6 +54,7 @@ from enrollment.api import _default_course_mode
|
||||
import lms.lib.comment_client as cc
|
||||
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
import request_cache
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from util.model_utils import emit_field_changed_events, get_changed_fields_dict
|
||||
from util.query import use_read_replica_if_available
|
||||
@@ -974,6 +975,12 @@ class CourseEnrollmentManager(models.Manager):
|
||||
)
|
||||
|
||||
|
||||
# Named tuple for fields pertaining to the state of
|
||||
# CourseEnrollment for a user in a course. This type
|
||||
# is used to cache the state in the request cache.
|
||||
CourseEnrollmentState = namedtuple('CourseEnrollmentState', 'mode, is_active')
|
||||
|
||||
|
||||
class CourseEnrollment(models.Model):
|
||||
"""
|
||||
Represents a Student's Enrollment record for a single Course. You should
|
||||
@@ -1120,6 +1127,11 @@ class CourseEnrollment(models.Model):
|
||||
|
||||
if activation_changed or mode_changed:
|
||||
self.save()
|
||||
self._update_enrollment_in_request_cache(
|
||||
self.user,
|
||||
self.course_id,
|
||||
CourseEnrollmentState(self.mode, self.is_active),
|
||||
)
|
||||
|
||||
if activation_changed:
|
||||
if self.is_active:
|
||||
@@ -1389,12 +1401,9 @@ class CourseEnrollment(models.Model):
|
||||
"""
|
||||
if not user.is_authenticated():
|
||||
return False
|
||||
|
||||
try:
|
||||
record = cls.objects.get(user=user, course_id=course_key)
|
||||
return record.is_active
|
||||
except cls.DoesNotExist:
|
||||
return False
|
||||
else:
|
||||
enrollment_state = cls._get_enrollment_state(user, course_key)
|
||||
return enrollment_state.is_active or False
|
||||
|
||||
@classmethod
|
||||
def is_enrolled_by_partial(cls, user, course_id_partial):
|
||||
@@ -1436,11 +1445,8 @@ class CourseEnrollment(models.Model):
|
||||
and is_active is whether the enrollment is active.
|
||||
Returns (None, None) if the courseenrollment record does not exist.
|
||||
"""
|
||||
try:
|
||||
record = cls.objects.get(user=user, course_id=course_id)
|
||||
return (record.mode, record.is_active)
|
||||
except cls.DoesNotExist:
|
||||
return (None, None)
|
||||
enrollment_state = cls._get_enrollment_state(user, course_id)
|
||||
return enrollment_state.mode, enrollment_state.is_active
|
||||
|
||||
@classmethod
|
||||
def enrollments_for_user(cls, user):
|
||||
@@ -1593,6 +1599,45 @@ class CourseEnrollment(models.Model):
|
||||
"""
|
||||
return cls.COURSE_ENROLLMENT_CACHE_KEY.format(user_id, unicode(course_key))
|
||||
|
||||
@classmethod
|
||||
def _get_enrollment_state(cls, user, course_key):
|
||||
"""
|
||||
Returns the CourseEnrollmentState for the given user
|
||||
and course_key, caching the result for later retrieval.
|
||||
"""
|
||||
enrollment_state = cls._get_enrollment_in_request_cache(user, course_key)
|
||||
if not enrollment_state:
|
||||
try:
|
||||
record = cls.objects.get(user=user, course_id=course_key)
|
||||
enrollment_state = CourseEnrollmentState(record.mode, record.is_active)
|
||||
except cls.DoesNotExist:
|
||||
enrollment_state = CourseEnrollmentState(None, None)
|
||||
cls._update_enrollment_in_request_cache(user, course_key, enrollment_state)
|
||||
return enrollment_state
|
||||
|
||||
@classmethod
|
||||
def _get_mode_active_request_cache(cls):
|
||||
"""
|
||||
Returns the request-specific cache for CourseEnrollment
|
||||
"""
|
||||
return request_cache.get_cache('CourseEnrollment.mode_and_active')
|
||||
|
||||
@classmethod
|
||||
def _get_enrollment_in_request_cache(cls, user, course_key):
|
||||
"""
|
||||
Returns the cached value (CourseEnrollmentState) for the user's
|
||||
enrollment in the request cache. If not cached, returns None.
|
||||
"""
|
||||
return cls._get_mode_active_request_cache().get((user.id, course_key))
|
||||
|
||||
@classmethod
|
||||
def _update_enrollment_in_request_cache(cls, user, course_key, enrollment_state):
|
||||
"""
|
||||
Updates the cached value for the user's enrollment in the
|
||||
request cache.
|
||||
"""
|
||||
cls._get_mode_active_request_cache()[(user.id, course_key)] = enrollment_state
|
||||
|
||||
|
||||
@receiver(models.signals.post_save, sender=CourseEnrollment)
|
||||
@receiver(models.signals.post_delete, sender=CourseEnrollment)
|
||||
|
||||
@@ -6,7 +6,6 @@ from datetime import datetime, timedelta
|
||||
import json
|
||||
import logging
|
||||
import unittest
|
||||
from urlparse import urljoin
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
@@ -30,6 +29,7 @@ from certificates.tests.factories import GeneratedCertificateFactory # pylint:
|
||||
from config_models.models import cache
|
||||
from course_modes.models import CourseMode
|
||||
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.programs.tests import factories as programs_factories
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
|
||||
@@ -589,7 +589,7 @@ class EnrollmentEventTestMixin(EventTestMixin):
|
||||
self.mock_tracker.reset_mock()
|
||||
|
||||
|
||||
class EnrollInCourseTest(EnrollmentEventTestMixin, TestCase):
|
||||
class EnrollInCourseTest(EnrollmentEventTestMixin, CacheIsolationTestCase):
|
||||
"""Tests enrolling and unenrolling in courses."""
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
|
||||
Reference in New Issue
Block a user