Login

Ajax auto-filtered Foreign Key Field

Author:
anentropic
Posted:
November 10, 2009
Language:
Python
Version:
1.1
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. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 12 months ago

Comments

Please login first before commenting.