class ChildSearchChangeList(ChangeList): # list of tuples (child_model, field_to_search, foreign_key_field) searches = [] def get_query_set(self, request): qs = super(ChildSearchChangeList, self).get_query_set(request) if request.GET and request.GET.get('q') and len(self.searches) > 0: query = request.GET.get('q') # applying all child filters for (child_model, field_to_search, foreign_key_field) in self.searches: filters = {self.construct_search(field_to_search): query} qs = qs | qs.model.objects.filter(id__in=child_model.objects.filter(**filters).values_list( self.get_db_name(child_model, foreign_key_field), flat=True)) # collect all the declared list filters. (self.filter_specs, self.has_filters, remaining_lookup_params, use_distinct) = self.get_filters(request) # let every list filter modify the queryset to its liking. for filter_spec in self.filter_specs: new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs return qs @staticmethod def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name @staticmethod def get_db_name(model, field_name): for field in model._meta.fields: if field.name == field_name: return field.get_attname() return None class ChildSearchAdmin(admin.ModelAdmin): child_searches = [] def get_changelist(self, request, **kwargs): ChildSearchChangeList.searches = self.child_searches return ChildSearchChangeList