views:

192

answers:

5

Supose this portion of a Django template. regs is a list of Reg objects. Reg.editable is a BooleanField. I want to render a radio button per element in the list. If r.editable is False, the radio button must be disabled:

{% for r in regs %}
<input type="radio" value="{{ forloop.counter }}"
{% if forloop.first %}checked="checked"{% endif %}
{% if not r.editable %}disabled="disabled"{% endif %}/> 
{% endfor %}

As you can see, I'm using forloop.first to check the first radio button, but this has a problem! What about if the first element has editable == False? The first radio button will be rendered disabled and chequed. If then a user submit "the form" I'll receive a value not expected.

Am I clear with the problem here? How can I rewrite this template to render as checked the FIRST ENABLED radio button?

Thanks

+1  A: 

Just adjust your ifs

{% for r in regs %}
    {% if forloop.first %}
        <input type="radio" value="{{ forloop.counter }}" checked="checked"/>
    {% else %}
        {% if not r.editable %}
            <input type="radio" value="{{ forloop.counter }}" disabled="disabled"/> 
        {% endif %}
    {% endif %}
{% endfor %}

PS: Your question did not clearly explain what you wanted. I made some reasonable assumption. Update the question if what you want is something else.

Lakshman Prasad
This solves the problem of the first radio not being (checked and disabled), but what if the first radio should be (disabled and not checked)?
T. Stone
+2  A: 

Djangos templating language doesn't give you a lot in the way of logic in the template (I've heard positive things about Jinja2 if you want to change that). There's also the "Smart" {% if %} tag which adds some more functionality and incidentally is being proposed for inclusion in Django 1.2.

As for solving this problem I would most likely move the logic over to the view. (Disclaimer: don't have the time to test this code snippet but it should provide the general idea)

def my_view(request, *args, **kwargs):

    # QuerySet is fetched however it's done...
    regs = Regs.objects.all()

    # Wrap regs in display information    
    checked = False
    radio_buttons = []

    for r in regs:
        if r.editable:
            if not checked:
                 radio_buttons.append( { 'checked':True, 'enabled':True, 'object':r } )
                 # Set to true once
                 checked = True
            else:
                 radio_buttons.append( { 'checked':False, 'enabled':True, 'object':r } )
        else:
            radio_buttons.append( { 'checked':False, 'enabled':False, 'object':r } )          

    # Then pass in radio_buttons for the value of regs down here
    render_to_whatever(..., { 'regs':radio_buttons } )

In this case we've wrapped the QuerySet which will give our template some more details about rendering. The template becomes "dumb" now.

{% for r in regs %}
    <input type="radio" value="{{ forloop.counter }}"
    {% if r.checked %}checked="checked"{% endif %}
    {% if not r.enabled %}disabled="disabled"{% endif %}/> 
    {% comment %} To access the original object use: {{ r.object }} {% endcomment %}
{% endfor %}
T. Stone
+1  A: 

The real answer to this question is as follows:

Such logic has no place in the template. You can preprocess the context before passing it to the template, thus eleminating the need to do this using the (intentionally) crippled template engine logic.

In my opinion, what you are doing is wrong. I mean, django has perfectly fine forms api, why render the inputs directly then? Some might argue, that django's forms api is inflexible, but for this specific need it will undoubtely be sufficient.

And to reiterate - this kind of logic does not belong in the presentation layer. So don't put it there, it will bite you. In fact it already did.

shylent
A: 

Similar to T. Stone's answer of doing this logic in the view, you could just add a new template variable that indicated the first checked radio:

def my_view(request, *args, **kwargs):
  regs = Regs.objects.all()
  checked_index = None
  for i, reg in enumerate(regs):
    if reg.enabled:
      checked_index = i
      break
  # pass checked_index into the template...

Template:

{% for r in regs %}
  {% ifequal forloop.counter0 checked_index %}
    <input type="radio" value="{{ forloop.counter }}" checked="checked"/>
  {% else %}
    <input type="radio" value="{{ forloop.counter }}" {% if not r.editable %}disabled="disabled"{% endif %}/> 
  {% endif %}
{% endfor %}
Dan Breen
A: 

Similar to becomingGuru but solving your problems:

{% for r in regs %}
    {% if not r.editable %}
        <input type="radio" value="{{ forloop.counter }}" disabled="disabled"/>     
    {% else %}
        {% if forloop.first %}
            <input type="radio" value="{{ forloop.counter }}" checked="checked"/>
        {% endif %}
    {% endif %}
{% endfor %}

It first checks if r is editable, and then checks if it is the first. Regards.

pedromagnus