1
votes

I'm using Django 1.7 with bootstrap 3 and want to render each form field the same way, best utilizing bootstrap has-error fields:

<div class="form-group {% if form.field.errors %}has-error has-feedback{% endif %}">
    <label class="control-label" for="field">{{ form.field.help_text }}</label>

    <div class="input-group">
        <span class="input-group-addon"><span class="some-icon"></span></span>
        {% render_field form.field class+="form-control" placeholder=form.field.label id="end-date" aria-describedby="inputError2Status" %}
    </div>
    <label class="control-label" for="field">{{ form.field.errors.as_text }}</label>
</div>

As you can see, the moment the number of fields increase it becomes a LOT of work, not only to write it, but also to maintain it and refactoring becomes a hell... not really DRY. For each field I need to change the form.field variables and id="field" and the icon

How would I write a function, templatetag or something else to make django render all the fields in my form this way? Is it even possible to do this? It would be awesome if the Form in django would have a setting to accept an optionalglypycon class which the renderer will use to render the icon.

-- EDIT --

As indicated by @Serafeim django-crispy-forms could be the solution. But how do I use crispy-forms to produce the above html?

-- EDIT 2 --

After reading up on crispy-forms I managed to come up with the following solution: My forms.py file looks like this:

class CreateForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(CreateForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.form_action = 'submit'
        self.helper.add_input(Submit('submit', 'Submit'))
        self.helper.html5_required = True
        self.helper.layout = Layout(
            PrependedText('fieldA', '<span class="fa fa-calendar"></span>', placeholder="yyyy-mm-dd"),
            PrependedText('fieldB', '<span class="fa fa-clock-o"></span>', placeholder="0:00:00 (h:m:s)"),
        'fieldC')

    fieldA = forms.DateField(required=True)
    fieldB = FDurationField(required=True)
    fieldC = forms.CharField(widget=forms.Textarea(attrs={'rows':3}), required=False, max_length=128)

    class Meta:
        model = AModel
        exclude = ('fieldD', 'FieldE', 'fieldF', 'fieldG')

and in my template I only have to call {% crispy form %} which is much better than my initial version. However, I still feel like I have to repeat certain steps, like adding the fields to the Layout... Is it possible to reduce the code even further?

1
Have you ever heard of django-crispy-forms django-crispy-forms.readthedocs.org/en/latest ? Try it and it will resolve all your problems :) - Serafeim
I have, and I've tried it but I couldn't get it to work the way I wanted to... Perhaps I just didn't use it the correct way... Unfortunately I can't show you the code I had with crispy-forms, deleted it a while back :( - Tim

1 Answers

1
votes

It would be awesome if the Form in django would have a setting to accept an optional glypycon class which the renderer will use to render the icon.

Forms are just a collection of fields. A Field is a combination of a widget (which control the HTML rendered) and validation rules.

To control what is rendered on the page, you'd modify the widget.

For example, if I want to specify a css class or a placeholder, I would do this at the widget level:

email = forms.EmailField(label='Email Address',
                         required=False,
                         widget=forms.TextInput(attrs={'class': 'form-control',
                                                       'placeholder': '[email protected]'}))

You can create your own custom widgets and fields and obtain fine control over how forms are rendered; so what you are asking is absolutely possible; but as this is something that is often repeated, django-crispy-forms has become the defacto standard way of getting this done.