如何在Django 2中过滤autocomplete_fields的选项?
这问题我之前踩过坑!你说的情况完全正常——因为autocomplete_fields走的是Django Admin独立的AJAX接口,跟formfield_for_foreignkey修改的表单字段查询集根本不是一回事,所以才会失效。
先理清楚逻辑
当你开启autocomplete_fields后,Admin页面会通过AJAX请求去后台拉取选项数据,这个请求指向的是Django内置的autocomplete视图,它直接从关联模型的Admin查询集(或者自定义视图)里取数据,完全绕开了你在表单字段里设置的查询集。所以formfield_for_foreignkey的修改自然不会生效。
分场景解决
场景1:全局限制关联模型的选项(简单需求)
如果你的需求是所有用到这个关联模型的autocomplete都显示相同过滤后的结果(比如只显示活跃状态的对象),那直接在关联模型的ModelAdmin里重写get_queryset就行:
from django.contrib import admin from .models import RelatedModel class RelatedModelAdmin(admin.ModelAdmin): def get_queryset(self, request): # 先拿到默认的查询集 qs = super().get_queryset(request) # 添加你的过滤条件,比如只保留活跃的对象 return qs.filter(is_active=True)
这样所有关联到RelatedModel的autocomplete字段,都会自动用这个过滤后的查询集。
场景2:针对当前ModelAdmin的局部限制(复杂需求)
如果你的限制和当前编辑的对象、当前用户相关(比如只能看到当前用户创建的关联对象,或者和当前编辑对象关联的选项),就得自定义autocomplete视图,替换掉默认的接口:
- 第一步:自定义Autocomplete视图
继承Django内置的AutocompleteJsonView,重写get_queryset来实现你的过滤逻辑:
from django.contrib.admin.views.autocomplete import AutocompleteJsonView from .models import RelatedModel class CustomRelatedAutocompleteView(AutocompleteJsonView): def get_queryset(self): # 先拿到默认的查询集(已经包含了搜索关键词的过滤) qs = super().get_queryset() # 示例1:根据当前用户过滤 qs = qs.filter(created_by=self.request.user) # 示例2:根据当前编辑的对象过滤(从GET参数里取object_id) current_object_id = self.request.GET.get('object_id') if current_object_id: from .models import MyModel # 你的当前Model current_obj = MyModel.objects.get(id=current_object_id) # 添加和当前对象相关的过滤条件 qs = qs.filter(related_field=current_obj.some_field) return qs
- 第二步:在当前ModelAdmin里替换默认URL
重写get_urls添加自定义视图的URL,再通过get_autocomplete_fields_url指定字段用这个自定义URL:
from django.contrib import admin from django.urls import path from django.urls import reverse from .models import MyModel from .views import CustomRelatedAutocompleteView class MyModelAdmin(admin.ModelAdmin): autocomplete_fields = ['related_field'] # 你的外键字段名 def get_urls(self): # 先拿到默认的Admin URL default_urls = super().get_urls() # 添加自定义的autocomplete URL,优先级比默认的高 custom_urls = [ path( 'related-field-autocomplete/', self.admin_site.admin_view(CustomRelatedAutocompleteView.as_view(model_admin=self)), name='myapp-mymodel-related-field-autocomplete', ), ] return custom_urls + default_urls def get_autocomplete_fields_url(self, model_admin, field_name): # 当字段是related_field时,返回自定义的URL if field_name == 'related_field': return reverse('admin:myapp-mymodel-related-field-autocomplete') # 其他字段用默认逻辑 return super().get_autocomplete_fields_url(model_admin, field_name)
小提醒
测试的时候记得清空浏览器缓存,AJAX请求有时候会缓存旧的结果,导致你看起来修改没生效。
内容的提问来源于stack exchange,提问作者Oren Shpigel
相关产品推荐
相关产品推荐

