Login

Ajax auto-filtered Foreign Key Field

Author:
anentropic
Posted:
November 10, 2009
Language:
Python
Version:
1.1
Tags:
ajax widget
Score:
1 (after 1 ratings)

Say you have a ModelChoiceField and you want the choices to depend on the value of another field in the form... that's what these bits are for.

They need to be used in conjunction with the Ajax views from: code.google.com/p/django-ajax-filtered-fields/

See my blog for full details: anentropic.wordpress.com

...um, this is work in progress and the explanatory blog post is not up yet...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class AjaxAutoSelect(forms.Select):
    """
    May not work for all possible widget types for the related field
    (simple html form widgets should be okay, we listen to jQuery 'change' event)
    """
    related_field = ''
    def render(self, name, value, attrs=None, choices=()):
        self._element_id = attrs['id']
        
        # normal widget output from the ancestor
        # create a field with a dummy name, the real value
        # will be retrieved from a hidden field
        parent_output = super(AjaxAutoSelect, self).render("dummy-%s" % name, value, attrs, choices)
        
        # output
        if value is None:
            value = ''
        mapping = {
            "related_field": self.related_field,
            "parent_output": parent_output,
            "name": name,
            "element_id": self._element_id, 
            "value": value,
            'app_label': self.model._meta.app_label,
            'object_name': self.model._meta.object_name,
            'select_related': self.select_related,
        }
        
        # TO DO:
        # we shouldn't SelectBox.empty() if we're editing an existing record that has a value in the related field
        output = u"""
            <input type="hidden" name="%(name)s" id="hidden-%(element_id)s" value="%(value)s" />
            
            %(parent_output)s
            
            <script type="text/javascript" charset="utf-8">
        		$(document).ready(function(){
                    SelectBox.init('%(element_id)s');
                    if (!$('#id_%(related_field)s').val()) {
                        SelectBox.empty('%(element_id)s');
                    }
                    
                    ajax_filtered_fields.bindForeignKeyOptions("%(element_id)s");
                    
                    $('#id_%(related_field)s').change(function(evt){
                        ajax_filtered_fields.getForeignKeyJSON('%(element_id)s', '%(app_label)s', '%(object_name)s', '%(related_field)s__pk='+$(evt.target).val(), '%(select_related)s');
                    });
        		});
        	</script>
            """ % mapping
            
        return mark_safe(output)


class AjaxFilteredForeignKeyField(forms.ModelChoiceField):
    def __init__(self, model, field_name, widget=AjaxAutoSelect, *args, **kwargs):
        queryset = model.objects.none()
        super(AjaxFilteredForeignKeyField, self).__init__(queryset, widget=widget, *args, **kwargs)
        
        self.isFilteredForeignKeyField = True
        self.widget.model = self.model = model
        self.widget.related_field = self.related_field = field_name
        self.widget.select_related = None


class AjaxFilteredFKFieldsForm(forms.ModelForm):
    def __init__(self, initial=None, *args, **kwargs):
        super(AjaxFilteredFKFieldsForm, self).__init__(initial=initial, *args, **kwargs)
        for field_name in self.fields:
            field = self.fields[field_name]
            if getattr(field, 'isFilteredForeignKeyField', False):
                if field.related_field in self.initial:
                    # sometimes it's a list with one member, sometimes it's just a number (awesome!)
                    try:
                        fk_val = self.initial.get(field.related_field, self.fields[field.related_field].initial)[0]
                    except TypeError:
                        fk_val = self.initial.get(field.related_field, self.fields[field.related_field].initial)
                    fargs = {'%s__pk'%field.related_field: fk_val}
                    field.queryset = field.model.objects.filter(**fargs)

More like this

  1. ModelChoiceField with optiongroups by anentropic 5 years, 4 months ago
  2. django-mptt enabled replacement for SelectBox.js by anentropic 5 years, 9 months ago
  3. django-mptt enabled FilteredSelectMultiple m2m widget by anentropic 5 years, 9 months ago
  4. MPTTModelAdmin by anentropic 5 years, 9 months ago
  5. django ajax_view decorator by chizcracker 3 years, 2 months ago

Comments

Please login first before commenting.