Cómo obtener instancias de ModelChoiceField en la plantilla

Tengo un ModelForm que contiene un ModelChoiceField usando el widget RadioSelect.

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. 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 opción 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" %}  {% for choice in field.choices %}  {% endfor %} 
{{choice.description}}
{% endif %} {% endfor %}

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

¿Existe una forma sencilla de obtener instancias de las opciones para un ModelChoiceField para usar dentro de una plantilla?

Después de profundizar en la fuente de django para ModelChoiceField, descubrí que tenía una propiedad “queryset”.

Pude usar algo como …

 {% for field in form.visible_fields %} {% if field.name == "one_property" %}  {% for choice in field.queryset %}  {% endfor %} 
{{choice.description}}
{% endif %} {% endfor %}

Quería hacer algo casi idéntico a la pregunta de OP (tabla y todo), estaba frustrado de manera similar por la falta de cooperación de Django y, de manera similar, terminé ahondando en la fuente para encontrar 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 una {{ form.as_table }} simple en mi plantilla y no quería tener que recorrer los visible_fields necesidad o con visible_fields codifique 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

El widget RadioSelect de Django usa RadioFieldRenderer para generar un generador de RadioInputs , que realiza el trabajo real de renderizar los botones de radio. RadioSelect parece tener una función no documentada en la que puede pasarle un renderizador diferente a este predeterminado, así que puede subclasificar ambos para obtener lo que OP desea.

 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, solo tenemos que decirle al widget RadioSelect que use nuestro renderizador personalizado cada vez que lo llamemos en algún lugar de nuestro código de formulario:

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

¡Y eso es!

Tenga en cuenta que para usar esto en la plantilla, probablemente querrá recorrer el campo en lugar de llamarlo directamente, es decir, esto:

  {% for tr in form.radio %} {{ tr }} {% endfor %} 

En vez de esto:

 {{ form.radio }}

Si hace lo último, intentará envolver los elementos de su tabla en

  • ...

.

Normalmente no necesitas el objeto real, sino 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("%s" % self.get_object(value)) return s 

Entonces puedes usarlo así:

 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 }} generará un campo oculto con la identificación del objeto, concatenado con alguna etiqueta. Por supuesto, su SomeModel debería implementar __unicode__ o algún otro método que devuelva una etiqueta agradable, legible para el ser humano.