After many studies, I found that the problem is related to how the search query is constructed for the admin search field (in the ChangeList class). In multi-mode searches (words separated by a space), each term is added to the QuerySet, catching a new filter() . When search_fields has one or more related fields, the generated SQL query will have many JOIN chained one after another with many JOIN for each related field (see My related question for some examples and more details). This chain of JOIN exists so that each term will only search in a subset of the data filter using the use case term AND, most importantly, the associated field should have only one term (vs, which needs to have EVERYTHING) to make the match. See Pinning ambiguous relationships in Django docs for more information on this. I am pretty sure that this behavior was most required for the admin search field.
The disadvantage of this query (with related fields) is that the performance change (query execution time) can be very large. It depends on many factors: the number of searched terms, search terms, type of search in the field (VARCHAR, etc.), the amount of search in the field, data in tables, size of tables, etc. With the right combination, itβs easy for a query to be executed mostly forever (a query that takes more than 10 minutes for me is a query that runs forever in the context of this search field).
The reason this can take so long is that the database needs to create a temporary table for each term and look at it completely to find the next term. Thus, it adds up very quickly.
A possible change that needs to be made to improve performance is ANDed all members in the same filter() . Thus, there will be only one JOIN related field (or 2, if this is a lot for many), and not much more. This query will be much faster and with very little change in performance. The disadvantage is that the related fields must have ALL CONDITIONS to match, so in many cases you can get fewer matches.
UPDATE
As trinchet heres asked, what was needed to change the search behavior (for Django 1.7). You must override get_search_results() of the admin classes where you want to perform this search. You need to copy all the method code from the base class ( ModelAdmin ) into your own class. Then you need to change these lines:
for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries))
To that:
and_queries = [] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] and_queries.append(Q(reduce(operator.or_, or_queries))) queryset = queryset.filter(reduce(operator.and_, and_queries))
This code has not been verified. My original code was for Django 1.4, and I just adapt it for 1.7 here.