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 = Nonedef 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.
I had somehow the same problem, when using autocomplete_fields the limit_choices_to was not taking affect, and then I found a solution for my case which may help others too.this an idea and a solution for my case, anybody should change the code for his/her use.
imagine we have two models model_A and modle_B:we are going to override the "get_search_results" of
model-admin of model_A(because model_B has a foreign_key(or m2m) to it)in my case I just want to limit choices to all model_A objects which
currentlly dont have a model_B connected object(s)or in case of updating an object of model_B limit to just the previous model_A object(s).so we go
# moodels.pyclass model_A(models.Model): name = models.CharField()class model_B(models.Model): name = models.CharField() fk_field = models.OneToOneField( #ManyToManyField or ForeignKey model_A, related_name='fk_reverse', on_delete=models.CASCADE)# admin.pyclass model_A_Admin(admin.ModelAdmin): search_fields = ('name', ) def get_search_results(self, request, queryset, search_term): import re queryset, use_distinct = super().get_search_results(request, queryset, search_term) # note: str(request.META.get('HTTP_REFERER')) is the url from which the request had come/previous url. if "model_b/add/" in str(request.META.get('HTTP_REFERER')): # if we were in creating new model_B instanse page # note: the url is somehow containing model_Bs calss name then / then "add" # so there is no related object(of model_A) for non exsisting object(of model_B) queryset = self.model.objects.filter(fk_reverse=None) elif re.search(r"model_b/\d/change/", str(request.META.get('HTTP_REFERER'))): # if we were in updatineg page of an exsisting model_B instanse # the calling page url contains the id of the model_B instanse # we are extracting the id and use it for limitaion proccess pk = int(re.findall(r'\d+', str(str(request.META.get('HTTP_REFERER')).split('/')[-3: ]))[-1]) queryset = self.model.objects.filter(fk_reverse=pk) return queryset, use_distinct
https://gist.github.com/mh-firouzjaah/48dceae592d4b4275fa31d37ac77ff69