Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ Form must inherit from ChainedChoicesForm which loads the options when there is
field_two = ChainedChoiceField(parent_field='field_one', ajax_url='/chainedselectchoices')
field_three = ChainedChoiceField(parent_field='field_two', ajax_url='/chainedselectchoices')

If the `parent_field` is a foreign key, then inherit from FKChainedChoicesForm
instead. This obtains the `id` of the field, and ensures that the widget works
correctly with both new and existing instances.

Ajax call is made whenever the parent field is changed. You must set up the ajax to return json list of lists.

class ChainedSelectChoices(BaseDetailView):
"""
View to handel the ajax request for the field options.
View to handle the ajax request for the field options.
"""

def get(self, request, *args, **kwargs):
Expand All @@ -45,4 +48,3 @@ Ajax call is made whenever the parent field is changed. You must set up the ajax
)
add_never_cache_headers(response)
return response

2 changes: 0 additions & 2 deletions chained_selectbox/form_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ def __init__(self, parent_field, ajax_url, choices=None, *args, **kwargs):

super(ChainedChoiceField, self).__init__(choices=self.choices, *args, **defaults)


def valid_value(self, value):
"Dynamic choices so just return True for now"
return True

34 changes: 29 additions & 5 deletions chained_selectbox/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,43 @@

class ChainedChoicesForm(forms.ModelForm):
"""
Form class to be used with ChainedChoiceField and ChainedSelect widget
If there is already an instance (i.e. editing) then the options will be loaded when the form is built.
Form class to be used with ChainedChoiceField and ChainedSelect widget If
there is already an instance (i.e. editing) then the options will be loaded
when the form is built.
"""
def __init__(self, *args, **kwargs):
super(ChainedChoicesForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
if "instance" in kwargs:
instance = kwargs['instance']
c = Client()

for field_name, field in self.fields.items():
if hasattr(field, 'parent_field'):
article = c.get(field.ajax_url, {
'field_name' : field_name,
'parent_value' : getattr(instance, field.parent_field)
'field_name': field_name,
'parent_value': getattr(instance, field.parent_field)
})
field.choices = json.loads(article.content)


class FKChainedChoicesForm(forms.ModelForm):
"""
Similar to ChainedChoicesForm - however, this uses parent_field_id rather than
parent_field.
"""
def __init__(self, *args, **kwargs):
super(FKChainedChoicesForm, self).__init__(*args, **kwargs)
if "instance" in kwargs:
instance = kwargs['instance']
c = Client()

for field_name, field in self.fields.items():
if hasattr(field, 'parent_field'):
article = c.get(field.ajax_url, {
'field_name': field_name,
'parent_value': getattr(
instance,
"%s_id" % field.parent_field
)
})
field.choices = json.loads(article.content)
25 changes: 17 additions & 8 deletions chained_selectbox/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
from django.contrib.admin.templatetags.admin_static import static
from django.utils.safestring import mark_safe


class ChainedSelect(Select):
"""
A ChoiceField widget where the options for the select are dependant on the value of the parent select field.
When the parent field is changed an ajax call is made to determine the options.
A ChoiceField widget where the options for the select are dependant on the
value of the parent select field. When the parent field is changed an ajax
call is made to determine the options.

Form must inherit from ChainedChoicesForm which loads the options when there is already an instance.
Form must inherit from ChainedChoicesForm which loads the options when
there is already an instance.

class StandardModelForm(ChainedChoicesForm):
field_one = forms.ChoiceField(choices=(('', '------------'), (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), ))
Expand All @@ -21,16 +24,22 @@ def __init__(self, parent_field=None, ajax_url=None, *args, **kwargs):
super(ChainedSelect, self).__init__(*args, **kwargs)

class Media:
js = ['admin/js/jquery.min.js', 'admin/js/jquery.init.js', 'js/chained-select.min.js']
js = [
'admin/js/jquery.min.js',
'admin/js/jquery.init.js',
'js/chained-select.min.js'
]

def render(self, name, value, attrs={}, choices=()):
field_prefix = attrs['id'][:attrs['id'].rfind('-') + 1]

# this variable is set but never used
formset_prefix = attrs['id'][:attrs['id'].find('-') + 1]

if not field_prefix:
paretnfield_id = "id_" + self.parent_field
parentfield_id = "id_" + self.parent_field
else:
paretnfield_id = field_prefix + self.parent_field
parentfield_id = field_prefix + self.parent_field

attrs['ajax_url'] = self.ajax_url

Expand All @@ -41,15 +50,15 @@ def render(self, name, value, attrs={}, choices=()):
//<![CDATA[
(function($) {
$(document).ready(function(){
var parent_field = $("#%(paretnfield_id)s");
var parent_field = $("#%(parentfield_id)s");
parent_field.addClass('chained-parent-field');
parent_field.attr('chained_id', "%(chained_id)s");
})
})(django.jQuery);
//]]>
</script>

""" % {"paretnfield_id":paretnfield_id, 'chained_id': attrs['id']}
""" % {"parentfield_id": parentfield_id, 'chained_id': attrs['id']}

output += js

Expand Down