Repository: carpentries/amy

Issue:
Server error when looking up nonexistent person
I just merged levy.michael.2 into levy.michael - now when I go to https://amy.software-carpentry.org/workshops/person/4678 (which used to be levy.michael.2) I get a 500 error and this report:

```
Internal Server Error: /workshops/person/4678
Traceback (most recent call last):
  File "/home/amy/amy_site/venv/lib/python3.4/site-packages/django/core/handlers/base.py", line 132, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/amy/amy_site/venv/lib/python3.4/site-packages/django/contrib/auth/decorators.py", line 22, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "./workshops/views.py", line 376, in person_details
    person = Person.objects.get(id=person_id)
  File "/home/amy/amy_site/venv/lib/python3.4/site-packages/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/amy/amy_site/venv/lib/python3.4/site-packages/django/db/models/query.py", line 334, in get
    self.model._meta.object_name
workshops.models.DoesNotExist: Person matching query does not exist.
```



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -312,7 +312,7 @@ def all_airports(request):
 @login_required
 def airport_details(request, airport_iata):
     '''List details of a particular airport.'''
-    airport = Airport.objects.get(iata=airport_iata)
+    airport = get_object_or_404(Airport, iata=airport_iata)
     context = {'title' : 'Airport {0}'.format(airport),
                'airport' : airport}
     return render(request, 'workshops/airport.html', context)
@@ -373,7 +373,7 @@ def all_persons(request):
 @login_required
 def person_details(request, person_id):
     '''List details of a particular person.'''
-    person = Person.objects.get(id=person_id)
+    person = get_object_or_404(Person, id=person_id)
     awards = person.award_set.all()
     tasks = person.task_set.all()
     lessons = person.lessons.all()
@@ -583,12 +583,9 @@ def form_valid(self, form):
                       'workshops.add_task'],
                      raise_exception=True)
 def person_edit(request, person_id):
-    try:
-        person = Person.objects.get(pk=person_id)
-        awards = person.award_set.order_by('badge__name')
-        tasks = person.task_set.order_by('-event__slug')
-    except ObjectDoesNotExist:
-        raise Http404("No person found matching the query.")
+    person = get_object_or_404(Person, id=person_id)
+    awards = person.award_set.order_by('badge__name')
+    tasks = person.task_set.order_by('-event__slug')
 
     person_form = PersonForm(prefix='person', instance=person)
     award_form = PersonAwardForm(prefix='award', initial={
@@ -834,8 +831,10 @@ def all_events(request):
 @login_required
 def event_details(request, event_ident):
     '''List details of a particular event.'''
-
-    event = Event.get_by_ident(event_ident)
+    try:
+        event = Event.get_by_ident(event_ident)
+    except Event.DoesNotExist:
+        raise Http404('Event matching query does not exist.')
     tasks = Task.objects.filter(event__id=event.id).order_by('role__name')
     todos = event.todoitem_set.all()
     todo_form = SimpleTodoForm(prefix='todo', initial={
@@ -889,7 +888,10 @@ def event_details(request, event_ident):
 @login_required
 def validate_event(request, event_ident):
     '''Check the event's home page *or* the specified URL (for testing).'''
-    event = Event.get_by_ident(event_ident)
+    try:
+        event = Event.get_by_ident(event_ident)
+    except Event.DoesNotExist:
+        raise Http404('Event matching query does not exist.')
 
     page_url = request.GET.get('url', None)  # for manual override
     if page_url is None:
@@ -1143,7 +1145,7 @@ def all_tasks(request):
 @login_required
 def task_details(request, task_id):
     '''List details of a particular task.'''
-    task = Task.objects.get(pk=task_id)
+    task = get_object_or_404(Task, pk=task_id)
     context = {'title' : 'Task {0}'.format(task),
                'task' : task}
     return render(request, 'workshops/task.html', context)
@@ -1216,7 +1218,7 @@ def all_badges(request):
 def badge_details(request, badge_name):
     '''List details of a particular event.'''
 
-    badge = Badge.objects.get(name=badge_name)
+    badge = get_object_or_404(Badge, name=badge_name)
 
     initial = {
         'badge': badge,
@@ -1887,16 +1889,9 @@ def eventrequest_accept(request, request_id):
 def eventrequest_assign(request, request_id, person_id=None):
     """Set eventrequest.assigned_to. See `_assign` docstring for more
     information."""
-
-    try:
-        event_req = EventRequest.objects.get(pk=request_id)
-
-        _assign(request, event_req, person_id)
-
-        return redirect(reverse('eventrequest_details', args=[event_req.pk]))
-
-    except Event.DoesNotExist:
-        raise Http404("No event request found matching the query.")
+    event_req = get_object_or_404(EventRequest, pk=request_id)
+    _assign(request, event_req, person_id)
+    return redirect(reverse('eventrequest_details', args=[event_req.pk]))
 
 
 def profileupdaterequest_create(request):
@@ -2098,7 +2093,10 @@ def profileupdaterequest_accept(request, request_id, person_id):
 @permission_required('workshops.add_todoitem', raise_exception=True)
 def todos_add(request, event_ident):
     """Add a standard TodoItems for a specific event."""
-    event = Event.get_by_ident(event_ident)
+    try:
+        event = Event.get_by_ident(event_ident)
+    except Event.DoesNotExist:
+        raise Http404('Event matching query does not exist.')
 
     dt = datetime.datetime
     timedelta = datetime.timedelta


Test patch:
diff --git a/workshops/test/test_views_404.py b/workshops/test/test_views_404.py
new file mode 100644
--- /dev/null
+++ b/workshops/test/test_views_404.py
@@ -0,0 +1,55 @@
+from django.core.urlresolvers import reverse
+
+from .base import TestBase
+
+
+class TestViewsFor404ing(TestBase):
+    """Make sure specific tests return 404 instead of 500."""
+
+    def setUp(self):
+        super()._setUpUsersAndLogin()
+
+    def test_airport_details(self):
+        url = reverse('airport_details', args=['ASD'])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_person_details(self):
+        url = reverse('person_details', args=[404])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_person_edit(self):
+        url = reverse('person_edit', args=[404])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_event_details(self):
+        url = reverse('event_details', args=['non-existing-event'])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_validate_event(self):
+        url = reverse('validate_event', args=['non-existing-event'])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_task_details(self):
+        url = reverse('task_details', args=[404])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_badge_details(self):
+        url = reverse('badge_details', args=['non-existing-badge'])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def test_eventrequest_assign(self):
+        url = reverse('eventrequest_assign', args=[404, 404])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)
+
+    def todos_add(self):
+        url = reverse('todos_add', args=['non-existing-event'])
+        rv = self.client.get(url)
+        self.assertEqual(rv.status_code, 404)