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""" %(parent_output)s """ % 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)