簡體   English   中英

如何過濾 Django2 的 autocomplete_fields 中的選擇?

[英]How to filter choices in Django2's autocomplete_fields?

Django 2.0,增加了autocomplete_fields ,很棒。

沒有 autocomplete_fields,我可以使用formfield_for_foreignkey更改 ForeignKeyField 的查詢集。

但是將兩者組合在一起是行不通的——自動完成選項列表似乎是動態的,並且來自不同的 url,而不是來自當前表單。

所以問題是——

如何更改自動完成小部件中的查詢集?

如果您對“self”上的ManyToManyField使用autocomplete_fields ,則此示例將排除當前對象。

通過覆蓋get_form獲取當前對象的 id :

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/cont /admin/views/autocomplete.py#L42

我在某種程度上遇到了同樣的問題,當使用 autocomplete_fields 時 limit_choices_to 沒有生效,然后我為我的案例找到了一個解決方案,它也可以幫助其他人。 這是我的想法和解決方案,任何人都應該更改代碼以供他/她使用。

假設我們有兩個模型 model_A 和 modle_B:我們將覆蓋“get_search_results”
在我的例子中,model_A 的模型管理員(因為 model_B 有一個外鍵(或 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.is_ajax and '/autocomplete/' in request.path

此外,我們只有 request.headers['Referer']。 在此幫助下,我們可以將受影響的外鍵限制為 1 個模型。 但是如果我們有 2 個以上的外鍵進入同一個目標(假設:同一個模型實例中的兩個用戶角色),我們不知道其中哪一個調用了 ajax。

我的想法是修改網址。 使用請求 url 我沒有成功(經過長時間嘗試在 DOM 和 js 中查找 select2 元素並擴展 url)。

但是我在使用 window.history.replaceState() 修改 Referer url(即源管理頁面 url)方面取得了一些成功。 我可以臨時修改像/?key=author這樣的 url - 如果您將使用django-admin-autocomplete-all ,它將始終運行,並且我可以使用額外的自定義 javascript 將幾乎所有內容添加到 Referer url 中。 特別是添加其他表單字段的當前值對於實現動態過濾(字段的依賴關系)非常有用。

所以,這是一個黑客,當然。 但是您可以嘗試 django-admin-autocomplete-all。 - 更多關於它的文檔。

您可以通過重寫 get_search_results 來修改自動完成查詢集,如下所示:

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

這個function在admin.py里面,和你參考的model一起使用,如果autocomplete中使用的model是Bar,那么get_search_results應該在class BarAdmin(admin.ModelAdmin)

此外,如果您在多個 autocomplete_fields 中使用相同的 Model,您可以根據調用位置更改查詢集。 例子:

@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

我們得到了 Foo 和 Bar 模型,其 ForeingKeys 為 Key Model 並與自動完成一起使用。 現在當我們打開自動完成更改 Foo 時,我們只會看到名稱等於'foo'的查詢集,同樣當我們在 Bar 中打開自動完成時,我們只會看到名稱等於'bar'的查詢集,這樣您就可以根據需要修改查詢集在哪里調用自動完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM