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