Say I have the following models...
class Person(models.Model):
name = models.CharField()
specialty = models.CharField()
class Team(models.Model):
captain = models.ForeignKey(Person)
vice_captain = models.ForeignKey(Person)
and I have a form for creating a team...
class TeamForm(ModelForm):
class Meta:
model = Team
widgets['vice_captain'] = MySelectWidget()
I also have an additional constraint for the form, whereby the Vice Captain must have the same Specialty as the Captain. I have implemented checks in the form clean etc, but want the UI to 'filter' itself. I have decided that rather than use ajax to populate/filter the field, I will add html 'data-' tags to the widget output, then hide options with javascript.
I have written a widget (and the javascript) that works with the an Select widget. Here it is (note this is simplified from my actual code, but should work).
class Select_with_Data(forms.Select):
# Not sure if this is necessary.
allow_multiple_selected = False
def render_option(self, selected_choices, option_value, option_label):
# This paragraph is copied from django Select.
option_value = force_text(option_value)
if option_value in selected_choices:
selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected:
# Only allow for a single selection.
selected_choices.remove(option_value)
else:
selected_html = ''
# My custom code to add data-specialty attributes to <option> tags.
# Get the object to filter upon.
obj = self.choices.queryset.get(pk=option_value)
# Get the data field.
data_field = getattr(obj, 'specialty', False)
# If the data field has a value set, add it to the return html.
# Need to check if the data_field has a pk (ie is a ForeignKey field), and handle it appropriately.
if data_field:
selected_html += ' data-{0}={1}'.format( 'specialty', str(getattr(data_field, 'pk', data_field)) )
# This paragraph is copied from django Select.
return format_html('<option value="{0}" {1}>{2}</option>',
option_value,
selected_html,
force_text(option_label))
But now I have decided I want radio buttons, not a select list. My problem is, attempting to use code similar to the above within the renderer of the radio widget fails, as self.choices.queryset isn't set, so I cannot access the information I need. How can I get the information I need, and is this the best way to do what I want?
I have even modified the core django files to see where the queryset was dissapearing. RadioSelect subclasses RendererMixin. self.choices.queryset is available during its init, render and get_renderer ?sub-functions? (is functions the word?). The renderer for RadioSelect is RadioFieldRenderer, which subclasses ChoiceFieldRenderer. Inside its init and render though, the queryset is gone (it sets its own self.choices, but even before that step in the init, self.choices isnt set).