views:

83

answers:

1

I am having difficulties with forms, specifically ModelMultipleChoiceField.

I've pieced together this code from various examples, but it sadly doesn't work.

I would like to be able to:

  1. Search for some Works on work_search.html
  2. Display the results of the search, with checkboxes next to each result
  3. Select the Works I want, via the checkboxes
  4. After pressing Add, display which works were selected.

I believe everything is okay except the last part. The page simply displays "works" :(

Here is the code - sorry about the length.

Models.py

class Work(models.Model):
    title = models.CharField(max_length=200)
    artist = models.CharField(max_length=200)
    writers = models.CharField(max_length=200)

    def __unicode__(self):
        return self.title + ' - ' + self.artist

forms.py

class WorkSelectForm(forms.Form):
    def __init__(self, queryset, *args, **kwargs):
        super(WorkSelectForm, self).__init__(*args, **kwargs)
        self.fields['works'] = forms.ModelMultipleChoiceField(queryset=queryset,       widget=forms.CheckboxSelectMultiple())

views.py

def work_search(request):
    query = request.GET.get('q', '')
    if query:
        qset = (
            Q(title__icontains=query) |
            Q(artist__icontains=query) |
            Q(writers__icontains=query)
        )
        results = Work.objects.filter(qset).distinct()
        form = WorkSelectForm(results)
        return render_to_response("work_search.html", {"form": form, "query": query })    
    else:
        results = []
    return render_to_response("work_search.html", {"query": query })

def add_works(request):
    #if request.method == POST:
    form = WorkSelectForm(request.POST)
    #if form.isvalid():
    items = form.fields['works'].queryset
    return render_to_response("add_works.html", {"items":items})

work_search.html

{% extends "base.html" %}
{% block content %}
  <h1>Search</h1>
  <form action="." method="GET">
    <label for="q">Search: </label>
    <input type="text" name="q" value="{{ query|escape }}">
    <input type="submit" value="Search">
  </form>

  {% if query %}
    <h2>Results for "{{ query|escape }}":</h2>
    <form action="add_works" method="post">
        <ul>
        {% if form %}
            {{ form.as_ul }}
        {% endif %}
        </ul>
        <input type="submit" value="Add">
    </form>
  {% endif %}
{% endblock %}

add_works.html

{% extends "base.html" %}
{% block content %}
    {% if items %}
        {% for item in items %}
            {{ item }}
        {% endfor %}
    {% else %}
        <p>Nothing selected</p>
    {% endif %}
{% endblock %}
+1  A: 

In add_works, you're not constructing your WorkSelectForm the right way. It's expecting as a first parameter the queryset of possible/authorized choices, then the POST data.

Also, you're not accessing the selected works correctly from the form. You have to use is_valid method on the form, then use cleaned_data as described in the doc.

From what I see in your work_search view, there's no restriction on which Work objects you can search then add to the result, so you could do simply:

def add_works(request):
    #if request.method == POST:
    form = WorkSelectForm(Work.objects.all(), request.POST)
    if form.is_valid():
        # the items are in form.cleaned_data['works']
        items = form.cleaned_data['works']
        return render_to_response("add_works.html", {"items":items})
    else:
       # handle error case here
       ...
Clément
You sir, are a legend. Thank you very much!
colinjameswebb
What would you advise if Work.objects.all() is going to contain a massive number of results?
colinjameswebb
It's not a problem. The queryset in your `ModelMultipleChoiceField` is just used as a base queryset, on top of which is added an `__in` lookup with the value you submitted to the form. So the total number of items in `Work.objects.all()` doesn't matter.
Clément
Ah right. Thanks for being helpful again!
colinjameswebb