Source code for dynamicforms.renderers
import copy
import six
from django.template import loader
from django.utils.safestring import mark_safe
from rest_framework.renderers import HTMLFormRenderer, TemplateHTMLRenderer
from rest_framework.serializers import HiddenField, ListSerializer
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
from dynamicforms.fields import BooleanField
from .settings import DYNAMICFORMS
from .template_render import ComponentDefRenderer, ComponentHTMLRenderer # noqal
# noinspection PyRedeclaration
[docs]class TemplateHTMLRenderer(TemplateHTMLRenderer):
"""
Renderers serializer data into an HTML form.
If the serializer was instantiated without an object then this will
return an HTML form not bound to any object,
otherwise it will return an HTML form with the appropriate initial data
populated from the object.
Note that rendering of field and form errors is not currently supported.
"""
[docs] def render(self, data, accepted_media_type=None, renderer_context=None):
link_next = link_prev = ''
if isinstance(data, dict) and 'next' in data and 'results' in data and \
isinstance(data['results'], (ReturnList, ReturnDict)):
# This is in case of Pagination
link_next = mark_safe(data.get('next', ''))
link_prev = mark_safe(data.get('previous', ''))
data = data['results']
if isinstance(data, (ReturnList, ReturnDict)):
ser = data.serializer
data = dict(data=data, serializer=ser.child if isinstance(ser, ListSerializer) else ser,
link_next=link_next, link_prev=link_prev)
if getattr(ser, '_errors', {}):
# CAUTION: bad hacks start here. This should be removed at some point
# unmark exception from response because this was a validation error
# This will allow TemplateHTMLRenderer to still render the template as if it was without problems
# If this is not done, the only result user will see will be a 404 error without any details
response = renderer_context['response']
response.exception = False
# result data should be object data otherwise nothing will render...
data['data'] = data['serializer'].data
return super().render(data, accepted_media_type, renderer_context)
def get_template_names(self, response, view):
if view.render_type == 'page' and DYNAMICFORMS.page_template:
return [DYNAMICFORMS.page_template]
return super().get_template_names(response, view)
def get_template_context(self, data, renderer_context):
res = super().get_template_context(data, renderer_context)
view = renderer_context['view']
if hasattr(view, 'template_context'):
if callable(view.template_context):
res.update(view.template_context())
else:
res.update(view.template_context)
res['df_render_type'] = view.render_type # This one should not fail because it's set in initialize_request
res['DYNAMICFORMS'] = DYNAMICFORMS
return res
# noinspection PyRedeclaration
[docs]class HTMLFormRenderer(HTMLFormRenderer):
"""
An HTML renderer for use with templates.
The data supplied to the Response object should be a dictionary that will
be used as context for the template.
The template name is determined by (in order of preference):
1. An explicit `.template_name` attribute set on the response.
2. An explicit `.template_name` attribute set on this class.
3. The return result of calling `view.get_template_names()`.
For example:
data = {'users': User.objects.all()}
return Response(data, template_name='users.html')
"""
def render_field(self, field, parent_style):
# noinspection PyProtectedMember
if isinstance(field._field, HiddenField):
return ''
if isinstance(field._field, BooleanField):
# allow also null to have max values available for determening the right value to render
allow_null = copy.copy(field._field.allow_null)
field._field.allow_null = True
field.value = field._field.to_internal_value(field.value)
field._field.allow_null = allow_null
style = dict(self.default_style[field])
style.update(field.style)
if 'template_pack' not in style:
style['template_pack'] = parent_style.get('template_pack', self.template_pack)
style['serializer'] = parent_style.get('serializer', None)
style['renderer'] = self
# Get a clone of the field with text-only value representation.
field = field.as_form_field()
if style.get('input_type') == 'datetime' and isinstance(field.value, six.text_type):
field.value = field.value.rstrip('Z')
if 'template' in style:
template_name = style['template']
else:
template_name = style['template_pack'].strip('/') + '/' + style['base_template']
template = loader.get_template(template_name)
context = {
'field': field,
'style': style,
'DYNAMICFORMS': DYNAMICFORMS,
}
try:
context.update(style['serializer'].context['view'].template_context)
except:
pass
return template.render(context)
# Jure: had to copy this one over to support custom template as parameter + DF context variable
[docs] def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render serializer data and return an HTML form, as a string.
"""
renderer_context = renderer_context or {}
form = data.serializer
style = renderer_context.get('style', {})
style['template_pack'] = DYNAMICFORMS.template_root + 'field'
style['renderer'] = self
template_pack = style['template_pack'].strip('/')
# getting the template to use
template_name = next((x for x in ( # Get the first specified template name as encountered
style.get('form_template', None), # template name from tag parameter
getattr(self, 'form_template', None), # if ViewSet set the form_template member of this renderer
getattr(form, 'form_template', None), # if Serializer has form_template member set
template_pack + '/' + self.base_template # take default template from pack
) if x))
template = loader.get_template(template_name)
context = {
'form': form,
'style': style,
'DYNAMICFORMS': DYNAMICFORMS,
}
context.update(getattr(form, 'template_context', {}))
try:
context.update(form.context['view'].template_context)
except:
pass
return template.render(context)