Как отфильтровать варианты в полях автозаполнения Django2?

В Django 2.0 autocomplete_fields Добавлено, и это здорово.

Без autocomplete_fields я могу изменить набор запросов ForeignKeyField, используя formfield_for_foreignkey.

Но объединение двух вместе не работает - похоже, что список параметров для автозаполнения является динамическим и исходит из другого URL-адреса, а не из текущей формы.

Итак, вопрос -

Как я могу изменить набор запросов в виджете автозаполнения?


person Oren Shpigel    schedule 08.01.2018    source источник
comment
Я столкнулся с тем же вопросом... у вас есть пример вашего решения, пожалуйста   -  person Glenn Sampson    schedule 06.03.2019


Ответы (4)


Переопределить get_search_results ModelAdmin. метод для использования запроса, который вы хотите. Вы можете видеть в методе get_queryset представление, предоставляющее данные для полей автозаполнения, которые используются для получения набора запросов. Источником этого ответа является https://github.com/django/django/blob/03dbdfd9bbbbd0b0172aad648c6bbe3f39541137/ L42.

person Peter DeGlopper    schedule 08.01.2018
comment
Поправьте меня, если я ошибаюсь, но при переопределении get_search_results затрагивается каждый внешний ключ модели? В моем случае у меня есть родитель FK для «я», и я хочу предотвратить выбор какой-либо модели в качестве собственного родителя и разрешить только модели верхнего уровня (родитель = Нет) в качестве родителя. В то же время другие модели должны иметь возможность назначать каждую запись как FK. - person kelvan; 11.01.2019
comment
Я не углублялся в это, но сразу же вы могли бы сделать это, проверив аргумент request для get_search_results. Он должен иметь PK объекта, который вы редактируете, чтобы вы могли его исключить. И у него будет URL-адрес, поэтому, если вы хотите, чтобы автозаполнение ограничивалось только экземплярами верхнего уровня из этого административного представления, вы можете это сделать. Если вы хотите автозаполнение с такими ограничениями для одного поля и одно без них для другого поля, это сложнее — вам может потребоваться настроить подкласс представления json для его обработки. Однако подключить это к администратору может быть сложно. - person Peter DeGlopper; 11.01.2019

Если вы используете autocomplete_fields вместо ManyToManyField для 'self', этот пример исключит текущий объект.

Получите идентификатор текущего объекта, переопределив 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
person Mike    schedule 20.05.2019
comment
Я использую часть вашего ответа, это работает, большое спасибо. Кстати, request.path== можно найти в исходном коде страницы администратора, поиск: data-ajax--url. Я не использовал /admin/ в качестве URL-адреса администратора. Так и не получилось в начале. - person C.K.; 07.08.2019

У меня была такая же проблема: при использовании autocomplete_fields limit_choices_to не влиял, а затем я нашел решение для своего случая, которое может помочь и другим. это идея и решение для моего случая, любой должен изменить код для своего использования.

представьте, что у нас есть две модели model_A и modle_B: мы собираемся переопределить get_search_results
model-admin для 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

person Mahdi Firouzjaah    schedule 14.06.2020
comment
Хотя теоретически это может ответить на вопрос, было бы предпочтительнее включить сюда основные части ответа и предоставить ссылку для справки. . - person β.εηοιτ.βε; 14.06.2020
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. – Из обзора - person Nick; 14.06.2020

Кратко: вы можете попробовать мое решение в django-admin-autocomlete-all. или сделать что-то подобное.

Длинный ответ:

Одна проблема: limit_choices_to-.. внешнего ключа источника тоже не реализован :(

Мне удалось внедрить фильтр в get_search_results() целевого ModelAdmin. Но здесь у нас есть еще одна серьезная боль. Мы можем проверить request.is_ajax and '/autocomplete/' in request.path.

Кроме того, у нас есть только request.headers['Referer']. С помощью этого мы можем ограничить затронутые внешние ключи одной моделью. Но если у нас есть 2+ внешних ключа в одной и той же цели (скажем: две роли пользователя внутри одного и того же экземпляра модели), мы не знаем, какой из них вызывает ajax.

Моя идея заключалась в том, чтобы изменить URL-адрес. С URL-адресом запроса мне не удалось (после долгих попыток найти в DOM и в js элементы select2 и расширить URL-адрес).

Но у меня есть некоторый успех в изменении URL-адреса Referer (т.е. исходного URL-адреса страницы администратора) с помощью window.history.replaceState(). Я могу временно изменить URL-адрес, например /?key=author, который работает всегда, если вы будете использовать django-admin-autocomplete-all, и я могу добавить почти все в URL-адрес Referer с помощью дополнительного пользовательского javascript. Особенно может быть полезно добавление текущих значений других полей формы для реализации динамической фильтрации (зависимости полей).

Так что это взлом, конечно. Но вы можете попробовать django-admin-autocomplete-all. - Подробнее в документации.

person mirek    schedule 27.01.2020
comment
Спасибо! Ваша библиотека - единственное, что сработало! - person Marc LaBelle; 03.04.2020