Opened 18 months ago

Closed 12 months ago

Last modified 12 months ago

#34077 closed New feature (fixed)

Make BoundField renderable.

Reported by: David Smith Owned by: David Smith
Component: Forms Version: 4.1
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

As of Django 4.1 Forms and Widgets (i.e. its <input>) are renderable using the template engine. Django also has a strong concept of a field with the BoundField class. This contains all of the information required to render a field such as its label, errors, help text and its Widget.

Currently something like this is possible:

{% for field in form %}
  {{ field.label_tag }}
  {{ field }} # Note this is the widget!
  {{ field.help_text }}
{% endfor %}

The current limitation is that each component of each field needs to be rendered individually. It's fine when they are rendered all in the same way as we can put them in a nice loop like above.

However, a reasonable request could be to have the first name and last name on the same row (say). This would result in needing to do something like the following. This would become even more verbose with errors being introduced too:

<div class="row">
   <div class="col"> 
     {{ form.first_name.label }}
     {{ form.first_name }}
     {{ form.first_name.help_text }}
  </div>
  <div class="col">
     {{ form.last_name.label }}
     {{ form.last_name }}
     {{ form.last_name.help_text }}
   </div>
</div>
<div>
   # more fields here
</div>

The proposal is that the BoundField should be a renderable object, this could either be done by a new method (e.g. field.as_field) or maybe a template filter (e.g field|as_field). We need the as_field (or another function name) as the field on it's own currently renders the fields widget rather than the whole field (i.e. incl label, help text , errors).

This would allow the above to be written as:

<div class="row">
   <div class="col"> 
     {{ form.first_name|as_field }}
  </div>
  <div class="col">
     {{ form.last_name|as_field }}
   </div>
</div>
<div>
   # more fields here
</div>

A few initial thoughts on a possible implementation approach. This is not fully thought out, but should hopefully be a helpful rough guide.

  1. Field would learn a template_name attribute which can also be set via it's __init__. Therefore in a form you would have:
class MyForm(forms.Form)
    first_name = forms.CharField(max=10, template_name="my_template_name.html")
  1. Add a new method, either as a filter or as a new method to BoundField which renders the BoundField with it's fields template (boundfield.field.template_name)
  1. Consider if we need to provide a default implementation of this. If so we should replicate the div.html templates. Specifically these lines. The div.html template can be updated to use this new method.
  1. If we do step 3 then the errors will likely be an issue as they currently get rendered to a str in the form's get_context. This may need breaking out into a separate function so it can be called when rendering a field. BoundField know's about it's associated form (BoundField.Form)

Change History (9)

comment:1 by Carlton Gibson, 18 months ago

Component: UncategorizedForms
Triage Stage: UnreviewedAccepted
Type: UncategorizedNew feature

Yes, thanks David. I think this is the logical next/final? step in the form rendering adjustments. +1

comment:2 by Rahmat Faisal, 17 months ago

Owner: changed from nobody to Rahmat Faisal
Status: newassigned

comment:3 by David Smith, 17 months ago

I think this is the logical next/final? step in the form

I think so.

@skidipap -- How are you getting on with this. I had made a tentative start, but then realised you have claimed the ticket and therefore wanted to avoid duplicate work. Maybe if you have some work to share I could help with comments on a PR?

comment:4 by David Smith, 17 months ago

Owner: changed from Rahmat Faisal to David Smith

comment:5 by Carlton Gibson, 13 months ago

Has patch: set
Patch needs improvement: set

comment:6 by Carlton Gibson <carlton.gibson@…>, 12 months ago

In 051d594:

Refs #33134, Refs #34077 -- Adjusted form rendering recursion test.

Adjusted recursion depth test to use str() rather than the form or
field’s render() method.

comment:7 by Mariusz Felisiak, 12 months ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:8 by Mariusz Felisiak <felisiak.mariusz@…>, 12 months ago

Resolution: fixed
Status: assignedclosed

In cad376f8:

Fixed #34077 -- Added form field rendering.

in reply to:  8 comment:9 by Rami Boutassghount, 12 months ago

Many thanks to all who made this possible. Now I can add my CSS classes to my field label tags or at least try 😅

Note: See TracTickets for help on using tickets.
Back to Top