Как я могу отфильтровать набор запросов автозаполнения по атрибутам form.instance?

Я пытаюсь отфильтровать набор запросов M2M с поддержкой автозаполнения. Я могу заставить фильтр работать со встроенной ModelForm. Вот упрощенная версия кода, которая безупречно работает без автозаполнения:

модели.py:

class FEmodel(models.Model):
    name = models.Charfield(max_length=200)

class Workspace(models.Model):
    name = models.Charfield(max_length=200)
    fem = models.ForeignKey(FEmodel)

class Element(models.Model):
    EID = models.PositiveIntegerField()
    fem = models.ForeignKey(FEmodel)

class Panel(models.Model):
    workspace = models.ForeignKey(Workspace)
    elements = models.ManyToManyField(Element)

формы.py:

class PanelForm(forms.ModelForm):
    def __init__(self,*args,**kwargs):
        super(PanelForm,self).__init__(*args,**kwargs)
        ws = self.instance.workspace
        self.fields['elements'].queryset = Element.objects.filter(fem=ws.fem)
    class Meta:
        model = Panel
        fields = ('__all__')

просмотров.py:

@login_required(login_url='/login/')
def panel_edit(request, pk, id=None):
    workspace = get_object_or_404(Workspace, pk=pk)
    if id:
        panel = get_object_or_404(Panel, pk=id)
    else:
        panel = Panel(workspace = workspace)
    if request.method == 'POST':
        form = PanelForm(request.POST, instance=panel)    
        if form.is_valid():
            panel = form.save(commit=True)        
            return panels(request, pk)
        else:
            print form.errors
    else:
        form = PanelForm(instance=panel)

    return render(request, 'structures/Panel/panel_edit.html', {'form': form, 'panel': panel, 'workspace': workspace})

URL.py:

...
url(r'^workspace/(?P<pk>[0-9]+)/panel/new/$', views.panel_edit, name='panel_edit'),
...

панель_edit.html:

...
<form method="POST" class="form-horizontal">
    {% csrf_token %}
    {% bootstrap_form form %}
    {% buttons %}
        <button type="submit"> Save</button>
    {% endbuttons %}
</form>
....

И вот версия автозаполнения, которую я не мог заставить работать:

autocomplete_light_registry.py

class ElementAutocomplete(acl.AutocompleteModelBase):
    search_fields = ['EID']
acl.register(Element, ElementAutocomplete)

формы.py:

import autocomplete_light.shortcuts as acl

class PanelForm(acl.ModelForm):
    def __init__(self,*args,**kwargs):
        super(PanelForm,self).__init__(*args,**kwargs)
        ws = self.instance.workspace
        self.fields['elements'].queryset = Element.objects.filter(fem=ws.fem)
    class Meta:
        model = Panel
        fields = ('__all__')

Эта версия не выдает ошибок, но не предоставляет выбор элементов, отфильтрованных по атрибуту form.instance.ws.fem. Вместо этого он дает все объекты Element.

Что я делаю не так?

редактировать 1:

  • в form.py super(Panel,self) было исправлено как super(PanelForm,self)
  • опечатки в отступах были исправлены

редактировать 2: добавлены необходимые части URL, представления и шаблона

редактировать 3: Согласно ответу @jpic, вот решение:

добавлено в panel_edit.html:

{% block bootstrap3_extra_head %}
{{ block.super }}

<script type="text/javascript">
    $( document ).ready(function() {
        elements_autocomplete = $('input[name=elements-autocomplete]').yourlabsAutocomplete()
        elements_autocomplete.data['ws_pk'] = {{ form.instance.workspace.pk }}
    });

</script>

{% endblock %}

autocomplete_light_registry.py:

import autocomplete_light as acl

class ElementAutocomplete(acl.AutocompleteModelBase):
    search_fields = ['EID']
    model = Element

    def choices_for_request(self):
        ws = Workspace.objects.get(pk=self.request.GET.get('ws_pk', None))
        self.choices = self.choices.filter(fem=ws.fem)
        return super(ElementAutocomplete, self).choices_for_request()

acl.register(ElementAutocomplete)

формы.py:

class PanelForm(acl.ModelForm):

    class Meta:
        model = Panel
        fields = ('__all__')

person gsahin    schedule 11.01.2016    source источник
comment
как выглядят просмотры и URL-адреса?   -  person jpic    schedule 14.01.2016
comment
@jpic добавил. Кстати, манипулирование вариантами на стороне клиента с помощью javascript не подходит для моего случая, поскольку это вызовет проблемы с производительностью.   -  person gsahin    schedule 15.01.2016


Ответы (1)


Объект автозаполнения JS нуждается в значении pk, чтобы передать его в представление, которое вызывает объект автозаполнения Python, затем вы можете отфильтровать экземпляр pk в методе selection_for_request() объекта автозаполнения python.

Один из способов получить объект автозаполнения js — получить его из самого элемента ввода с помощью подключаемого модуля jQuery, т.е.:

elements_autocomplete = $('input[name=elements-autocomplete]').yourlabsAutocomplete()

Убедитесь, что это вызывается после загрузки jquery-autocomplete-light JS.

Затем добавьте fk к своим данным. :

elements_autocomplete.data['panel_pk'] = {{ form.instance.pk }}

В choices_for_request() вы, вероятно, уже поняли это:

def choices_for_request(self):
    choices = super(ElementAutocomplete, self).choices_for_request()

    panel_pk = request.GET.get('panel_pk', None)
    if panel_pk and panel_pk.isdigit():
        choices = choices.filter(panel__pk=panel_pk)

    return choices

На самом деле, это очень легко проверить, в браузере откройте консоль JS и запустите: $('input[name=elements-autocomplete]').yourlabsAutocomplete().data['foo'] = 'bar' и вы увидите, что последующие запросы, сделанные сценарием автозаполнения, будут добавлять &foo=bar к его URL-адресу, делая его доступным для selection_for_request через себя. запрос !

person jpic    schedule 15.01.2016
comment
Вот так, спасибо! Ваше предложение сработало. Однако мне пришлось немного изменить метод selection_for_request(). Я сообщаю весь рабочий набор как новое редактирование для возможных новичков. - person gsahin; 17.01.2016