Skip to content

Commit

Permalink
Merge pull request #302 from cpennington/store-parent-on-load
Browse files Browse the repository at this point in the history
Pass parent to xblock on load
  • Loading branch information
cpennington committed Jun 26, 2015
2 parents e1831fa + 2cf79c3 commit 74fdc5a
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 8 deletions.
5 changes: 2 additions & 3 deletions xblock/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def load_tagged_classes(cls, tag, fail_silently=True):
if tag in class_._class_tags:
yield name, class_

def __init__(self, runtime, field_data=None, scope_ids=UNSET):
def __init__(self, runtime, field_data=None, scope_ids=UNSET, *args, **kwargs):
"""
Construct a new XBlock.
Expand All @@ -153,13 +153,12 @@ def __init__(self, runtime, field_data=None, scope_ids=UNSET):
scope_ids (:class:`.ScopeIds`): Identifiers needed to resolve
scopes.
"""
if scope_ids is UNSET:
raise TypeError('scope_ids are required')

# Provide backwards compatibility for external access through _field_data
super(XBlock, self).__init__(runtime=runtime, scope_ids=scope_ids, field_data=field_data)
super(XBlock, self).__init__(runtime=runtime, scope_ids=scope_ids, field_data=field_data, *args, **kwargs)

def render(self, view, context=None):
"""Render `view` with this block's runtime and the supplied `context`"""
Expand Down
3 changes: 3 additions & 0 deletions xblock/field_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,6 @@ def has(self, block, name):

def default(self, block, name):
return self._source.default(block, name)

def __repr__(self):
return "ReadOnlyFieldData({!r})".format(self._source)
42 changes: 41 additions & 1 deletion xblock/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,19 +332,59 @@ def __init__(self, **kwargs):
# A cache of the parent block, retrieved from .parent
self._parent_block = None
self._parent_block_id = None
self._child_cache = {}

for_parent = kwargs.pop('for_parent', None)

if for_parent is not None:
self._parent_block = for_parent
self._parent_block_id = for_parent.scope_ids.usage_id

super(HierarchyMixin, self).__init__(**kwargs)

def get_parent(self):
"""Return the parent block of this block, or None if there isn't one."""
if self._parent_block_id != self.parent:
if not self.has_cached_parent:
if self.parent is not None:
self._parent_block = self.runtime.get_block(self.parent)
else:
self._parent_block = None
self._parent_block_id = self.parent
return self._parent_block

@property
def has_cached_parent(self):
"""Return whether this block has a cached parent block."""
return self.parent is not None and self._parent_block_id == self.parent

def get_child(self, usage_id):
"""Return the child identified by ``usage_id``."""
if usage_id in self._child_cache:
return self._child_cache[usage_id]

child_block = self.runtime.get_block(usage_id, for_parent=self)
self._child_cache[usage_id] = child_block
return child_block

def get_children(self, usage_id_filter=None):
"""
Return instantiated XBlocks for each of this blocks ``children``.
"""
if not self.has_children:
return []

return [
self.get_child(usage_id)
for usage_id in self.children
if usage_id_filter is None or usage_id_filter(usage_id)
]

def clear_child_cache(self):
"""
Reset the cache of children stored on this XBlock.
"""
self._child_cache.clear()


class XmlSerializationMixin(ScopedStorageMixin):
"""
Expand Down
6 changes: 3 additions & 3 deletions xblock/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def __init__(self, kvs, **kwargs):
self._kvs = kvs

def __repr__(self):
return "<{0.__class__.__name__} {0._kvs!r}>".format(self)
return "{0.__class__.__name__}({0._kvs!r})".format(self)

def _getfield(self, block, name):
"""
Expand Down Expand Up @@ -635,7 +635,7 @@ def construct_xblock_from_class(self, cls, scope_ids, field_data=None, *args, **
*args, **kwargs
)

def get_block(self, usage_id):
def get_block(self, usage_id, for_parent=None):
"""
Create an XBlock instance in this runtime.
Expand All @@ -647,7 +647,7 @@ def get_block(self, usage_id):
except NoSuchDefinition:
raise NoSuchUsage(repr(usage_id))
keys = ScopeIds(self.user_id, block_type, def_id, usage_id)
block = self.construct_xblock(block_type, keys)
block = self.construct_xblock(block_type, keys, for_parent=for_parent)
return block

def get_aside(self, aside_usage_id):
Expand Down
3 changes: 2 additions & 1 deletion xblock/test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,8 @@ def test_basic(self):
self.id_reader.get_block_type.assert_called_with(self.def_id)
self.construct_block.assert_called_with(
self.block_type,
ScopeIds(self.user_id, self.block_type, self.def_id, self.usage_id)
ScopeIds(self.user_id, self.block_type, self.def_id, self.usage_id),
for_parent=None,
)

def test_missing_usage(self):
Expand Down

0 comments on commit 74fdc5a

Please sign in to comment.