Clear cached children when binding an XModuleDescriptor/XBlock to a particular user

This commit is contained in:
Calen Pennington
2015-02-20 13:11:53 -05:00
parent 9379fd2711
commit de23d6defe
7 changed files with 52 additions and 17 deletions

View File

@@ -110,7 +110,7 @@ def get_test_system(course_id=SlashSeparatedCourseKey('org', 'course', 'run')):
# So, bind to the same one as the current descriptor.
module_system.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access
descriptor.bind_for_student(module_system, descriptor._field_data)
descriptor.bind_for_student(module_system, descriptor._field_data, user.id)
return descriptor

View File

@@ -58,7 +58,7 @@ class LibraryContentTest(MixedSplitTestCase):
sub_module_system = get_test_system(course_id=self.course.location.course_key)
sub_module_system.get_module = get_module
sub_module_system.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access
descriptor.bind_for_student(sub_module_system, descriptor._field_data) # pylint: disable=protected-access
descriptor.bind_for_student(sub_module_system, descriptor._field_data, self.user_id) # pylint: disable=protected-access
return descriptor
module_system.get_module = get_module

View File

@@ -81,6 +81,7 @@ class SplitTestModuleTest(XModuleXmlImportTest, PartitionTestCase):
self.module_system.descriptor_runtime = self.course._runtime # pylint: disable=protected-access
self.course.runtime.export_fs = MemoryFS()
user = Mock(username='ma', email='ma@edx.org', is_staff=False, is_active=True)
self.partitions_service = StaticPartitionService(
[
self.user_partition,
@@ -90,14 +91,18 @@ class SplitTestModuleTest(XModuleXmlImportTest, PartitionTestCase):
MockUserPartitionScheme()
)
],
user=Mock(username='ma', email='ma@edx.org', is_staff=False, is_active=True),
user=user,
course_id=self.course.id,
track_function=Mock(name='track_function'),
)
self.module_system._services['partitions'] = self.partitions_service # pylint: disable=protected-access
self.split_test_module = self.course_sequence.get_children()[0]
self.split_test_module.bind_for_student(self.module_system, self.split_test_module._field_data) # pylint: disable=protected-access
self.split_test_module.bind_for_student(
self.module_system,
self.split_test_module._field_data, # pylint: disable=protected-access
user.id
)
@ddt.ddt

View File

@@ -17,8 +17,11 @@ from webob import Response
from webob.multidict import MultiDict
from xblock.core import XBlock, XBlockAside
from xblock.fields import Scope, Integer, Float, List, XBlockMixin, String, Dict, ScopeIds, Reference, \
ReferenceList, ReferenceValueDict
from xblock.fields import (
Scope, Integer, Float, List, XBlockMixin,
String, Dict, ScopeIds, Reference, ReferenceList,
ReferenceValueDict, UserScope
)
from xblock.fragment import Fragment
from xblock.runtime import Runtime, IdReader, IdGenerator
from xmodule.fields import RelativeTime
@@ -281,6 +284,8 @@ class XModuleMixin(XBlockMixin):
def __init__(self, *args, **kwargs):
self.xmodule_runtime = None
self._child_instances = None
super(XModuleMixin, self).__init__(*args, **kwargs)
@property
@@ -410,7 +415,7 @@ class XModuleMixin(XBlockMixin):
if not self.has_children:
return []
if getattr(self, '_child_instances', None) is None:
if self._child_instances is None:
self._child_instances = [] # pylint: disable=attribute-defined-outside-init
for child_loc in self.children:
# Skip if it doesn't satisfy the filter function
@@ -525,15 +530,39 @@ class XModuleMixin(XBlockMixin):
"""
return None
def bind_for_student(self, xmodule_runtime, field_data):
def bind_for_student(self, xmodule_runtime, field_data, user_id):
"""
Set up this XBlock to act as an XModule instead of an XModuleDescriptor.
Arguments:
xmodule_runtime (:class:`ModuleSystem'): the runtime to use when accessing student facing methods
field_data (:class:`FieldData`): The :class:`FieldData` to use for all subsequent data access
user_id: The user_id to set in scope_ids
"""
# pylint: disable=attribute-defined-outside-init
# Skip rebinding if we're already bound a user, and it's this user.
if self.scope_ids.user_id is not None and user_id == self.scope_ids.user_id:
return
# If we are switching users mid-request, save the data from the old user.
self.save()
# Update scope_ids to point to the new user.
self.scope_ids = self.scope_ids._replace(user_id=user_id)
# Clear out any cached instantiated children.
self._child_instances = None
# Clear out any cached field data scoped to the old user.
for field in self.fields.values():
if field.scope in (Scope.parent, Scope.children):
continue
if field.scope.user == UserScope.ONE:
field._del_cached_value(self)
# Set the new xmodule_runtime and field_data (which are user-specific)
self.xmodule_runtime = xmodule_runtime
self._field_data = field_data