views:

663

answers:

2

Django newbie question....

I'm trying to write a search form and maintain the state of the input box between the search request and the search results.

Here's my form:

class SearchForm(forms.Form):
    q = forms.CharField(label='Search: ', max_length=50)

And here's my views code:

def search(request, q=""):
    if (q != ""): 
        q = q.strip()
        form = SearchForm(initial=q) 
        #get results here...
        return render_to_response('things/search_results.html',
          {'things': things,  'form': form, 'query': q})
    elif (request.method == 'POST'): # If the form has been submitted
        form = SearchForm(request.POST) 
        if form.is_valid(): 
          q = form.cleaned_data['q']
          # Process the data in form.cleaned_data
          return HttpResponseRedirect('/things/search/%s/' % q) # Redirect after POST
        else:
          form = SearchForm() 
          return render_to_response('things/search.html', {
            'form': form,
          })
    else:
        form = SearchForm()
        return render_to_response('things/search.html', {
            'form': form,
        })

But this gives me the error:

Caught an exception while rendering: 'unicode' object has no attribute 'get'

How can I pass the initial value? Various things I've tried seem to interfere with the request.POST parameter.

+3  A: 

Several things are not good here...

1) The recommended thing after a POST is to redirect. This avoids the infamous popup saying that you are resubmitting the form when using the back button.

2) You don't need to say if request.method == 'POST', just if request.POST. That makes your code easier to read.

3) The view generally looks something like:

def myview(request):
    # Some set up operations
    if request.POST:
       form=MyForm(request.POST)
       if form.is_valid():
          # some other operations and model save if any
          # redirect to results page
    form=MyForm()
    #render your form template

That is not to say that there can't be much simpler and much more complicated views. But that is the gist of a view: if request is post process the form and redirect; if request is get render the form.

I don't know why you are getting an unicode error. I can only think that it is related to one of your models that you don't provide. The error, as spookylukey mentions is in his comment, most likely is caused by you submitting a string instead of a dict to the initial parameter.

I really recommend the django documentation, in particular the tutorial., but there is also the very nice Django Book.

All that said, I think you want something like:

def search(request, q=None):
    if request.POST:
        form = SearchForm(request.POST) 
        if form.is_valid(): 
           q = form.cleaned_data['q']
           url=reverse('search_results', args=(q,))
           return HttpResponseRedirect(url)
    if q is None:
        form = SearchForm() 
    else: 
        form = SearchForm(initial={'q': q})
    return render_to_response('things/search.html', {
        'form': form,
    })

Notice that the parameter to initial is a dict of the field values of your form.

Hope that helps.

celopes
The error is due to passing a unicode object instead of a dict. (dict defines 'get()', but string/unicode does not).
spookylukey
Ah, excellent point. I saw the error but didn't make the connection!
celopes
Thanks for the detailed comments, and the solution. I'd implemented it this way because I wanted the results URLs to look like /search/query (I know there are problems with this, though, and may well revise it). I do redirect after the POST, so there is no problem with pressing the Back button.
AP257
+2  A: 

Django forms are not particularly helpful for your use case. Also, for a search page, it's much better to use a GET form and maintain state in the URL. The following code is much shorter, simpler and conforms far better to HTTP standards:

def search(request):
    q = request.GET.get('q','').strip()
    results = get_some_results(q)
    render_to_response("things/search.html", {'q': q, 'results': results})

The template:

<form method="GET" action=".">
<p><input type="text" value="{{ q }}" /> <input type="submit" value="Search" /></p>
{% if q %}
   {% if results %}
       Your results...
   {% else %}
       No results
   {% endif %}
{% endif %}
</form>
spookylukey
+1, indeed seems to fulfill his intentions better.
celopes