8👍
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.
11👍
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
- [Django]-Django delete superuser
- [Django]-Simple Subquery with OuterRef
- [Django]-Django on IronPython
3👍
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.
- [Django]-What is the deal with the pony in Python community?
- [Django]-Django class based view ListView with form
- [Django]-How can I display a Django admin inline model within a fieldset?
3👍
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.py
class 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.py
class 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
- [Django]-Django can' t load Module 'debug_toolbar': No module named 'debug_toolbar'
- [Django]-RequestDataTooBig Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE
- [Django]-Uninstall Django completely
1👍
You can modify autocomplete queryset just by overriding get_search_results like this:
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
if 'autocomplete' in request.path:
queryset = queryset.exclude(field_name='foo')
return queryset, use_distinct
This function is inside admin.py and use it with model you refer to, if model used in autocomplete is Bar, then get_search_results should be in class BarAdmin(admin.ModelAdmin)
Also if you use same Model in multiple autocomplete_fields you can change queryset depending on where it’s called. Example:
@admin.register(Foo)
class FooAdmin(admin.ModelAdmin):
fields = (
'the_field',
)
autocomplete_fields = ('the_field',)
@admin.register(Bar)
class BarAdmin(admin.ModelAdmin):
fields = (
'the_same_field',
)
autocomplete_fields = ('the_same_field',)
@admin.register(Key)
class KeyAdmin(admin.ModelAdmin):
fields = (
'name',
)
ordering = [
'-id',
]
search_fields = [
'name',
]
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
if 'autocomplete' in request.path:
if 'foo' in request.headers['referer']:
queryset = queryset.exclude(name='foo')
elif 'bar' in request.headers['referer']:
queryset = queryset.exclude(name='bar')
return queryset, use_distinct
We got Foo and Bar models with ForeingKeys to Key Model and used with autocomplete.
Now when we open autocomplete changing Foo, we will only see queryset with name equal to ‘foo’, also when we open autocomplete in Bar, we will only see queryset with name equal to ‘bar’, this way you can modify queryset depending on where is autocomplete called.
- [Django]-Django character set with MySQL weirdness
- [Django]-Django and postgresql schemas
- [Django]-Simple way to reset Django PostgreSQL database?
0👍
Another way to filter the options of an autocomplete field is through the limit_choices_to
parameter of the relation field in the model.
Here is an example which limits the cities shown in the autocomplete field to cities in Europe.
class Location(models.Model):
...
cities = models.ManyToManyField(City, limit_choices_to=Q(region="Europe"))
...
@admin.register(Location)
class LocationAdmin(admin.ModelAdmin):
...
autocomplete_fields = ["cities"]
- [Django]-How to create SaaS application with Python and Django
- [Django]-"Too many SQL variables" error in django with sqlite3
- [Django]-How to get the current URL within a Django template?