Repository: carpentries/amy

Issue:
Search form should use GET method
Benefits:
- the browser won't ask you to confirm form resubmission when refreshing the search result page
- search result page is stateless -- the URL includes the term you search for, so that you can send the link to other people


Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -32,6 +32,7 @@
 from django.http import Http404, HttpResponse, JsonResponse
 from django.http import HttpResponseBadRequest
 from django.shortcuts import redirect, render, get_object_or_404
+from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import ListView
 from django.views.generic.edit import (
     ModelFormMixin,
@@ -1825,6 +1826,7 @@ def workshop_staff(request):
 #------------------------------------------------------------
 
 
+@csrf_exempt
 @admin_required
 def search(request):
     '''Search the database by term.'''
@@ -1832,8 +1834,8 @@ def search(request):
     term = ''
     organizations = events = persons = airports = training_requests = None
 
-    if request.method == 'POST':
-        form = SearchForm(request.POST)
+    if request.method == 'GET':
+        form = SearchForm(request.GET)
         if form.is_valid():
             term = form.cleaned_data['term']
             tokens = re.split('\s+', term)
@@ -1903,7 +1905,7 @@ def search(request):
             if len(results) == 1:
                 return redirect(results[0].get_absolute_url())
 
-    # if a GET (or any other method) we'll create a blank form
+    # if empty GET, we'll create a blank form
     else:
         form = SearchForm()
 


Test patch:
diff --git a/workshops/test/test_search.py b/workshops/test/test_search.py
--- a/workshops/test/test_search.py
+++ b/workshops/test/test_search.py
@@ -11,44 +11,44 @@ def setUp(self):
         self._setUpUsersAndLogin()
 
     def test_search_for_organization_with_no_matches(self):
-        response = self.client.post(reverse('search'),
-                                    {'term' : 'non.existent',
-                                     'in_organizations' : 'on'})
+        response = self.client.get(reverse('search'),
+                                   {'term' : 'non.existent',
+                                    'in_organizations' : 'on'})
         doc = self._check_status_code_and_parse(response, 200)
         self._check_0(doc, ".//a[@class='searchresult']",
                       'Expected no search results')
 
     def test_search_for_organization_when_host_matching_turned_off(self):
-        response = self.client.post(reverse('search'),
-                                    {'term' : 'Alpha'})
+        response = self.client.get(reverse('search'),
+                                   {'term' : 'Alpha'})
         doc = self._check_status_code_and_parse(response, 200)
         node = self._check_0(doc, ".//a[@class='searchresult']",
                              'Expected no search results')
 
     def test_search_for_organization_by_partial_name(self):
-        response = self.client.post(reverse('search'),
-                                    {'term' : 'Alpha',
-                                     'in_organizations' : 'on'},
-                                    follow=True)
+        response = self.client.get(reverse('search'),
+                                   {'term' : 'Alpha',
+                                    'in_organizations' : 'on'},
+                                   follow=True)
         assert response.status_code == 200
         content = response.content.decode('utf-8')
         # no way for us to check the url…
         assert str(self.org_alpha.domain) in content
 
     def test_search_for_organization_by_full_domain(self):
-        response = self.client.post(reverse('search'),
-                                    {'term' : 'beta.com',
-                                     'in_organizations' : 'on'},
-                                    follow=True)
+        response = self.client.get(reverse('search'),
+                                   {'term' : 'beta.com',
+                                    'in_organizations' : 'on'},
+                                   follow=True)
         assert response.status_code == 200
         content = response.content.decode('utf-8')
         # no way for us to check the url…
         assert str(self.org_beta.domain) in content
 
     def test_search_for_organization_with_multiple_matches(self):
-        response = self.client.post(reverse('search'),
-                                    {'term' : 'a', # 'a' is in both 'alpha' and 'beta'
-                                     'in_organizations' : 'on'})
+        response = self.client.get(reverse('search'),
+                                   {'term' : 'a', # 'a' is in both 'alpha' and 'beta'
+                                    'in_organizations' : 'on'})
         doc = self._check_status_code_and_parse(response, 200)
         nodes = self._get_N(doc,  ".//a[@class='searchresult']",
                             'Expected three search results',
@@ -64,7 +64,7 @@ def test_search_for_people_by_personal_family_names(self):
         self.org_alpha.notes = 'Hermione Granger'
         self.org_alpha.save()
 
-        response = self.client.post(reverse('search'), {
+        response = self.client.get(reverse('search'), {
             'term': 'Hermione Granger',
             'in_organizations': 'on',
             'in_persons': 'on',
@@ -99,20 +99,20 @@ def test_search_for_training_requests(self):
         }
         url = reverse('search')
 
-        response = self.client.post(url, search_options)
+        response = self.client.get(url, search_options)
         self.assertEqual(len(response.context['training_requests']), 1)
 
         search_options['term'] = 'Krum'
-        response = self.client.post(url, search_options)
+        response = self.client.get(url, search_options)
         self.assertEqual(len(response.context['training_requests']), 1)
 
         search_options['term'] = 'Lorem'
-        response = self.client.post(url, search_options)
+        response = self.client.get(url, search_options)
         self.assertEqual(len(response.context['training_requests']), 1)
 
         search_options['term'] = 'Potter'
         # otherwise it'd redirect to Harry Potter's profile
         del search_options['in_persons']
-        response = self.client.post(url, search_options)
+        response = self.client.get(url, search_options)
         self.assertEqual(len(response.context['training_requests']), 0)