Merge branch 'master' into feature/api-cleanup
diff --git a/xos/api/examples/misc/add_slice.sh b/xos/api/examples/misc/add_slice.sh
new file mode 100755
index 0000000..b2d7adc
--- /dev/null
+++ b/xos/api/examples/misc/add_slice.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+source ./config.sh
+
+SITE_ID=1
+USER_ID=1
+
+DATA=$(cat <<EOF
+{"name": "mysite_test1",
+ "site": $SITE_ID,
+ "creator": $USER_ID
+}
+EOF
+)
+
+curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH -X POST -d "$DATA" $HOST/xos/slices/?no_hyperlinks=1
diff --git a/xos/api/examples/misc/config.sh b/xos/api/examples/misc/config.sh
new file mode 100644
index 0000000..92d703c
--- /dev/null
+++ b/xos/api/examples/misc/config.sh
@@ -0,0 +1,2 @@
+# see config.sh in the parent directory
+source ../config.sh
diff --git a/xos/api/examples/misc/delete_slice.sh b/xos/api/examples/misc/delete_slice.sh
new file mode 100755
index 0000000..1113d4f
--- /dev/null
+++ b/xos/api/examples/misc/delete_slice.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+source ./config.sh
+source ./util.sh
+
+SLICE_NAME=mysite_test1
+
+SLICE_ID=$(lookup_slice_id $SLICE_NAME)
+if [[ $? != 0 ]]; then
+    exit -1
+fi
+
+curl -u $AUTH -X DELETE $HOST/xos/slices/$SLICE_ID/
diff --git a/xos/api/examples/misc/update_slice.sh b/xos/api/examples/misc/update_slice.sh
new file mode 100755
index 0000000..94e82cd
--- /dev/null
+++ b/xos/api/examples/misc/update_slice.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source ./config.sh
+source ./util.sh
+
+SLICE_NAME=mysite_test1
+
+SLICE_ID=$(lookup_slice_id $SLICE_NAME)
+if [[ $? != 0 ]]; then
+    exit -1
+fi
+
+DATA=$(cat <<EOF
+{"description": "foo"}
+EOF
+)
+
+curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH -X PATCH -d "$DATA" $HOST/xos/slices/$SLICE_ID/
diff --git a/xos/api/examples/misc/util.sh b/xos/api/examples/misc/util.sh
new file mode 100644
index 0000000..7b66903
--- /dev/null
+++ b/xos/api/examples/misc/util.sh
@@ -0,0 +1 @@
+source ../util.sh
diff --git a/xos/api/examples/util.sh b/xos/api/examples/util.sh
index 648b32c..73cc039 100644
--- a/xos/api/examples/util.sh
+++ b/xos/api/examples/util.sh
@@ -12,6 +12,19 @@
     echo $ID
 }
 
+function lookup_slice_id {
+    JSON=`curl -f -s -u $AUTH -X GET $HOST/xos/slices/?name=$1`
+    if [[ $? != 0 ]]; then
+        echo "function lookup_slice_id with arguments $1 failed" >&2
+        echo "See CURL output below:" >&2
+        curl -s -u $AUTH -X GET $HOST/xos/slices/?name=$1 >&2
+        exit -1
+    fi
+    ID=`echo $JSON | python -c "import json,sys; print json.load(sys.stdin)[0].get('id','')"`
+    #echo "(mapped slice_name to id $ID)" >&2
+    echo $ID
+}
+
 function lookup_subscriber_volt {
     JSON=`curl -f -s -u $AUTH -X GET $HOST/api/tenant/cord/subscriber/$1/`
     if [[ $? != 0 ]]; then
diff --git a/xos/tools/apigen/api.template.py b/xos/tools/apigen/api.template.py
index de733e8..d0852db 100644
--- a/xos/tools/apigen/api.template.py
+++ b/xos/tools/apigen/api.template.py
@@ -68,7 +68,8 @@
 # Based on serializers.py
 
 class XOSModelSerializer(serializers.ModelSerializer):
-    def save_object(self, obj, **kwargs):
+    # TODO: Rest Framework 3.x doesn't support save_object()
+    def NEED_TO_UPDATE_save_object(self, obj, **kwargs):
 
         """ rest_framework can't deal with ManyToMany relations that have a
             through table. In xos, most of the through tables we have
diff --git a/xos/xos/apibase.py b/xos/xos/apibase.py
index 03493ee..addbbe9 100644
--- a/xos/xos/apibase.py
+++ b/xos/xos/apibase.py
@@ -15,32 +15,27 @@
 
     def update(self, request, *args, **kwargs):
         partial = kwargs.pop('partial', False)
-        self.object = self.get_object_or_none()
+        self.object = self.get_object()
 
         if self.object is None:
             raise XOSProgrammingError("Use the List API for creating objects")
 
-        serializer = self.get_serializer(self.object, data=request.DATA,
-                                         files=request.FILES, partial=partial)
-
-        assert(serializer.object is not None)
-
-        serializer.object.caller = request.user
+        serializer = self.get_serializer(self.object, data=request.data, partial=partial)
 
         if not serializer.is_valid():
             raise XOSValidationError(fields=serializer._errors)
 
-        if not serializer.object.can_update(request.user):
+        # Do the XOS perm check
+
+        assert(serializer.instance is not None)
+        obj = serializer.instance
+        for attr, value in serializer.validated_data.items():
+            setattr(obj, attr, value)
+        obj.caller = request.user
+        if not obj.can_update(request.user):
             raise XOSPermissionDenied()
 
-        if (hasattr(self, "pre_save")):
-            # rest_framework 2.x
-            self.pre_save(serializer.object)
-            self.object = serializer.save(force_update=True)
-            self.post_save(self.object, created=False)
-        else:
-            # rest_framework 3.x
-            self.perform_update(serializer)
+        self.perform_update(serializer)
 
         return Response(serializer.data, status=status.HTTP_200_OK)
 
@@ -48,11 +43,7 @@
         obj = self.get_object()
         obj.caller = request.user
         if obj.can_update(request.user):
-            # this is the guts of DestroyModelMixin, copied here so that we
-            # can use the obj with caller set in it,
-            self.pre_delete(obj)
-            obj.delete()
-            self.post_delete(obj)
+            self.perform_destroy(obj)
             return Response(status=status.HTTP_204_NO_CONTENT)
         else:
             return Response(status=status.HTTP_400_BAD_REQUEST)
@@ -69,7 +60,7 @@
 
 class XOSListCreateAPIView(generics.ListCreateAPIView):
     def create(self, request, *args, **kwargs):
-        serializer = self.get_serializer(data=request.DATA, files=request.FILES)
+        serializer = self.get_serializer(data=request.data)
 
         # In rest_framework 3.x: we can pass raise_exception=True instead of
         # raising the exception ourselves
@@ -77,22 +68,12 @@
             raise XOSValidationError(fields=serializer._errors)
 
         # now do XOS can_update permission checking
-
-        obj = serializer.object
+        obj = serializer.Meta.model(**serializer.validated_data)
         obj.caller = request.user
         if not obj.can_update(request.user):
             raise XOSPermissionDenied()
 
-        # stuff below is from generics.ListCreateAPIView
-
-        if (hasattr(self, "pre_save")):
-            # rest_framework 2.x
-            self.pre_save(serializer.object)
-            self.object = serializer.save(force_insert=True)
-            self.post_save(self.object, created=True)
-        else:
-            # rest_framework 3.x
-            self.perform_create(serializer)
+        self.perform_create(serializer)
 
         headers = self.get_success_headers(serializer.data)
         return Response(serializer.data, status=status.HTTP_201_CREATED,
diff --git a/xos/xos/hpcapi.py b/xos/xos/hpcapi.py
index d444684..5b97ab9 100644
--- a/xos/xos/hpcapi.py
+++ b/xos/xos/hpcapi.py
@@ -123,7 +123,8 @@
 # Based on serializers.py
 
 class XOSModelSerializer(serializers.ModelSerializer):
-    def save_object(self, obj, **kwargs):
+    # TODO: Rest Framework 3.x doesn't support save_object()
+    def NEED_TO_UPDATE_save_object(self, obj, **kwargs):
 
         """ rest_framework can't deal with ManyToMany relations that have a
             through table. In xos, most of the through tables we have
diff --git a/xos/xos/xosapi.py b/xos/xos/xosapi.py
index d0a9646..7673f28 100644
--- a/xos/xos/xosapi.py
+++ b/xos/xos/xosapi.py
@@ -605,7 +605,8 @@
 # Based on serializers.py
 
 class XOSModelSerializer(serializers.ModelSerializer):
-    def save_object(self, obj, **kwargs):
+    # TODO: Rest Framework 3.x doesn't support save_object()
+    def NEED_TO_UPDATE_save_object(self, obj, **kwargs):
 
         """ rest_framework can't deal with ManyToMany relations that have a
             through table. In xos, most of the through tables we have