在 Django 2.0 中,添加了autocomplete_fields,这很棒。
如果没有 autocomplete_fields,我可以使用formfield_for_foreignkey更改 ForeignKeyField 的查询集。
但是将两者结合在一起是行不通的——看起来自动完成的选项列表是动态的,并且来自不同的 url,而不是来自当前表单。
所以问题是——
如何更改自动完成小部件中的查询集?
在 Django 2.0 中,添加了autocomplete_fields,这很棒。
如果没有 autocomplete_fields,我可以使用formfield_for_foreignkey更改 ForeignKeyField 的查询集。
但是将两者结合在一起是行不通的——看起来自动完成的选项列表是动态的,并且来自不同的 url,而不是来自当前表单。
所以问题是——
如何更改自动完成小部件中的查询集?
如果您使用autocomplete_fields
for a ManyToManyField
on 'self',此示例将排除当前对象。
通过覆盖获取当前对象的 id 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)
接下来,覆盖get_search_results
. 仅为模型的自动完成 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
覆盖 ModelAdmin 的get_search_results
方法以使用您想要的查询。您可以在get_queryset
提供用于获取查询集的自动完成字段的数据的视图方法中看到 - 此答案的来源是https://github.com/django/django/blob/03dbdfd9bbbbd0b0172aad648c6bbe3f39541137/django/contrib/管理员/视图/autocomplete.py#L42。
我遇到了同样的问题,当使用 autocomplete_fields 时,limit_choices_to 没有生效,然后我为我的案例找到了一个解决方案,它也可以帮助其他人。这是我的想法和解决方案,任何人都应该更改代码以供他/她使用。
假设我们有两个模型model_A和model_B:我们将覆盖model_A的
model-admin的“get_search_results”(因为model_B有一个foreign_key(或m2m))在我的情况下,我只想限制对所有model_A对象的选择当前没有 model_B 连接对象,
或者在将 model_B 的对象更新为仅先前的 model_A 对象的情况下。所以我们去
# 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
https://gist.github.com/mh-firouzjaah/48dceae592d4b4275fa31d37ac77ff69
简短:您可以在django-admin-autocomlete-all中尝试我的解决方案或制作类似的东西。
长答案:
一个痛苦是:源外键的limit_choices_to-..也没有实现:(
我能够在目标 ModelAdmin 的 get_search_results() 中实现过滤器。但在这里,我们还有另一个严重的痛苦。我们可以检查request.is_ajax and '/autocomplete/' in request.path
。
此外,我们只有 request.headers['Referer']。借助此功能,我们可以将受影响的外键限制为 1 个模型。但是如果我们在同一个目标中有 2 个以上的外键(比如说:同一个模型实例中的两个用户角色),我们不知道其中哪一个调用了 ajax。
我的想法是修改网址。使用 Request url 我没有成功(经过长时间尝试在 DOM 和 js 中查找 select2 元素并扩展 url)。
但是我在使用 window.history.replaceState() 修改引用 url(即源管理页面 url)方面取得了一些成功。我可以临时修改 url /?key=author
,如果你将使用django-admin-autocomplete-all,它总是运行,我可以使用额外的自定义 javascript 将几乎所有内容添加到 Referer url 中。特别是添加其他表单字段的当前值可能有助于实现动态过滤(字段的依赖关系)。
所以,这是一个黑客,当然。但是你可以尝试 django-admin-autocomplete-all。- 更多关于它的文档。