How to filter choices in Django2's autocomplete_fields?

If you are using autocomplete_fields for a ManyToManyField on 'self', this example will exclude the current object.

Get the current object's id by overriding get_form:

field_for_autocomplete = None

def get_form(self, request, obj=None, **kwargs):
    if obj:
        self.field_for_autocomplete = obj.pk

    return super(MyAdmin, self).get_form(request, obj, **kwargs)

Next, override get_search_results. Modify the queryset only for your model's autocomplete URI:

def get_search_results(self, request, queryset, search_term):
    queryset, use_distinct = super().get_search_results(request, queryset, search_term)

    # Exclude only for autocomplete
    if request.path == '/admin/myapp/mymodel/autocomplete/':
        queryset = queryset.exclude(field=self.field_for_autocomplete)

    return queryset, use_distinct

Override the ModelAdmin's get_search_results method to use the query you want. You can see in the get_queryset method for the view providing the data for autocomplete fields that it's used to get the queryset - the source as of this answer is https://github.com/django/django/blob/03dbdfd9bbbbd0b0172aad648c6bbe3f39541137/django/contrib/admin/views/autocomplete.py#L42.


Short: You can try my solution in django-admin-autocomlete-all or make something similar.

Long answer:

One pain is: limit_choices_to-.. of source foreign key is not implemented too :(

I was able to implement filter into get_search_results() of the target ModelAdmin. But here we have another serious pain. We can check request.is_ajax and '/autocomplete/' in request.path.

In addition we only have request.headers['Referer']. With help of this we can limit affected foreign keys to 1 model. But if we have 2+ foreign keys into same target (lets say: two user roles inside the same model instance), we don't know which one of them calls the ajax.

My idea was modify the url's. With Request url I was not successfull (after long attempts to find in DOM and in js the select2 elements and extend the url).

But I have some success with modifying of the Referer url (ie. source admin page url) using window.history.replaceState(). I can temporary modify the url like /?key=author - which run always if you will use django-admin-autocomplete-all and I am able to add almost everything into Referer url with additional custom javascript. Especially adding of current values of other form fields could be useful to implement dynamic filtering (dependencies of fields).

So, it is a hack, sure. But you can give try to django-admin-autocomplete-all. - More in docs of it.