python - tutorial - django forma de diseño de entrada de radio
django urls (4)
¿Cuál es la forma "djangoy" de abordar este problema?
En mi clase de formulario, tengo un forms.ChoiceField cuyo widget es un widget forms.RadioSelect, una de cuyas elecciones debe presentarse con una entrada de texto en línea (que también es un campo en el formulario). Estoy usando la validación personalizada para ignorar el campo de texto cuando su opción de radio no está seleccionada. Cuando se procesa, quiero que aparezca como a continuación:
<ul>
<li><label for="id_rad_0"><input type="radio" id="id_rad_0" value="none" name="rad" /> No Textbox</label></li>
<li><label for="id_rad_1"><input type="radio" id="id_rad_1" value="one" name="rad" /> One Textbox: <input type="text" name="bar" id="id_bar" /></label></li>
</ul>
Sin embargo, no puedo simplemente producir esto en mi plantilla, porque las opciones de radio no están expuestas. No veo la manera de hacerlo sin combinar mi formulario a mi plantilla o, alternativamente, poner toda la lógica de presentación en la clase de formulario. ¿Cuál es la forma correcta de resolver este problema?
editar
Me doy cuenta de que lo anterior podría ser solo un problema oscuro, pero no estoy seguro de qué otra información puedo proporcionar para inspirar a alguien que me ayude con esto. Soy un programador de back-end mucho mejor que un diseñador web, y estoy solo en este proyecto, entonces tal vez sea una falta de educación. ¿Es lo que describí simplemente como un diseño pobre? ¿Debo simplemente diseñar esto de otra manera? Estoy realmente abierto a cualquier sugerencia que me ayude a superar esto.
editar 2
Por solicitud, el código actual, abreviado para guardar cordura, cambió los nombres para proteger a los inocentes:
# forms.py
from myapp.models import RatherComplicatedModel
from django import forms
class RatherComplicatedForm(forms.ModelForm):
#various and sundry code...
RADIO_CHOICES = (
(''none'', "No Textbox"),
(''one'', "One Textbox: "),
)
# although I''ve abbreviated the model, ''rad'' does not appear in the model;
# it merely provides input to the un-provided clean function
rad = forms.ChoiceField(widget=forms.RadioSelect(),choices=RADIO_CHOICES)
class Meta:
model = RatherComplicatedModel
-
# models.py
from django.db import models
class RatherComplicatedModel(models.Model):
#some other stuff...
bar = models.IntegerField(blank=True,null=True)
Haría esto subclasificando RadioFieldRenderer y adjuntándolo a un widget personalizado:
# forms.py
from django import forms
from django.forms.widgets import RadioSelect, RadioFieldRenderer
from django.template.loader import render_to_string
from myapp.models import RatherComplicatedModel
class MyRadioFieldRenderer(RadioFieldRenderer):
def render(self):
return render_to_string(
''my_radio_widget.html'',
{''field'': self})
class MyRadioSelect(RadioSelect):
renderer = MyRadioFieldRenderer
class RatherComplicatedForm(forms.ModelForm):
RADIO_CHOICES = (
(''none'', "No Textbox"),
(''one'', "One Textbox: "),
)
rad = forms.ChoiceField(widget=MyRadioSelect(),choices=RADIO_CHOICES)
class Meta:
model = RatherComplicatedModel
Entonces la plantilla:
#my_radio_widget.html
<ul>
{% for choice in field %}
<li>
<label for="id_{{ field.name }}_{{ forloop.counter0 }}">
<input type="radio"
name="{{ field.name }}"
value="{{ choice.choice_value }}"
id="id_{{ field.name }}_{{ forloop.counter0 }}"
{% if field.value == choice.choice_value %}
checked=''checked''
{% endif %}/>
{{ choice.choice_label }}
</label>
</li>
{% endfor %}
</ul>
La respuesta de Anton funcionó, y fue una respuesta decente por un tiempo allí, pero desafortunadamente se volvió imposible de mantener. Así que, siguiendo el ejemplo de un diff adjunto al ticket de django # 9230 , acabo de parchear django.forms.forms.BoundField
from django import forms
def MonkeyPatchDjangoFormsBoundField():
def prepare_widget_render(self, widget=None, attrs=None, only_initial=False):
"""
Prepare the data needed for the widget rendering.
"""
if not widget:
widget = self.field.widget
attrs = attrs or {}
auto_id = self.auto_id
if auto_id and ''id'' not in attrs and ''id'' not in widget.attrs:
if not only_initial:
attrs[''id''] = auto_id
else:
attrs[''id''] = self.html_initial_id
if not only_initial:
name = self.html_name
else:
name = self.html_initial_name
return widget, name, attrs
def as_widget(self, widget=None, attrs=None, only_initial=False):
"""
Renders the field by rendering the passed widget, adding any HTML
attributes passed as attrs. If no widget is specified, then the
field''s default widget will be used.
"""
widget, name, attrs = self.prepare_widget_render(widget, attrs, only_initial)
return widget.render(name, self.value(), attrs=attrs)
def __iter__(self):
"""
Check if current widget has a renderer and iterate renderer.
"""
widget, name, attrs = self.prepare_widget_render()
if not hasattr(widget, ''get_renderer''):
raise Exception, "Can not iterate over widget ''%s''" % widget.__class__.__name__
renderer = widget.get_renderer(name, self.value(), attrs=attrs)
for entry in renderer:
yield entry
def __getitem__(self,idx):
"""
Tries to use current widget''s renderer, and then check attribute.
"""
widget, name, attrs = self.prepare_widget_render()
try:
renderer = widget.get_renderer(name, self.value(), attrs=attrs)
return renderer[idx]
except Exception:
return getattr(self,idx)
forms.forms.BoundField.prepare_widget_render = prepare_widget_render
forms.forms.BoundField.as_widget = as_widget
forms.forms.BoundField.__iter__ = __iter__
forms.forms.BoundField.__getitem__ = __getitem__
Esto me permitió poder acceder a las entradas de radio directamente, usando {{ form.field.0.tag }}
, o mediante iteración - {% for radio in form.field %} {{ radio.tag }} {% endfor %}
. ¡Mucho más fácil de cuidar!
Las elecciones deben estar en el Modelo:
class RatherComplicatedModel(models.Model):
BAR_CHOICES = (
(0, "No Textbox"),
(1, "One Textbox: "),
)
#some other stuff...
bar = models.IntegerField(blank=True, null=True, choices=BAR_CHOICES)
Entonces solo:
class RatherComplicatedForm(forms.ModelForm):
#various and sundry code...
bar = forms.ChoiceField(widget=forms.RadioSelect(),
choices=RatherComplicatedModel.BAR_CHOICES)
class Meta:
model = RatherComplicatedModel
Si entiendo su problema correctamente, puede acceder a las opciones tuple en la plantilla:
<ul>
{# Assuming {{ field }} here is {{ form.rad }} #}
{% for choice in field.field.choices %}
<li>
<label for="id_{{ field.html_name }}_{{ forloop.counter0 }}">
<input type="radio"
id="id_{{ field.html_name }}_{{ forloop.counter0 }}"
value="{{ choice.0 }}"
name="{{ field.html_name }}" />
{{ choice.1 }}
{% if choice.0 == ''one'' %}
{# Necessary field here #}
{{ form.bar }}
{% else %}
No Textbox
{% endif %}
</label>
</li>
{% endfor %}
</ul>