25

在 Django 2.0 中,添加了autocomplete_fields,这很棒。

如果没有 autocomplete_fields,我可以使用formfield_for_foreignkey更改 ForeignKeyField 的查询集。

但是将两者结合在一起是行不通的——看起来自动完成的选项列表是动态的,并且来自不同的 url,而不是来自当前表单。

所以问题是——

如何更改自动完成小部件中的查询集?

4

4 回答 4

9

如果您使用autocomplete_fieldsfor a ManyToManyFieldon '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
于 2019-05-20T19:06:56.653 回答
8

覆盖 ModelAdmin 的get_search_results方法以使用您想要的查询。您可以在get_queryset提供用于获取查询集的自动完成字段的数据的视图方法中看到 - 此答案的来源是https://github.com/django/django/blob/03dbdfd9bbbbd0b0172aad648c6bbe3f39541137/django/contrib/管理员/视图/autocomplete.py#L42

于 2018-01-08T15:31:14.287 回答
3

我遇到了同样的问题,当使用 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

于 2020-06-14T12:05:06.500 回答
2

简短:您可以在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。- 更多关于它的文档。

于 2020-01-27T15:59:24.090 回答