diff --git a/README.md b/README.md index 29048bb..a6ae3dd 100644 --- a/README.md +++ b/README.md @@ -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): @@ -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 - diff --git a/chained_selectbox/form_fields.py b/chained_selectbox/form_fields.py index 5895a69..043b51f 100644 --- a/chained_selectbox/form_fields.py +++ b/chained_selectbox/form_fields.py @@ -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 - diff --git a/chained_selectbox/forms.py b/chained_selectbox/forms.py index c4b834a..d8d4240 100644 --- a/chained_selectbox/forms.py +++ b/chained_selectbox/forms.py @@ -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) diff --git a/chained_selectbox/widgets.py b/chained_selectbox/widgets.py index a8749f1..c7ef6c9 100644 --- a/chained_selectbox/widgets.py +++ b/chained_selectbox/widgets.py @@ -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'), )) @@ -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 @@ -41,7 +50,7 @@ def render(self, name, value, attrs={}, choices=()): // - """ % {"paretnfield_id":paretnfield_id, 'chained_id': attrs['id']} + """ % {"parentfield_id": parentfield_id, 'chained_id': attrs['id']} output += js