diff --git a/django/contrib/gis/db/backends/oracle/adapter.py b/django/contrib/gis/db/backends/oracle/adapter.py
index 60961af817..11eb0424aa 100644
--- a/django/contrib/gis/db/backends/oracle/adapter.py
+++ b/django/contrib/gis/db/backends/oracle/adapter.py
@@ -1,7 +1,54 @@
 from cx_Oracle import CLOB
 
 from django.contrib.gis.db.backends.base.adapter import WKTAdapter
+from django.contrib.gis.geos import GeometryCollection, Polygon
+from django.utils.six.moves import range
 
 
 class OracleSpatialAdapter(WKTAdapter):
     input_size = CLOB
+
+    def __init__(self, geom):
+        """
+        Oracle requires that polygon rings are in proper orientation. This
+        affects spatial operations and an invalid orientation may cause
+        failures. Correct orientations are:
+         * Outer ring - counter clockwise
+         * Inner ring(s) - clockwise
+        """
+        if isinstance(geom, Polygon):
+            self._fix_polygon(geom)
+        elif isinstance(geom, GeometryCollection):
+            self._fix_geometry_collection(geom)
+
+        self.wkt = geom.wkt
+        self.srid = geom.srid
+
+    def _fix_polygon(self, poly):
+        # Fix single polygon orientation as described in __init__()
+        if self._isClockwise(poly.exterior_ring):
+            poly.exterior_ring = list(reversed(poly.exterior_ring))
+
+        for i in range(1, len(poly)):
+            if not self._isClockwise(poly[i]):
+                poly[i] = list(reversed(poly[i]))
+
+        return poly
+
+    def _fix_geometry_collection(self, coll):
+        # Fix polygon orientations in geometry collections as described in
+        # __init__()
+        for i, geom in enumerate(coll):
+            if isinstance(geom, Polygon):
+                coll[i] = self._fix_polygon(geom)
+
+    def _isClockwise(self, coords):
+        # A modified shoelace algorithm to determine polygon orientation.
+        # See https://en.wikipedia.org/wiki/Shoelace_formula
+        n = len(coords)
+        area = 0.0
+        for i in range(n):
+            j = (i + 1) % n
+            area += coords[i][0] * coords[j][1]
+            area -= coords[j][0] * coords[i][1]
+        return area < 0.0
diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
index 8709202cd2..30e87e63a1 100644
--- a/django/contrib/gis/db/backends/oracle/operations.py
+++ b/django/contrib/gis/db/backends/oracle/operations.py
@@ -70,7 +70,6 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
     extent = 'SDO_AGGR_MBR'
     intersection = 'SDO_GEOM.SDO_INTERSECTION'
     length = 'SDO_GEOM.SDO_LENGTH'
-    num_geom = 'SDO_UTIL.GETNUMELEM'
     num_points = 'SDO_UTIL.GETNUMVERTICES'
     perimeter = length
     point_on_surface = 'SDO_GEOM.SDO_POINTONSURFACE'
@@ -80,6 +79,23 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
     union = 'SDO_GEOM.SDO_UNION'
     unionagg = 'SDO_AGGR_UNION'
 
+    function_names = {
+        'Area': 'SDO_GEOM.SDO_AREA',
+        'Centroid': 'SDO_GEOM.SDO_CENTROID',
+        'Difference': 'SDO_GEOM.SDO_DIFFERENCE',
+        'Distance': 'SDO_GEOM.SDO_DISTANCE',
+        'Intersection': 'SDO_GEOM.SDO_INTERSECTION',
+        'Length': 'SDO_GEOM.SDO_LENGTH',
+        'NumGeometries': 'SDO_UTIL.GETNUMELEM',
+        'NumPoints': 'SDO_UTIL.GETNUMVERTICES',
+        'Perimeter': 'SDO_GEOM.SDO_LENGTH',
+        'PointOnSurface': 'SDO_GEOM.SDO_POINTONSURFACE',
+        'Reverse': 'SDO_UTIL.REVERSE_LINESTRING',
+        'SymDifference': 'SDO_GEOM.SDO_XOR',
+        'Transform': 'SDO_CS.TRANSFORM',
+        'Union': 'SDO_GEOM.SDO_UNION',
+    }
+
     # We want to get SDO Geometries as WKT because it is much easier to
     # instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.
     # However, this adversely affects performance (i.e., Java is called
@@ -109,6 +125,13 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
 
     truncate_params = {'relate': None}
 
+    unsupported_functions = {
+        'AsGeoHash', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG',
+        'BoundingCircle', 'Envelope',
+        'ForceRHR', 'MemSize', 'Scale',
+        'SnapToGrid', 'Translate', 'GeoHash',
+    }
+
     def geo_quote_name(self, name):
         return super(OracleOperations, self).geo_quote_name(name).upper()
 
diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py
index 792edac929..55324a8eaa 100644
--- a/django/contrib/gis/db/models/functions.py
+++ b/django/contrib/gis/db/models/functions.py
@@ -85,6 +85,9 @@ class GeomValue(Value):
     def as_sqlite(self, compiler, connection):
         return 'GeomFromText(%%s, %s)' % self.srid, [connection.ops.Adapter(self.value)]
 
+    def as_oracle(self, compiler, connection):
+        return 'SDO_GEOMETRY(%%s, %s)' % self.srid, [connection.ops.Adapter(self.value)]
+
 
 class GeoFuncWithGeoParam(GeoFunc):
     def __init__(self, expression, geom, *expressions, **extra):
@@ -112,27 +115,38 @@ class SQLiteDecimalToFloatMixin(object):
         return super(SQLiteDecimalToFloatMixin, self).as_sql(compiler, connection)
 
 
-class Area(GeoFunc):
+class OracleToleranceMixin(object):
+    tolerance = 0.05
+
+    def as_oracle(self, compiler, connection):
+        tol = self.extra.get('tolerance', self.tolerance)
+        self.template = "%%(function)s(%%(expressions)s, %s)" % tol
+        return super(OracleToleranceMixin, self).as_sql(compiler, connection)
+
+
+class Area(OracleToleranceMixin, GeoFunc):
     def as_sql(self, compiler, connection):
-        if connection.ops.oracle:
-            self.output_field = AreaField('sq_m')  # Oracle returns area in units of meters.
-        else:
-            if connection.ops.geography:
-                # Geography fields support area calculation, returns square meters.
-                self.output_field = AreaField('sq_m')
-            elif not self.output_field.geodetic(connection):
-                # Getting the area units of the geographic field.
-                units = self.output_field.units_name(connection)
-                if units:
-                    self.output_field = AreaField(
-                        AreaMeasure.unit_attname(self.output_field.units_name(connection)))
-                else:
-                    self.output_field = FloatField()
+        if connection.ops.geography:
+            # Geography fields support area calculation, returns square meters.
+            self.output_field = AreaField('sq_m')
+        elif not self.output_field.geodetic(connection):
+            # Getting the area units of the geographic field.
+            units = self.output_field.units_name(connection)
+            if units:
+                self.output_field = AreaField(
+                    AreaMeasure.unit_attname(self.output_field.units_name(connection))
+                )
             else:
-                # TODO: Do we want to support raw number areas for geodetic fields?
-                raise NotImplementedError('Area on geodetic coordinate systems not supported.')
+                self.output_field = FloatField()
+        else:
+            # TODO: Do we want to support raw number areas for geodetic fields?
+            raise NotImplementedError('Area on geodetic coordinate systems not supported.')
         return super(Area, self).as_sql(compiler, connection)
 
+    def as_oracle(self, compiler, connection):
+        self.output_field = AreaField('sq_m')  # Oracle returns area in units of meters.
+        return super(Area, self).as_oracle(compiler, connection)
+
 
 class AsGeoJSON(GeoFunc):
     output_field_class = TextField
@@ -189,11 +203,11 @@ class BoundingCircle(GeoFunc):
         super(BoundingCircle, self).__init__(*[expression, num_seg], **extra)
 
 
-class Centroid(GeoFunc):
+class Centroid(OracleToleranceMixin, GeoFunc):
     pass
 
 
-class Difference(GeoFuncWithGeoParam):
+class Difference(OracleToleranceMixin, GeoFuncWithGeoParam):
     pass
 
 
@@ -215,7 +229,7 @@ class DistanceResultMixin(object):
         return value
 
 
-class Distance(DistanceResultMixin, GeoFuncWithGeoParam):
+class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFuncWithGeoParam):
     output_field_class = FloatField
     spheroid = None
 
@@ -246,6 +260,11 @@ class Distance(DistanceResultMixin, GeoFuncWithGeoParam):
                 self.function = 'ST_Distance_Sphere'
         return super(Distance, self).as_sql(compiler, connection)
 
+    def as_oracle(self, compiler, connection):
+        if self.spheroid:
+            self.source_expressions.pop(2)
+        return super(Distance, self).as_oracle(compiler, connection)
+
 
 class Envelope(GeoFunc):
     pass
@@ -265,11 +284,11 @@ class GeoHash(GeoFunc):
         super(GeoHash, self).__init__(*expressions, **extra)
 
 
-class Intersection(GeoFuncWithGeoParam):
+class Intersection(OracleToleranceMixin, GeoFuncWithGeoParam):
     pass
 
 
-class Length(DistanceResultMixin, GeoFunc):
+class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
     output_field_class = FloatField
 
     def __init__(self, expr1, spheroid=True, **extra):
@@ -325,7 +344,7 @@ class NumPoints(GeoFunc):
         return super(NumPoints, self).as_sql(compiler, connection)
 
 
-class Perimeter(DistanceResultMixin, GeoFunc):
+class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
     output_field_class = FloatField
 
     def as_postgresql(self, compiler, connection):
@@ -335,7 +354,7 @@ class Perimeter(DistanceResultMixin, GeoFunc):
         return super(Perimeter, self).as_sql(compiler, connection)
 
 
-class PointOnSurface(GeoFunc):
+class PointOnSurface(OracleToleranceMixin, GeoFunc):
     pass
 
 
@@ -376,7 +395,7 @@ class SnapToGrid(SQLiteDecimalToFloatMixin, GeoFunc):
         super(SnapToGrid, self).__init__(*expressions, **extra)
 
 
-class SymDifference(GeoFuncWithGeoParam):
+class SymDifference(OracleToleranceMixin, GeoFuncWithGeoParam):
     pass
 
 
@@ -412,5 +431,5 @@ class Translate(Scale):
         return super(Translate, self).as_sqlite(compiler, connection)
 
 
-class Union(GeoFuncWithGeoParam):
+class Union(OracleToleranceMixin, GeoFuncWithGeoParam):
     pass
diff --git a/django/contrib/gis/db/models/sql/conversion.py b/django/contrib/gis/db/models/sql/conversion.py
index 28e601613e..dbbdb8b338 100644
--- a/django/contrib/gis/db/models/sql/conversion.py
+++ b/django/contrib/gis/db/models/sql/conversion.py
@@ -2,6 +2,7 @@
 This module holds simple classes to convert geospatial values from the
 database.
 """
+from __future__ import unicode_literals
 
 from django.contrib.gis.db.models.fields import GeoSelectFormatMixin
 from django.contrib.gis.geometry.backend import Geometry
@@ -24,6 +25,8 @@ class AreaField(BaseField):
         self.area_att = area_att
 
     def from_db_value(self, value, expression, connection, context):
+        if connection.features.interprets_empty_strings_as_nulls and value == '':
+            value = None
         if value is not None:
             value = Area(**{self.area_att: value})
         return value
diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py
index a819c17ada..d6ca4f5010 100644
--- a/tests/gis_tests/geoapp/models.py
+++ b/tests/gis_tests/geoapp/models.py
@@ -61,6 +61,14 @@ class MultiFields(NamedModel):
     point = models.PointField()
     poly = models.PolygonField()
 
+    class Meta:
+        required_db_features = ['gis_enabled']
+
+
+class UniqueTogetherModel(models.Model):
+    city = models.CharField(max_length=30)
+    point = models.PointField()
+
     class Meta:
         unique_together = ('city', 'point')
         required_db_features = ['gis_enabled', 'supports_geometry_field_unique_index']
diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py
index 64af08aa77..0d6ee0362f 100644
--- a/tests/gis_tests/geoapp/tests.py
+++ b/tests/gis_tests/geoapp/tests.py
@@ -615,7 +615,7 @@ class GeoQuerySetTest(TestCase):
         if oracle:
             # No precision parameter for Oracle :-/
             gml_regex = re.compile(
-                r'^<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml">'
+                r'^<gml:Point srsName="EPSG:4326" xmlns:gml="http://www.opengis.net/gml">'
                 r'<gml:coordinates decimal="\." cs="," ts=" ">-104.60925\d+,38.25500\d+ '
                 r'</gml:coordinates></gml:Point>'
             )