Generic forms (ModelForm) provide HTML for a form! Can generic views (DetailView) do same?

305 views
Skip to first unread message

Bernd Wechner

unread,
Aug 10, 2015, 7:17:54 AM8/10/15
to Django users
This has bamboozled me for a while. I Have a wonderful little test app I'm playing with which lists objects (ListView) and next to each object puts an edit and view link. Sort of like:
  • [view] [edit] Object 1
  • [view] [edit] Object 2
  • [view] [edit] Object 3

The ListView works like a dream. The ModelForm works a dream and on the edit link I can simply display the form by including {{ form }} in a generic template. What a beautifully fficient generic way of rendering quick test forms and such. Love it.


Now I want to do same with a DetailView, i.e. render a page that just shows me Object 1 say much like my ModelForm does but not as a form as a readonly display, using labels not fields:

The form say provides:

<tr><th><label for="id_prop1">Label 1:</label></th><td><input id="id_prop1" ... /></td></tr>
<tr><th><label for="id_prop2">Label 2:</label></th><td><input id="id_prop2" ... /></td></tr>
<tr><th><label for="id_prop3">Label 3:</label></th><td><input id="id_prop3" ... /></td></tr>
<tr><th><label for="id_prop4">Label 4:</label></th><td><input id="id_prop4" ... /></td></tr>



Can DetailView not provide me with something similar, like:

<tr><th>Label 1:</th><td>Value 1</td></tr>
<tr><th>Label 2:</th><td>Value 2</td></tr>
<tr><th>Label 3:</th><td>Value 3</td></tr>
<tr><th>Label 4:</th><td>Value 4</td></tr>

I find it hard to believe not, but danged if I can find it!

An odd inconsistency if so. Do I really have to derive a class DetailViewWithHTML from it and write my own wrappers?

Regards,

Bernd.


Bernd Wechner

unread,
Aug 12, 2015, 6:54:19 AM8/12/15
to Django users
OK, I give up, seriously dismayed that Django doesn't have this built in already I did as I threatened to and derived a class as follows:


from django.utils import six
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
from django.utils.encoding import force_text
from django.forms.models import model_to_dict, fields_for_model

class DetailViewWithHTML(DetailView):
    fields
= None
    field_data
= None
   
def __init__(self, instance):
       
self.fields = fields_for_model(self.model)
       
self.field_data = model_to_dict(instance)
                       
   
# HTML formatters stolen straight form the Django ModelForm class
   
def _html_output(self, normal_row, help_text_html):
       
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."      
        output
= []

       
for name, field in self.fields.items():
            value
= self.field_data[name]

           
if field.label:
                label
= conditional_escape(force_text(field.label))
           
else:
                label
= ''
             
           
if field.help_text:
                help_text
= help_text_html % force_text(field.help_text)
           
else:
                help_text
= ''
               
            output
.append(normal_row % {
                 
'label': force_text(label),
                 
'value': six.text_type(value),
                 
'help_text': help_text,
             
})

       
return mark_safe('\n'.join(output))

   
def as_table(self):
       
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
       
return self._html_output(
            normal_row
='<tr><th>%(label)s</th><td>%(value)s%(help_text)s</td></tr>',
            help_text_html
='<br /><span class="helptext">%s</span>')

   
def as_ul(self):
       
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
       
return self._html_output(
            normal_row
='<li>%(errors)s%(label)s %(value)s%(help_text)s</li>',
            help_text_html
=' <span class="helptext">%s</span>')

   
def as_p(self):
       
"Returns this form rendered as HTML <p>s."
       
return self._html_output(
            normal_row
='<p>%(label)s %(value)s%(help_text)s</p>',
            help_text_html
=' <span class="helptext">%s</span>')

It's a quick hack (if only, sheesh it took some time to work out how ModelForm does it and reduce it to the simplest of Data Viewers here) and hasn't been exhaustively tested or considered, and I remain blown away that DetailView doesn't implement these ... fail Django, fail.

OK before anyone gets all smart and asks "why would you need that anyway", I admit it's not a super powerful generic per se for production implementations, but sheesh it's nice to produce a quick instance viewer using the same paradigm as the edit form and not have to do something different.
    

Mike Dewhirst

unread,
Aug 12, 2015, 8:36:09 AM8/12/15
to django...@googlegroups.com
On 12/08/2015 8:54 PM, Bernd Wechner wrote:
> OK, I give up, seriously dismayed that Django doesn't have this built in
> already

Well done Bernd! Welcome to open source. Unfortunately, I can't use your
code unless it is part of Django. I have tried implementing my own
improvements in the past but I always encounter difficulties when the
next release of Django comes.

Could you please get the core developers to adopt your code so I can use it?

Thanks

Mike


I did as I threatened to and derived a class as follows:
>
>
> |
> fromdjango.utils importsix
> fromdjango.utils.safestring importmark_safe
> fromdjango.utils.html importconditional_escape
> fromdjango.utils.encoding importforce_text
> fromdjango.forms.models importmodel_to_dict,fields_for_model
>
> classDetailViewWithHTML(DetailView):
> Â Â fields =None
> Â Â field_data =None
> Â Â def__init__(self,instance):
> Â Â Â Â self.fields =fields_for_model(self.model)
> Â Â Â Â self.field_data =model_to_dict(instance)
> Â Â Â Â Â Â Â Â Â Â Â Â
> Â Â # HTML formatters stolen straight form the Django ModelForm class
> Â Â def_html_output(self,normal_row,help_text_html):
> Â Â Â Â "Helper function for outputting HTML. Used by as_table(),
> as_ul(), as_p()."Â Â Â
> Â Â Â Â output =[]
>
> Â Â Â Â forname,field inself.fields.items():
> Â Â Â Â Â Â value =self.field_data[name]
>
> Â Â Â Â Â Â iffield.label:
> Â Â Â Â Â Â Â Â label =conditional_escape(force_text(field.label))
> Â Â Â Â Â Â else:
> Â Â Â Â Â Â Â Â label =''
> Â Â Â Â Â Â Â
> Â Â Â Â Â Â iffield.help_text:
> Â Â Â Â Â Â Â Â help_text =help_text_html
> %force_text(field.help_text)
> Â Â Â Â Â Â else:
> Â Â Â Â Â Â Â Â help_text =''
> Â Â Â Â Â Â Â Â
> Â Â Â Â Â Â output.append(normal_row %{
> Â Â Â Â Â Â Â Â Â 'label':force_text(label),
> Â Â Â Â Â Â Â Â Â 'value':six.text_type(value),
> Â Â Â Â Â Â Â Â Â 'help_text':help_text,
> Â Â Â Â Â Â Â })
>
> Â Â Â Â returnmark_safe('\n'.join(output))
>
> Â Â defas_table(self):
> Â Â Â Â "Returns this form rendered as HTML <tr>s -- excluding the
> <table></table>."
> Â Â Â Â returnself._html_output(
> Â Â Â Â Â Â
> normal_row='<tr><th>%(label)s</th><td>%(value)s%(help_text)s</td></tr>',
> Â Â Â Â Â Â help_text_html='<br /><span class="helptext">%s</span>')
>
> Â Â defas_ul(self):
> Â Â Â Â "Returns this form rendered as HTML <li>s -- excluding the
> <ul></ul>."
> Â Â Â Â returnself._html_output(
> Â Â Â Â Â Â normal_row='<li>%(errors)s%(label)s
> %(value)s%(help_text)s</li>',
> Â Â Â Â Â Â help_text_html=' <span class="helptext">%s</span>')
>
> Â Â defas_p(self):
> Â Â Â Â "Returns this form rendered as HTML <p>s."
> Â Â Â Â returnself._html_output(
> Â Â Â Â Â Â normal_row='<p>%(label)s %(value)s%(help_text)s</p>',
> Â Â Â Â Â Â help_text_html=' <span class="helptext">%s</span>')
> |
>
> It's a quick hack (if only, sheesh it took some time to work out how
> ModelForm does it and reduce it to the simplest of Data Viewers here)
> and hasn't been exhaustively tested or considered, and I remain blown
> away that DetailView doesn't implement these ... fail Django, fail.
>
> OK before anyone gets all smart and asks "why would you need that
> anyway", I admit it's not a super powerful generic per se for production
> implementations, but sheesh it's nice to produce a quick instance viewer
> using the same paradigm as the edit form and not have to do something
> different.
> Â Â Â Â
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/914c4e04-3627-4f53-9243-14a20d50c197%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/914c4e04-3627-4f53-9243-14a20d50c197%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Bernd Wechner

unread,
Aug 12, 2015, 5:20:38 PM8/12/15
to Django users
Mike,

Thanks. This was just a quick hack ripped off from ModelForm (although not as quick as I'd have liked as reverse engineering never is and had to read through ModelForm and get my head around bits) In any case wouldn't be ready for adding to Django as is, but others could improve it to meet any quality standards for integration if they wanted. Alas I'm rather new to Django and have no idea how to propose such an inclusion and to whom. For me this is working as a quick fix, and let some look at records and from there, design better views ;-). WHat I love about teh generics is they give us this quick easy view from which we can start tweaking and thinking how to make it what we want, by styling and/or templating.

Mike Dewhirst

unread,
Aug 12, 2015, 11:25:32 PM8/12/15
to django...@googlegroups.com
On 13/08/2015 7:20 AM, Bernd Wechner wrote:
> Mike,
>
> Thanks. This was just a quick hack ripped off from ModelForm (although
> not as quick as I'd have liked as reverse engineering never is and had
> to read through ModelForm and get my head around bits) In any case
> wouldn't be ready for adding to Django as is, but others could improve
> it to meet any quality standards for integration if they wanted. Alas
> I'm rather new to Django and have no idea how to propose such an
> inclusion and to whom.

https://docs.djangoproject.com/en/1.8/#the-django-open-source-project

This link is at the bottom of the most useful page in the Django docs.
The first link after the Community heading will answer all your questions.

Good luck and welcome again!

Cheers

Mike


For me this is working as a quick fix, and let
> some look at records and from there, design better views ;-). WHat I
> love about teh generics is they give us this quick easy view from which
> we can start tweaking and thinking how to make it what we want, by
> styling and/or templating.
>
> On Wednesday, 12 August 2015 22:36:09 UTC+10, Mike Dewhirst wrote:
>
> On 12/08/2015 8:54 PM, Bernd Wechner wrote:
> > OK, I give up, seriously dismayed that Django doesn't have this
> built in
> > already
>
> Well done Bernd! Welcome to open source. Unfortunately, I can't use
> your
> code unless it is part of Django. I have tried implementing my own
> improvements in the past but I always encounter difficulties when the
> next release of Django comes.
>
> Could you please get the core developers to adopt your code so I can
> use it?
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/b79f1695-0c67-406c-b088-5cdeb100233a%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/b79f1695-0c67-406c-b088-5cdeb100233a%40googlegroups.com?utm_medium=email&utm_source=footer>.
Reply all
Reply to author
Forward
0 new messages