Repository: carpentries/amy
Issue: Admin commands broken

Please check all the admin commands (use `make` on its own to get a list):

```
$ make airports

/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/site-packages/crispy_forms/utils.py:25: RemovedInDjango19Warning: memoize wrapper is deprecated and will be removed in Django 1.9. Use django.utils.lru_cache instead.
  default_field_template = memoize(default_field_template, {}, 1)

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/site-packages/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/site-packages/django/core/management/__init__.py", line 190, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/site-packages/django/core/management/__init__.py", line 40, in load_command_class
    module = import_module('%s.management.commands.%s' % (app_name, name))
  File "/Users/gvwilson/anaconda/envs/py3k/lib/python3.3/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1582, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1563, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1530, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 584, in _check_name_wrapper
  File "<frozen importlib._bootstrap>", line 1022, in load_module
  File "<frozen importlib._bootstrap>", line 1003, in load_module
  File "<frozen importlib._bootstrap>", line 560, in module_for_loader_wrapper
  File "<frozen importlib._bootstrap>", line 868, in _load_module
  File "<frozen importlib._bootstrap>", line 313, in _call_with_frames_removed
  File "/Users/gvwilson/s/amy/workshops/management/commands/export_airports.py", line 3, in <module>
    from workshops.views import _export_instructors
ImportError: cannot import name _export_instructors
make: *** [airports] Error 1
```


Changed files:
workshops/management/commands/export_airports.py, api/test/test_events.py, api/test/test_export.py, workshops/management/commands/export_badges.py, api/views.py

diff --git a/api/test/test_events.py b/api/test/test_events.py
index d4381c3c..f386b32d 100644
--- a/api/test/test_events.py
+++ b/api/test/test_events.py
@@ -17,6 +17,7 @@ from workshops.models import (
 
 class TestListingPastEvents(APITestCase):
     view = PublishedEvents
+    serializer_class = EventSerializer
     url = 'api:events-published'
     maxDiff = None
 
@@ -87,7 +88,7 @@ class TestListingPastEvents(APITestCase):
                 'slug': 'event2',
                 'start': self.event2.start,
                 'end': self.event2.end,
-                'humandate': EventSerializer.human_readable_date(
+                'humandate': self.serializer_class.human_readable_date(
                     self.event2.start, self.event2.end
                 ),
                 'latitude': 3,
@@ -114,9 +115,7 @@ class TestListingPastEvents(APITestCase):
         ]
 
     def test_serialization(self):
-        # test calling the view directly, with "fake" request object (None)
-        view = self.view()
-        response = view.get(None)
+        response = self.serializer_class(self.view().get_queryset(), many=True)
         self.assertEqual(response.data, self.expecting)
 
     def test_view(self):
diff --git a/api/test/test_export.py b/api/test/test_export.py
index 34839e2a..91b39b23 100644
--- a/api/test/test_export.py
+++ b/api/test/test_export.py
@@ -9,6 +9,10 @@ from api.views import (
     ExportBadgesView,
     ExportInstructorLocationsView,
 )
+from api.serializers import (
+    ExportBadgesSerializer,
+    ExportInstructorLocationsSerializer,
+)
 from workshops.models import (
     Badge,
     Award,
@@ -49,9 +53,9 @@ class TestExportingBadges(APITestCase):
         ]
 
     def test_serialization(self):
-        # test calling the view directly, with "fake" request object (None)
         view = ExportBadgesView()
-        response = view.get(None)
+        serializer = view.get_serializer_class()
+        response = serializer(view.get_queryset(), many=True)
         self.assertEqual(response.data, self.expecting)
 
     def test_view(self):
@@ -104,9 +108,9 @@ class TestExportingInstructors(APITestCase):
         ]
 
     def test_serialization(self):
-        # test calling the view directly, with "fake" request object (None)
         view = ExportInstructorLocationsView()
-        response = view.get(None)
+        serializer = view.get_serializer_class()
+        response = serializer(view.get_queryset(), many=True)
         self.assertEqual(response.data, self.expecting)
 
     def test_view(self):
diff --git a/api/views.py b/api/views.py
index 488dd6b7..2a1b5755 100644
--- a/api/views.py
+++ b/api/views.py
@@ -26,45 +26,31 @@ class ApiRoot(APIView):
         })
 
 
-class ExportBadgesView(APIView):
+class ExportBadgesView(ListAPIView):
     """List all badges and people who have them."""
     permission_classes = (IsAuthenticatedOrReadOnly, )
+    paginator = None  # disable pagination
 
-    def get(self, request, format=None):
-        badges = Badge.objects.prefetch_related('person_set')
-        serializer = ExportBadgesSerializer(badges, many=True)
-        return Response(serializer.data)
+    queryset = Badge.objects.prefetch_related('person_set')
+    serializer_class = ExportBadgesSerializer
 
 
-class ExportInstructorLocationsView(APIView):
+class ExportInstructorLocationsView(ListAPIView):
     """List all airports and instructors located near them."""
     permission_classes = (IsAuthenticatedOrReadOnly, )
+    paginator = None  # disable pagination
 
-    def get(self, request, format=None):
-        # TODO: return only people marked as instructors?
-        airports = Airport.objects.exclude(person=None) \\
-                                  .prefetch_related('person_set')
-        serializer = ExportInstructorLocationsSerializer(airports, many=True)
-        return Response(serializer.data)
+    queryset = Airport.objects.exclude(person=None) \\
+                              .prefetch_related('person_set')
+    serializer_class = ExportInstructorLocationsSerializer
 
 
-class ListEvents(APIView):
-    # I wanted to use ListAPIView, but it had problems with the way we test
-    # this code... Basically ListAPIView uses pagination, and pagination
-    # requires existing Request object - something we're faking in part of the
-    # tests (request = None).
-    serializer_class = EventSerializer
+class PublishedEvents(ListAPIView):
+    # only events that have both a starting date and a URL
     permission_classes = (IsAuthenticatedOrReadOnly, )
-    queryset = None  # override this in the subclass
+    paginator = None  # disable pagination
 
-    def get(self, request, format=None):
-        objects = self.queryset.all()
-        serializer = self.serializer_class(objects, many=True)
-        return Response(serializer.data)
-
-
-class PublishedEvents(ListEvents):
-    # only events that have both a starting date and a URL
+    serializer_class = EventSerializer
     queryset = Event.objects.exclude(
         Q(start__isnull=True) | Q(url__isnull=True)
     ).order_by('-start')
diff --git a/workshops/management/commands/export_airports.py b/workshops/management/commands/export_airports.py
index 130507b1..3d24a27c 100644
--- a/workshops/management/commands/export_airports.py
+++ b/workshops/management/commands/export_airports.py
@@ -1,10 +1,14 @@
-import yaml
-from django.core.management.base import BaseCommand, CommandError
-from workshops.views import _export_instructors
+from django.core.management.base import BaseCommand
+from django.core.urlresolvers import reverse
+from rest_framework.test import APIClient
+
 
 class Command(BaseCommand):
     args = 'no arguments'
     help = 'Display YAML for airports.'
 
     def handle(self, *args, **options):
-        print(yaml.dump(_export_instructors()).rstrip())
+        client = APIClient()
+        response = client.get(reverse('api:export-instructors'),
+                              {'format': 'yaml'})
+        print(response.content.decode('utf-8'))
diff --git a/workshops/management/commands/export_badges.py b/workshops/management/commands/export_badges.py
index f3f99377..f25c77e3 100644
--- a/workshops/management/commands/export_badges.py
+++ b/workshops/management/commands/export_badges.py
@@ -1,10 +1,14 @@
-import yaml
-from django.core.management.base import BaseCommand, CommandError
-from workshops.views import _export_badges
+from django.core.management.base import BaseCommand
+from django.core.urlresolvers import reverse
+from rest_framework.test import APIClient
+
 
 class Command(BaseCommand):
     args = 'no arguments'
     help = 'Display YAML for badges.'
 
     def handle(self, *args, **options):
-        print(yaml.dump(_export_badges()).rstrip())
+        client = APIClient()
+        response = client.get(reverse('api:export-badges'),
+                              {'format': 'yaml'})
+        print(response.content.decode('utf-8'))