4
votes

I want to add extra infos for some fields of a django form, and use it in templates to add some extra html tags

I've a django form like:

class MyForm(forms.Form):
    integer = forms.IntegerField(
        help_text='Please enter your favorite integer number',
        label='Favorite integer',
        widget=forms.TextInput(attrs={'class':'input-xlarge'}))
    decimal = forms.DecimalField(
        min_value=Decimal('0.0001'),
        decimal_places=4,
        help_text='Please enter your favorite decimal number',
        label='Favorite decimal',
        widget=forms.TextInput(attrs={'class':'input-xlarge'}))
    ==> here I would like to say: decimal will have a new property

I am rendering MyForm in a template, using a loop over each field:

{% for item in form.visible_fields %}
  ==> here I would like to test if item has that property to add some html tags
  {{ item }}
{% endfor %}

I cannot use the widget's attrs, as I want to use the property info outside the rendered input tag.

Should I create a custom field, write custom renderers for all fields I use, or is there a simpler solution I did miss ?


Edit: So far my solution is : (simplified version) Main template:

<form action="/somewhere/" method="post">{% csrf_token %}
 {% include 'form_field.html' with item=form.integer icon="icon-home" %}
 {% include 'form_field.html' with item=form.decimal icon="icon-list-alt" %}
 {% include 'form_field.html' with item=form.another icon="icon-signal" %}
 {% include 'form_field.html' with item=form.again   icon="icon-time" %}
 ...

In form_field.html, I render a field using Twitter's Bootstrap input-prepend div:

<div class="control-group">
  <label class="control-label" for="{{ item.name }}">{{ item.label }}</label>
  <div class="controls">
    {% if icon %}
      <div class="input-prepend">
      <span class="add-on"><i class="{{ icon }}"></i></span>
    {% endif %}
      {{ item }}
      <p class="help-block">{{ item.help_text }}</p>
    {% if icon %}
    </div>
    {% endif %}
  </div>
</div>

All I want is to simplify the main template, using a loop rather enumerating all fields, which is error prone. So I want to move that 'icon-home' property from the template to the form definition, to have all attributes in the same place (specific widget attrs are already in the form). Ideally, in the main template I would have:

{% for item in form.visible_fields %}
  {% include 'form_field.html' %}
{% endfor %}

I understand it can be viewed as a misunderstanding of Django's principles (site look should be in templates, not in forms)...

3
Can you elaborate on and explain in a high-level of what you are trying to accomplish?tamakisquare
I've added some precisions in comments on Paul's response. Does it clarifies ?Labarbiche
Next time when you add additional info to your question, like clarification, make sure you add it to the original post rather than in comments. That way you have all of the info in one place. Not to mention you can present the info better with rich-formatting capability, which is not offered in comments.tamakisquare
"want to have a 'prepend' icon for some of my fields". Are these fields all of the same form field type?tamakisquare
no, I've CharField, ChoiceField, IntegerField and DecimalFieldLabarbiche

3 Answers

5
votes

Custom form field + multiple inheritance + a class that stores the icon class name

First, let's define a class that captures the icon class name. We'll call it IconName:

class IconName(object):
    def get_icon_name(self):
        return self._icon_name
    def set_icon_name(self, value):
        self._icon_name = value
    icon_name = property(get_icon_name, set_icon_name)

You need to come up with custom field type for each form field type you use. Here is how it's done with IntegerField. Your custom field class needs to inherit from the original django form field class (IntegerField, in this case) and the IconName class.

class CustomCharField(CharField, IconName):
    pass

After you have defined all of the custom form field types that your form requires, update your form with them. Next, you need to initialize the icon_name for each form field in the form's __init__():

def __init__(self, *args, **kwargs):
    super(forms.Form, self).__init__(*args, **kwargs)
    self.fields['integer'].icon_name = 'icon-home'
    ...

In your template, you can do:

{% for item in form.visible_fields %}
<div class="control-group">
  <label class="control-label" for="{{ item.name }}">{{ item.label }}</label>
  <div class="controls">
    {% if item.field.icon_name %}
      <div class="input-prepend">
      <span class="add-on"><i class="{{ item.field.icon_name }}"></i></span>
    {% endif %}
      {{ item }}
      <p class="help-block">{{ item.help_text }}</p>
    {% if item.field.icon_name %}
    </div>
    {% endif %}
  </div>
</div>
{% endfor %}

I have not tried this myself but it should work (assuming I understand you correctly)

0
votes

Warning: I'm just starting with Python and Django.

See if this will help you get started:

class MyCustomTextInput(forms.widgets.TextInput):
    def render(self, name, value, attrs=None):
        original_html = super(MyCustomRender,self).render(name, value, attrs)
        icon_html = "<img src='/img.png' />"
        return "%s%s" % (icon_html,original_html)

class MyForm(ModelForm):
    myfield = MyCustomTextInput()

I believe this would produce output similar to:

<img src='/img.png' /><input type="text" ... />

Take a look at https://github.com/django/django/blob/master/django/forms/widgets.py

-2
votes

Django comes by default with a simple renderer.

from django's documentation:

<form action="/contact/" method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Submit" />
</form>

As for the use of custom image, Javascript would be a solution if your form is dynamic. A form model is not, since it is just a representation designed to create fomrs efficently.