def render_content_items(request, items, template_name=None): """ Render a list of :class:`~fluent_contents.models.ContentItem` objects as HTML string. This is a variation of the :func:`render_placeholder` function. Note that the items are not filtered in any way by parent or language. The items are rendered as-is. :param request: The current request object. :type request: :class:`~django.http.HttpRequest` :param items: The list or queryset of objects to render. Passing a queryset is preferred. :type items: list or queryset of :class:`~fluent_contents.models.ContentItem`. :param template_name: Optional template name used to concatenate the placeholder output. :type template_name: str :rtype: :class:`~fluent_contents.models.ContentItemOutput` """ if not items: output = ContentItemOutput(mark_safe(u"<!-- no items to render -->")) else: output = _render_items(request, None, items, template_name=template_name) if is_edit_mode(request): output.html = _wrap_anonymous_output(output.html) return output
def render_content_items(request, items, template_name=None, cachable=None): """ Render a list of :class:`~fluent_contents.models.ContentItem` objects as HTML string. This is a variation of the :func:`render_placeholder` function. Note that the items are not filtered in any way by parent or language. The items are rendered as-is. :param request: The current request object. :type request: :class:`~django.http.HttpRequest` :param items: The list or queryset of objects to render. Passing a queryset is preferred. :type items: list or queryset of :class:`~fluent_contents.models.ContentItem`. :param template_name: Optional template name used to concatenate the placeholder output. :type template_name: str :param cachable: Whether the output is cachable, otherwise the full output will not be cached. Default: False when using a template, True otherwise. :type cachable: bool | None :rtype: :class:`~fluent_contents.models.ContentItemOutput` """ if not items: output = ContentItemOutput(mark_safe(u"<!-- no items to render -->")) else: output = RenderingPipe(request).render_items( placeholder=None, items=items, parent_object=None, template_name=template_name, cachable=cachable) # Wrap the result after it's stored in the cache. if markers.is_edit_mode(request): output.html = markers.wrap_anonymous_output(output.html) return output
def render_content_items(request, items, template_name=None, cachable=None): """ Render a list of :class:`~fluent_contents.models.ContentItem` objects as HTML string. This is a variation of the :func:`render_placeholder` function. Note that the items are not filtered in any way by parent or language. The items are rendered as-is. :param request: The current request object. :type request: :class:`~django.http.HttpRequest` :param items: The list or queryset of objects to render. Passing a queryset is preferred. :type items: list or queryset of :class:`~fluent_contents.models.ContentItem`. :param template_name: Optional template name used to concatenate the placeholder output. :type template_name: str :param cachable: Whether the output is cachable, otherwise the full output will not be cached. Default: False when using a template, True otherwise. :type cachable: bool | None :rtype: :class:`~fluent_contents.models.ContentItemOutput` """ if not items: output = ContentItemOutput(mark_safe(u"<!-- no items to render -->")) else: output = _render_items(request, None, items, parent_object=None, template_name=template_name, cachable=cachable) if is_edit_mode(request): output.html = _wrap_anonymous_output(output.html) return output
def render_content_items(request, items, template_name=None): """ Render a list of :class:`~fluent_contents.models.ContentItem` objects as HTML string. This is a variation of the :func:`render_placeholder` function. Note that the items are not filtered in any way by parent or language. The items are rendered as-is. """ if not items: output = ContentItemOutput(mark_safe(u"<!-- no items to render -->")) else: output = _render_items(request, None, items, template_name=template_name) if is_edit_mode(request): output.html = _wrap_anonymous_output(output.html) return output
def _render_items(request, placeholder, items, parent_object=None, template_name=None, cachable=False): edit_mode = is_edit_mode(request) item_output = {} output_ordering = [] all_cacheable = True all_timeout = DEFAULT_TIMEOUT placeholder_cache_name = "@global@" if placeholder is None else placeholder.slot if not hasattr(items, "non_polymorphic"): # The items is either a list of manually created items, or it's a QuerySet. # Can't prevent reading the subclasses only, so don't bother with caching here. remaining_items = items output_ordering = [item.pk or id(item) for item in items] else: # Unless it was done before, disable polymorphic effects. if not items.polymorphic_disabled: items = items.non_polymorphic() # First try to fetch all items non-polymorphic from memcache # If these are found, there is no need to query the derived data from the database. remaining_items = [] for i, contentitem in enumerate(items): output_ordering.append(contentitem.pk) output = None # Respect the cache output setting of the plugin if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT: try: plugin = contentitem.plugin except PluginNotFound: pass else: if plugin.cache_output and contentitem.pk: output = plugin.get_cached_output(placeholder_cache_name, contentitem) all_timeout = _min_timeout(all_timeout, plugin.cache_timeout) # Support transition to new output format. if output is not None and not isinstance(output, ContentItemOutput): output = None logger.debug( "Flushed cached output of {0}#{1} to store new ContentItemOutput format (key: {2})".format( plugin.type_name, contentitem.pk, placeholder_cache_name ) ) # For debugging, ignore cached values when the template is updated. if output and settings.DEBUG: cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem) if _is_template_updated(request, contentitem, cachekey): output = None if output: item_output[contentitem.pk] = output else: remaining_items.append(contentitem) # Fetch derived table data for all objects not found in memcached if remaining_items: remaining_items = items.get_real_instances(remaining_items) # See if the queryset contained anything. # This test is moved here, to prevent earlier query execution. if not items: placeholder_name = _get_placeholder_name(placeholder) logger.debug("- no items in placeholder '%s'", placeholder_name) return ContentItemOutput(mark_safe(u"<!-- no items in placeholder '{0}' -->".format(escape(placeholder_name)))) elif remaining_items: # Render remaining items for contentitem in remaining_items: try: plugin = contentitem.plugin except PluginNotFound as e: output = ContentItemOutput(mark_safe(u"<!-- error: {0} -->\n".format(str(e)))) logger.debug("- item #%s has no matching plugin: %s", contentitem.pk, str(e)) else: if plugin.render_ignore_item_language or (plugin.cache_output and plugin.cache_output_per_language): # Render the template in the current language. # The cache also stores the output under the current language code. # # It would make sense to apply this for fallback content too, # but that would be ambiguous however because the parent_object could also be a fallback, # and that case can't be detected here. Hence, better be explicit when desiring multi-lingual content. render_language = get_language() # Avoid switching the content, else: # Render the template in the ContentItem language. # This makes sure that {% trans %} tag output matches the language of the model field data. render_language = contentitem.language_code with smart_override(render_language): # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping. # This is just like Django's Input.render() and unlike Node.render(). output = plugin._render_contentitem(request, contentitem) if ( appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and output.cacheable and contentitem.pk ): # Cache the output contentitem.plugin.set_cached_output(placeholder_cache_name, contentitem, output) else: # Item blocks caching the complete placeholder. all_cacheable = False if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT: logger.debug("- item #%s is NOT cachable! Prevented by %r", contentitem.pk, plugin) if edit_mode: output.html = _wrap_contentitem_output(output.html, contentitem) item_id = contentitem.pk or id(contentitem) item_output[item_id] = output # Order all rendered items in the correct sequence. The derived tables could be truncated/reset, # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling. # # The media is also collected in the same ordering, in case it's handled by django-compressor for example. output_ordered = [] merged_media = Media() for pk in output_ordering: try: output = item_output[pk] output_ordered.append(output.html) _add_media(merged_media, output.media) except KeyError: # The get_real_instances() didn't return an item for the derived table. This happens when either: # - that table is truncated/reset, while there is still an entry in the base ContentItem table. # A query at the derived table happens every time the page is being rendered. # - the model was completely removed which means there is also a stale ContentType object. item = next(item for item in items if item.pk == pk) try: class_name = item.plugin.type_name except PluginNotFound: # Derived table isn't there because the model has been removed. # There is a stale ContentType object, no plugin associated or loaded. class_name = "content type is stale" output_ordered.append( u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n".format(id=pk, cls=class_name) ) logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=class_name)) pass # Combine all rendered items. Allow rendering the items with a template, # to inserting separators or nice start/end code. if not template_name: merged_output = mark_safe("".join(output_ordered)) else: context = { "contentitems": list(zip(items, output_ordered)), "parent_object": parent_object, # Can be None "edit_mode": edit_mode, } merged_output = render_to_string(template_name, context, context_instance=RequestContext(request)) # By default, cachable is False for templates. # Template name is ambiguous, can't reliable expire. # Nor can be determined whether the template is consistent or not cacheable. if not cachable: all_cacheable = False return ContentItemOutput(merged_output, merged_media, cacheable=all_cacheable, cache_timeout=all_timeout)
def _render_items(request, placeholder, items, parent_object=None, template_name=None): edit_mode = is_edit_mode(request) item_output = {} output_ordering = [] placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot if not hasattr(items, "non_polymorphic"): # The items is either a list of manually created items, or it's a QuerySet. # Can't prevent reading the subclasses only, so don't bother with caching here. remaining_items = items output_ordering = [item.pk or id(item) for item in items] else: items = items.non_polymorphic() # First try to fetch all items non-polymorphic from memcache # If these are found, there is no need to query the derived data from the database. remaining_items = [] for i, contentitem in enumerate(items): output_ordering.append(contentitem.pk) output = None try: # Respect the cache output setting of the plugin if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and contentitem.plugin.cache_output and contentitem.pk: output = contentitem.plugin.get_cached_output(placeholder_cache_name, contentitem) # Support transition to new output format. if not isinstance(output, ContentItemOutput): output = None logger.debug("Flushed cached output of {0}#{1} to store new format (key: {2}) ".format(contentitem.plugin.type_name, contentitem.pk, placeholder_cache_name)) except PluginNotFound: pass # For debugging, ignore cached values when the template is updated. if output and settings.DEBUG: cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem) if _is_template_updated(request, contentitem, cachekey): output = None if output: item_output[contentitem.pk] = output else: remaining_items.append(contentitem) # Fetch derived table data for all objects not found in memcached if remaining_items: remaining_items = items.get_real_instances(remaining_items) # See if the queryset contained anything. # This test is moved here, to prevent earlier query execution. if not items: return ContentItemOutput(mark_safe(u"<!-- no items in placeholder '{0}' -->".format(escape(_get_placeholder_name(placeholder))))) elif remaining_items: # Render remaining items for contentitem in remaining_items: try: plugin = contentitem.plugin except PluginNotFound as e: output = ContentItemOutput(mark_safe(u'<!-- error: {0} -->\n'.format(str(e)))) else: # Always try to render the template in the ContentItem language. # This makes sure that {% trans %} tags function properly if the language happens to be different. with smart_override(contentitem.language_code): # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping. # This is just like Django's Input.render() and unlike Node.render(). output = plugin._render_contentitem(request, contentitem) if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and contentitem.pk: contentitem.plugin.set_cached_output(placeholder_cache_name, contentitem, output) if edit_mode: output.html = _wrap_contentitem_output(output.html, contentitem) item_id = contentitem.pk or id(contentitem) item_output[item_id] = output # Order all rendered items in the correct sequence. The derived tables could be truncated/reset, # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling. # # The media is also collected in the same ordering, in case it's handled by django-compressor for example. output_ordered = [] merged_media = Media() for pk in output_ordering: try: output = item_output[pk] output_ordered.append(output.html) _add_media(merged_media, output.media) except KeyError: # The get_real_instances() didn't return an item for the derived table. This happens when either: # - that table is truncated/reset, while there is still an entry in the base ContentItem table. # A query at the derived table happens every time the page is being rendered. # - the model was completely removed which means there is also a stale ContentType object. item = next(item for item in items if item.pk == pk) try: class_name = item.plugin.type_name except PluginNotFound: # Derived table isn't there because the model has been removed. # There is a stale ContentType object, no plugin associated or loaded. class_name = 'content type is stale' output_ordered.append(u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n".format(id=pk, cls=class_name)) logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=class_name)) pass # Combine all rendered items. Allow rendering the items with a template, # to inserting separators or nice start/end code. if not template_name: merged_output = mark_safe(''.join(output_ordered)) else: context = { 'contentitems': list(zip(items, output_ordered)), 'parent_object': parent_object, # Can be None 'edit_mode': edit_mode, } merged_output = render_to_string(template_name, context, context_instance=RequestContext(request)) return ContentItemOutput(merged_output, merged_media)
def _render_items(request, placeholder, items, parent_object=None, template_name=None, cachable=False): edit_mode = is_edit_mode(request) item_output = {} output_ordering = [] all_cacheable = True all_timeout = DEFAULT_TIMEOUT placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot if not hasattr(items, "non_polymorphic"): # The items is either a list of manually created items, or it's a QuerySet. # Can't prevent reading the subclasses only, so don't bother with caching here. remaining_items = items output_ordering = [item.pk or id(item) for item in items] else: # Unless it was done before, disable polymorphic effects. if not items.polymorphic_disabled: items = items.non_polymorphic() # First try to fetch all items non-polymorphic from memcache # If these are found, there is no need to query the derived data from the database. remaining_items = [] for i, contentitem in enumerate(items): output_ordering.append(contentitem.pk) output = None # Respect the cache output setting of the plugin if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT: try: plugin = contentitem.plugin except PluginNotFound: pass else: if plugin.cache_output and contentitem.pk: output = plugin.get_cached_output( placeholder_cache_name, contentitem) all_timeout = _min_timeout(all_timeout, plugin.cache_timeout) # Support transition to new output format. if output is not None and not isinstance( output, ContentItemOutput): output = None logger.debug( "Flushed cached output of {0}#{1} to store new ContentItemOutput format (key: {2})" .format(plugin.type_name, contentitem.pk, placeholder_cache_name)) # For debugging, ignore cached values when the template is updated. if output and settings.DEBUG: cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem) if _is_template_updated(request, contentitem, cachekey): output = None if output: item_output[contentitem.pk] = output else: remaining_items.append(contentitem) # Fetch derived table data for all objects not found in memcached if remaining_items: remaining_items = items.get_real_instances(remaining_items) # See if the queryset contained anything. # This test is moved here, to prevent earlier query execution. if not items: placeholder_name = _get_placeholder_name(placeholder) logger.debug("- no items in placeholder '%s'", placeholder_name) return ContentItemOutput( mark_safe(u"<!-- no items in placeholder '{0}' -->".format( escape(placeholder_name)))) elif remaining_items: # Render remaining items for contentitem in remaining_items: try: plugin = contentitem.plugin except PluginNotFound as e: output = ContentItemOutput( mark_safe(u'<!-- error: {0} -->\n'.format(str(e)))) logger.debug("- item #%s has no matching plugin: %s", contentitem.pk, str(e)) else: if plugin.render_ignore_item_language \ or (plugin.cache_output and plugin.cache_output_per_language): # Render the template in the current language. # The cache also stores the output under the current language code. # # It would make sense to apply this for fallback content too, # but that would be ambiguous however because the parent_object could also be a fallback, # and that case can't be detected here. Hence, better be explicit when desiring multi-lingual content. render_language = get_language( ) # Avoid switching the content, else: # Render the template in the ContentItem language. # This makes sure that {% trans %} tag output matches the language of the model field data. render_language = contentitem.language_code with smart_override(render_language): # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping. # This is just like Django's Input.render() and unlike Node.render(). output = plugin._render_contentitem(request, contentitem) if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT \ and plugin.cache_output \ and output.cacheable \ and contentitem.pk: # Cache the output contentitem.plugin.set_cached_output( placeholder_cache_name, contentitem, output) else: # Item blocks caching the complete placeholder. all_cacheable = False if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT: logger.debug( "- item #%s is NOT cachable! Prevented by %r", contentitem.pk, plugin) if edit_mode: output.html = _wrap_contentitem_output( output.html, contentitem) item_id = contentitem.pk or id(contentitem) item_output[item_id] = output # Order all rendered items in the correct sequence. The derived tables could be truncated/reset, # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling. # # The media is also collected in the same ordering, in case it's handled by django-compressor for example. output_ordered = [] merged_media = Media() for pk in output_ordering: try: output = item_output[pk] output_ordered.append(output.html) _add_media(merged_media, output.media) except KeyError: # The get_real_instances() didn't return an item for the derived table. This happens when either: # - that table is truncated/reset, while there is still an entry in the base ContentItem table. # A query at the derived table happens every time the page is being rendered. # - the model was completely removed which means there is also a stale ContentType object. item = next(item for item in items if item.pk == pk) try: class_name = item.plugin.type_name except PluginNotFound: # Derived table isn't there because the model has been removed. # There is a stale ContentType object, no plugin associated or loaded. class_name = 'content type is stale' output_ordered.append( u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n" .format(id=pk, cls=class_name)) logger.warning( "Missing derived model for ContentItem #{id}: {cls}.".format( id=pk, cls=class_name)) pass # Combine all rendered items. Allow rendering the items with a template, # to inserting separators or nice start/end code. if not template_name: merged_output = mark_safe(''.join(output_ordered)) else: context = { 'contentitems': list(zip(items, output_ordered)), 'parent_object': parent_object, # Can be None 'edit_mode': edit_mode, } merged_output = render_to_string( template_name, context, context_instance=RequestContext(request)) # By default, cachable is False for templates. # Template name is ambiguous, can't reliable expire. # Nor can be determined whether the template is consistent or not cacheable. if not cachable: all_cacheable = False return ContentItemOutput(merged_output, merged_media, cacheable=all_cacheable, cache_timeout=all_timeout)
def _render_items(request, placeholder, items, template_name=None): edit_mode = is_edit_mode(request) item_output = {} output_ordering = [] placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot if not hasattr(items, "non_polymorphic"): # The items is either a list of manually created items, or it's a QuerySet. # Can't prevent reading the subclasses only, so don't bother with caching here. remaining_items = items output_ordering = [item.pk or id(item) for item in items] else: items = items.non_polymorphic() # First try to fetch all items non-polymorphic from memcache # If these are found, there is no need to query the derived data from the database. remaining_items = [] for i, contentitem in enumerate(items): output_ordering.append(contentitem.pk) output = None try: # Respect the cache output setting of the plugin if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and contentitem.plugin.cache_output and contentitem.pk: output = contentitem.plugin.get_cached_output( placeholder_cache_name, contentitem) # Support transition to new output format. if not isinstance(output, ContentItemOutput): output = None logger.debug( "Flushed cached output of {0}#{1} to store new format (key: {2}) " .format(contentitem.plugin.type_name, contentitem.pk, placeholder_cache_name)) except PluginNotFound: pass # For debugging, ignore cached values when the template is updated. if output and settings.DEBUG: cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem) if _is_template_updated(request, contentitem, cachekey): output = None if output: item_output[contentitem.pk] = output else: remaining_items.append(contentitem) # Fetch derived table data for all objects not found in memcached if remaining_items: remaining_items = items.get_real_instances(remaining_items) # See if the queryset contained anything. # This test is moved here, to prevent earlier query execution. if not items: return ContentItemOutput( mark_safe(u"<!-- no items in placeholder '{0}' -->".format( escape(_get_placeholder_name(placeholder))))) elif remaining_items: # Render remaining items for contentitem in remaining_items: try: plugin = contentitem.plugin except PluginNotFound as e: output = ContentItemOutput( mark_safe(u'<!-- error: {0} -->\n'.format(str(e)))) else: # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping. # This is just like Django's Input.render() and unlike Node.render(). output = plugin._render_contentitem(request, contentitem) if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and contentitem.pk: contentitem.plugin.set_cached_output( placeholder_cache_name, contentitem, output) if edit_mode: output.html = _wrap_contentitem_output( output.html, contentitem) item_id = contentitem.pk or id(contentitem) item_output[item_id] = output # Order all rendered items in the correct sequence. The derived tables could be truncated/reset, # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling. # # The media is also collected in the same ordering, in case it's handled by django-compressor for example. output_ordered = [] merged_media = Media() for pk in output_ordering: try: output = item_output[pk] output_ordered.append(output.html) _add_media(merged_media, output.media) except KeyError: # The get_real_instances() didn't return an item for the derived table. This happens when either: # - that table is truncated/reset, while there is still an entry in the base ContentItem table. # A query at the derived table happens every time the page is being rendered. # - the model was completely removed which means there is also a stale ContentType object. item = next(item for item in items if item.pk == pk) try: class_name = item.plugin.type_name except PluginNotFound: # Derived table isn't there because the model has been removed. # There is a stale ContentType object, no plugin associated or loaded. class_name = 'content type is stale' output_ordered.append( u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n" .format(id=pk, cls=class_name)) logger.warning( "Missing derived model for ContentItem #{id}: {cls}.".format( id=pk, cls=class_name)) pass # Combine all rendered items. Allow rendering the items with a template, # to inserting separators or nice start/end code. if not template_name: merged_output = mark_safe(''.join(output_ordered)) else: context = { 'contentitems': zip(items, output_ordered), 'edit_mode': edit_mode, } merged_output = render_to_string( template_name, context, context_instance=RequestContext(request)) return ContentItemOutput(merged_output, merged_media)