Чтобы использовать методы и член объекта, вам сначала нужно создать этот объект. Если вы его не создали (переменная, которая должна содержать объект, не инициализируется), но вы пытаетесь использовать его методы или переменные, вы получите эту ошибку.
Иногда вы можете просто забыть инициализировать .
Отредактировано: new не может вернуть значение null, но исключение огня при ошибке. Давно это было на некоторых языках, но не больше. Спасибо @John Saunders за указание на это.
Ответ Антона работал, и на какое-то время был достойный ответ, но, к сожалению, он стал недостижимым. Итак, взяв сигнал из diff , прикрепленный к билету django # 9230 , я просто обезглавил 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__
Это позволило мне иметь доступ к радиовводам напрямую, используя {{ form.field.0.tag }}
или через итерацию - {% for radio in form.field %} {{ radio.tag }} {% endfor %}
. Намного легче позаботиться!
Выбор должен быть в модели:
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)
Тогда просто:
class RatherComplicatedForm(forms.ModelForm):
#various and sundry code...
bar = forms.ChoiceField(widget=forms.RadioSelect(),
choices=RatherComplicatedModel.BAR_CHOICES)
class Meta:
model = RatherComplicatedModel
Я сделал бы это путем подкласса RadioFieldRenderer и привязки его к пользовательскому виджету:
# 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
Затем шаблон:
#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>
Если я правильно понимаю вашу проблему, вы можете получить доступ к кортежу в шаблоне:
<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>