jrief / django-formset

The missing widgets and form manipulation library for Django
https://django-formset.fly.dev/
MIT License
317 stars 30 forks source link

Render form will always render default template #140

Closed codematsing closed 2 months ago

codematsing commented 3 months ago

Issue

I found that using the templatetag render_form will ignore the form renderer I declared in forms.py when I implement my project as follows:

from formset.renderers.bootstrap import FormRenderer as BootstrapFormRenderer
class Form(ModelForm):
    default_renderer = BootstrapFormRenderer(
        field_css_classes='row mb-3',
        label_css_classes='col-sm-3',
        control_css_classes='col-sm-9',
    )
    class Meta:
         model = ...
         fields = "__all__"

and template:

<django-formset endpoint="{{ request.path }}" csrf-token="{{ csrf_token }}">
  {% render_form form %}
  <div class="offset-sm-3">
    <button type="button" click="submit -> proceed" class="btn btn-primary">Submit</button>
    <button type="button" click="reset" class="ms-2 btn btn-warning">Reset to initial</button>
  </div>
</django-formset>

Clarification

I'm not sure if this is correct since I'm still checking this out:

https://github.com/jrief/django-formset/blob/e12c947a519ce84510b15df07030ac2c77df2a39/formset/templatetags/formsetify.py#L49C1-L52C66

def render_form(context, form, *args, **kwargs):
    get_token(context['request'])  # ensures that the CSRF-Cookie is set
    form = _formsetify(form, *args, **kwargs)
    # shouldnt this:
    return form.render(template_name='formset/default/form.html')
    # be this:
     return form.render()

It's causing rendering issues on my project since my ModelForm.default_renderer is bootstrap-based renderer, while {% render_form form %} forces to use formset/default/form.html instead of formset/bootstrap/form.html

jrief commented 3 months ago

I would have to step through with the debugger, but usually you would use the Bootstrap Renderer class to render your forms. And here https://github.com/jrief/django-formset/blob/e12c947a519ce84510b15df07030ac2c77df2a39/formset/renderers/bootstrap.py#L17C39-L17C66 the default template is mapped to one specific to Bootstrap.

codematsing commented 3 months ago

I agree to the reference. It is what I use, but when I check the rendered html, each field group follows formset/default/field_group.html instead of formset/bootstrap/field.group.html

... which in turn, fails to create the inline formatting of labels and inputs as referenced here

This is due to the implementation of formset/default/field_group.html having no separate div classes for label and input, which formset/bootstrap/field_group.html wraps widget field inside a div element

So I'm not sure if the templatetags.formsetify.render_form should be adjusted, or, default/field_group.html needs a div class similar to bootstrap/field_group.html

jrief commented 3 months ago

Can you please create a small example. Take the examples in testapp as blueprint.

codematsing commented 3 months ago

Investigatory diff:

image

<!-- snippet -->
<div role="group" class="dj-required row mb-3">
   <label for="id_name" class="col-sm-3">Team name:</label><input type="text" name="name" maxlength="50" required="" form="id_customrendererteamform" id="id_name" class="form-control">
   <div role="alert" class="dj-field-errors">
      <meta name="error-messages" value_missing="This field is required." too_long="Ensure this value has at most 50 characters." bad_input="Null characters are not allowed.">
      <ul class="dj-errorlist">
         <li class="dj-placeholder"></li>
      </ul>
   </div>
   <div class="dj-help-text">The name of the team</div>
</div>

Overridden diff:

image

<!-- snippet -->
<div role="group" class="row mb-3 dj-required">
   <label for="id_name" class="col-sm-3">
   Team name:</label>
   <div class="col-sm-9">
      <input type="text" name="name" maxlength="50" required="" form="id_customrendererteamform" id="id_name" class="form-control">
      <div role="alert" class="dj-field-errors">
         <meta name="error-messages" value_missing="This field is required." too_long="Ensure this value has at most 50 characters." bad_input="Null characters are not allowed.">
         <ul class="dj-errorlist">
            <li class="dj-placeholder"></li>
         </ul>
      </div>
      <div class="form-text text-muted">The name of the team</div>
   </div>
</div>

Some Notes:

Hopefully implementation is readable enough!

jrief commented 2 months ago

On branch https://github.com/jrief/django-formset/tree/releases/1.5 I have solved this. I used your sample code and applied them to an existing model in testapp.

Please try the examples at http://localhost:8000/bootstrap/person-bootstrap-params and http://localhost:8000/bootstrap/person-bootstrap-renderer

The first uses {% render_form form "boostrap" field_classes="row mb-3" label_classes="col-sm-3" control_classes="col-sm-9" %}

while the second uses a form with

class PersonFormBootstrapRenderer(FormMixin, PersonForm):
    default_renderer = BootstrapFormRenderer(
        field_css_classes='row mb-3',
        label_css_classes='col-sm-3',
        control_css_classes='col-sm-9',
    )

I haven't tested this with Bootstrap loaded from a CDN though.

Thanks for reporting.