2012-04-24 21 views
12

Tengo un ModelForm que contiene un ModelChoiceField usando el widget RadioSelect.Cómo obtener instancias de ModelChoiceField en la plantilla

class MyAForm(forms.ModelForm): 
    one_property = models.ModelChoiceField(
     widget=forms.RadioSelect, 
     queryset=MyBModel.objects.filter(visible=True), 
     empty_label=None) 
    class Meta: 
     model = MyAModel 

Hay atributos en MyBModel que quiero mostrar junto al botón de opción. Anularía label_from_instance en una subclase de ModelChoiceField pero esto no me permite hacer lo que quiero ya que quiero que el botón de radio aparezca dentro de una tabla que tiene una fila para cada elemento de selección.

Así que en algún lugar de mi plantilla Quiero algo como ...

{% for field in form.visible_fields %} 
    {% if field.name == "one_property" %} 
    <table> 
     {% for choice in field.choices %} 
      <tr> 
       <td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td> 
       <td><img src="{{choice.img_url}}" /></td> 
      </tr> 
     {% endfor %} 
    </table> 
    {% endif %} 
{% endfor %} 

field.choices Desafortunadamente devuelve una tupla de la identificación del objeto y de la etiqueta y no una instancia del conjunto de consultas.

¿Hay una manera simple de obtener instancias de las opciones de un ModelChoiceField para usar dentro de una plantilla?

Respuesta

10

Después de profundizar en la fuente django para ModelChoiceField descubrí que tiene una propiedad "queryset".

pude usar algo como ...

{% for field in form.visible_fields %} 
    {% if field.name == "one_property" %} 
    <table> 
     {% for choice in field.queryset %} 
      <tr> 
       <td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td> 
       <td><img src="{{choice.img_url}}" /></td> 
      </tr> 
     {% endfor %} 
    </table> 
    {% endif %} 
{% endfor %} 
+0

Hola, si imprimo este ** {{field.name}} **, imprime todo el nombre de los campos, pero si lo usé en la condición '{% if field.name =" attributes "%}' o '{% if field.name = attributes%}' e intenta imprimir algo con el éxito de esta condición y luego no imprime nada significa que la condición devuelve false. Pero el nombre de campo que utilicé en esta condición es uno que se imprime ... ** ¿POR QUÉ? ** – Inforian

+1

Tiene una sola = debería ser == –

2

Por lo general, no es necesario el objeto real, pero su interpretación.

Considere este código:

class LabelledHiddenWidget(forms.HiddenInput): 

    def __init__(self, get_object, *args, **kwargs): 
     super(LabelledHiddenWidget, self).__init__(*args, **kwargs) 
     self.get_object = get_object 

    def render(self, name, value, attrs=None): 
     s = super(LabelledHiddenWidget, self).render(name, value, attrs) 
     if value: 
      s += SafeUnicode("<span>%s</span>" % self.get_object(value)) 
     return s 

A continuación, se puede utilizar de esta manera:

class SomeForm(forms.Form): 
    object = forms.ModelChoiceField(
     SomeModel.objects.all(), 
     widget=LabelledHiddenWidget(get_object=lambda id: get_object_or_404(SomeModel, id=id))) 

Luego, en el código de la plantilla, {{ form.object }} dará salida a un campo oculto con el identificador de objeto, concatenado con algunos etiqueta. Por supuesto, su SomeModel debe implementar __unicode__ o algún otro método que devuelva una buena etiqueta legible para humanos.

4

Quería hacer algo casi idéntico a la pregunta de OP (mesa y todo), estaba igualmente frustrado por la falta de cooperación de Django, y de forma similar terminé profundizando en la fuente para llegar a mi propia implementación. Lo que se me ocurrió es un poco diferente a la respuesta aceptada, y me gustó más porque estaba usando un simple {{ form.as_table }} en mi plantilla y no quería tener que pasar por visible_fields innecesariamente o codificar un botón de radio en mi plantilla que simplemente se parece a la implementación actual de Django (que podría cambiar). Esto es lo que hice en su lugar:

RadioInput y RadioFieldRenderer

Reproductor de Django RadioSelect utiliza RadioFieldRenderer para producir un generator de RadioInputs, que haga el trabajo real de la prestación de los botones de radio. RadioSelect parece tener una característica no documentada en la que puede pasarle un representador diferente al predeterminado, por lo que puede subclasificar ambos para obtener lo que desea OP.

from django import forms 
from django.utils.safestring import mark_safe 

class CustomTableRadioInput(forms.widgets.RadioInput): 

    # We can override the render method to display our table rows 
    def render(self, *args, **kwargs): 
     # default_html will hold the normally rendered radio button 
     # which we can then use somewhere in our table 
     default_html = super(CustomTableRadioInput, self).render(*args, **kwargs) 
     # Do whatever you want to the input, then return it, remembering to use 
     # either django.utils.safestring.mark_safe or django.utils.html.format_html 
     # ... 
     return mark_safe(new_html) 

class CustomTableFieldRenderer(forms.widget.RadioFieldRenderer): 
    # Here we just need to override the two methods that yield RadioInputs 
    # and make them yield our custom subclass instead 
    def __iter__(self): 
     for i, choice in enumerate(self.choices): 
      yield CustomTableRadioInput(self.name, self.value, 
              self.attrs.copy(), choice, i) 

    def __getitem__(self, idx): 
     choice = self.choices[idx] # Let the IndexError propogate 
     return CustomTableRadioInput(self.name, self.value, 
             self.attrs.copy(), choice, idx) 

Una vez hecho esto, sólo tenemos que contar el widget RadioSelect utilizar nuestro intérprete personalizado cada vez que lo llamamos en algún lugar de nuestro código de formulario:

... 
radio = forms.ChoiceField(widget=forms.RadioSelect(renderer=CustomTableFieldRenderer), 
          choices=...) 
... 

Y eso es todo!

hacer la nota que para usar esto en la plantilla, es probable que desee para recorrer el campo en lugar de llamar directamente, es decir esto:

<table> 
    <tbody> 
    {% for tr in form.radio %} 
    <tr>{{ tr }}</tr> 
    {% endfor %} 
    </tbody> 
</table> 

en lugar de esto:

<table> 
    <tbody>{{ form.radio }}</tbody> 
</table> 

Si hace esto último, intentará envolver sus elementos de tabla en <ul><li>...</li></ul>.

Cuestiones relacionadas