mirror of https://github.com/django/django.git
Replaced no_mysql by connection features
Refs #22632. Thanks Tim Graham for the review.
This commit is contained in:
parent
a7d964ab87
commit
ba1d707b0f
|
@ -2,6 +2,7 @@
|
|||
Base/mixin classes for the spatial backend database operations and the
|
||||
`<Backend>SpatialRefSys` model.
|
||||
"""
|
||||
from functools import partial
|
||||
import re
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
|
@ -15,42 +16,38 @@ class BaseSpatialFeatures(object):
|
|||
# Does the database contain a SpatialRefSys model to store SRID information?
|
||||
has_spatialrefsys_table = True
|
||||
|
||||
# Does the database support SRID transform operations?
|
||||
supports_transform = True
|
||||
# Do geometric relationship operations operate on real shapes (or only on bounding boxes)?
|
||||
supports_real_shape_operations = True
|
||||
# Can geometry fields be null?
|
||||
supports_null_geometries = True
|
||||
# Can the `distance` GeoQuerySet method be applied on geodetic coordinate systems?
|
||||
supports_distance_geodetic = True
|
||||
# Does the database supports `left` and `right` lookups?
|
||||
supports_left_right_lookups = False
|
||||
# Is the database able to count vertices on polygons (with `num_points`)?
|
||||
supports_num_points_poly = True
|
||||
|
||||
# The following properties indicate if the database GIS extensions support
|
||||
# certain methods (dwithin, force_rhr, geohash, ...)
|
||||
# The following properties indicate if the database backend support
|
||||
# certain lookups (dwithin, left and right, relate, ...)
|
||||
supports_left_right_lookups = False
|
||||
|
||||
@property
|
||||
def supports_relate_lookup(self):
|
||||
return 'relate' in self.connection.ops.geometry_functions
|
||||
|
||||
@property
|
||||
def has_dwithin_lookup(self):
|
||||
return 'dwithin' in self.connection.ops.distance_functions
|
||||
|
||||
@property
|
||||
def has_force_rhr_method(self):
|
||||
return bool(self.connection.ops.force_rhr)
|
||||
|
||||
@property
|
||||
def has_geohash_method(self):
|
||||
return bool(self.connection.ops.geohash)
|
||||
|
||||
@property
|
||||
def has_make_line_method(self):
|
||||
return bool(self.connection.ops.make_line)
|
||||
|
||||
@property
|
||||
def has_perimeter_method(self):
|
||||
return bool(self.connection.ops.perimeter)
|
||||
|
||||
@property
|
||||
def has_reverse_method(self):
|
||||
return bool(self.connection.ops.reverse)
|
||||
|
||||
@property
|
||||
def has_snap_to_grid_method(self):
|
||||
return bool(self.connection.ops.snap_to_grid)
|
||||
# For each of those methods, the class will have a property named
|
||||
# `has_<name>_method` (defined in __init__) which accesses connection.ops
|
||||
# to determine GIS method availability.
|
||||
geoqueryset_methods = (
|
||||
'centroid', 'difference', 'envelope', 'force_rhr', 'geohash', 'gml',
|
||||
'intersection', 'kml', 'num_geom', 'perimeter', 'point_on_surface',
|
||||
'reverse', 'scale', 'snap_to_grid', 'svg', 'sym_difference',
|
||||
'transform', 'translate', 'union', 'unionagg',
|
||||
)
|
||||
|
||||
# Specifies whether the Collect and Extent aggregates are supported by the database
|
||||
@property
|
||||
|
@ -61,6 +58,20 @@ class BaseSpatialFeatures(object):
|
|||
def supports_extent_aggr(self):
|
||||
return 'Extent' in self.connection.ops.valid_aggregates
|
||||
|
||||
@property
|
||||
def supports_make_line_aggr(self):
|
||||
return 'MakeLine' in self.connection.ops.valid_aggregates
|
||||
|
||||
def __init__(self, *args):
|
||||
super(BaseSpatialFeatures, self).__init__(*args)
|
||||
for method in self.geoqueryset_methods:
|
||||
# Add dynamically properties for each GQS method, e.g. has_force_rhr_method, etc.
|
||||
setattr(self.__class__, 'has_%s_method' % method,
|
||||
property(partial(BaseSpatialFeatures.has_ops_method, method=method)))
|
||||
|
||||
def has_ops_method(self, method):
|
||||
return getattr(self.connection.ops, method, False)
|
||||
|
||||
|
||||
class BaseSpatialOperations(object):
|
||||
"""
|
||||
|
|
|
@ -10,6 +10,9 @@ from django.contrib.gis.db.backends.mysql.operations import MySQLOperations
|
|||
|
||||
class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures):
|
||||
has_spatialrefsys_table = False
|
||||
supports_transform = False
|
||||
supports_real_shape_operations = False
|
||||
supports_null_geometries = False
|
||||
supports_num_points_poly = False
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ from unittest import skipUnless
|
|||
from django.db import connection
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geos import HAS_GEOS
|
||||
from django.contrib.gis.tests.utils import (
|
||||
no_mysql, no_oracle, mysql, oracle, postgis, spatialite)
|
||||
from django.contrib.gis.tests.utils import mysql, oracle, postgis, spatialite
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
from django.utils import six
|
||||
|
||||
|
@ -104,7 +103,7 @@ class GeoModelTest(TestCase):
|
|||
self.assertEqual(ply, State.objects.get(name='NullState').poly)
|
||||
ns.delete()
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("supports_transform")
|
||||
def test_lookup_insert_transform(self):
|
||||
"Testing automatic transform for lookups and inserts."
|
||||
# San Antonio in 'WGS84' (SRID 4326)
|
||||
|
@ -176,7 +175,7 @@ class GeoModelTest(TestCase):
|
|||
self.assertEqual(True, isinstance(f_4.geom, GeometryCollection))
|
||||
self.assertEqual(f_3.geom, f_4.geom[2])
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("supports_transform")
|
||||
def test_inherited_geofields(self):
|
||||
"Test GeoQuerySet methods on inherited Geometry fields."
|
||||
# Creating a Pennsylvanian city.
|
||||
|
@ -205,13 +204,13 @@ class GeoModelTest(TestCase):
|
|||
class GeoLookupTest(TestCase):
|
||||
fixtures = ['initial']
|
||||
|
||||
@no_mysql
|
||||
def test_disjoint_lookup(self):
|
||||
"Testing the `disjoint` lookup type."
|
||||
ptown = City.objects.get(name='Pueblo')
|
||||
qs1 = City.objects.filter(point__disjoint=ptown.point)
|
||||
self.assertEqual(7, qs1.count())
|
||||
|
||||
if connection.features.supports_real_shape_operations:
|
||||
qs2 = State.objects.filter(poly__disjoint=ptown.point)
|
||||
self.assertEqual(1, qs2.count())
|
||||
self.assertEqual('Kansas', qs2[0].name)
|
||||
|
@ -317,7 +316,7 @@ class GeoLookupTest(TestCase):
|
|||
for c in [c1, c2, c3]:
|
||||
self.assertEqual('Houston', c.name)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("supports_null_geometries")
|
||||
def test_null_geometries(self):
|
||||
"Testing NULL geometry support, and the `isnull` lookup type."
|
||||
# Creating a state with a NULL boundary.
|
||||
|
@ -347,7 +346,7 @@ class GeoLookupTest(TestCase):
|
|||
State.objects.filter(name='Northern Mariana Islands').update(poly=None)
|
||||
self.assertEqual(None, State.objects.get(name='Northern Mariana Islands').poly)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("supports_relate_lookup")
|
||||
def test_relate_lookup(self):
|
||||
"Testing the 'relate' lookup type."
|
||||
# To make things more interesting, we will have our Texas reference point in
|
||||
|
@ -397,7 +396,7 @@ class GeoQuerySetTest(TestCase):
|
|||
|
||||
# Please keep the tests in GeoQuerySet method's alphabetic order
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_centroid_method")
|
||||
def test_centroid(self):
|
||||
"Testing the `centroid` GeoQuerySet method."
|
||||
qs = State.objects.exclude(poly__isnull=True).centroid()
|
||||
|
@ -410,7 +409,10 @@ class GeoQuerySetTest(TestCase):
|
|||
for s in qs:
|
||||
self.assertEqual(True, s.poly.centroid.equals_exact(s.centroid, tol))
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_difference_method")
|
||||
@skipUnlessDBFeature("has_intersection_method")
|
||||
@skipUnlessDBFeature("has_sym_difference_method")
|
||||
@skipUnlessDBFeature("has_union_method")
|
||||
def test_diff_intersection_union(self):
|
||||
"Testing the `difference`, `intersection`, `sym_difference`, and `union` GeoQuerySet methods."
|
||||
geom = Point(5, 23)
|
||||
|
@ -439,7 +441,7 @@ class GeoQuerySetTest(TestCase):
|
|||
self.assertSetEqual(set(g.wkt for g in c.mpoly.union(geom)),
|
||||
set(g.wkt for g in c.union))
|
||||
|
||||
@skipUnless(getattr(connection.ops, 'envelope', False), 'Database does not support envelope operation')
|
||||
@skipUnlessDBFeature("has_envelope_method")
|
||||
def test_envelope(self):
|
||||
"Testing the `envelope` GeoQuerySet method."
|
||||
countries = Country.objects.all().envelope()
|
||||
|
@ -520,12 +522,9 @@ class GeoQuerySetTest(TestCase):
|
|||
# Finally, we set every available keyword.
|
||||
self.assertEqual(chicago_json, City.objects.geojson(bbox=True, crs=True, precision=5).get(name='Chicago').geojson)
|
||||
|
||||
@skipUnlessDBFeature("has_gml_method")
|
||||
def test_gml(self):
|
||||
"Testing GML output from the database using GeoQuerySet.gml()."
|
||||
if mysql or (spatialite and not connection.ops.gml):
|
||||
self.assertRaises(NotImplementedError, Country.objects.all().gml, field_name='mpoly')
|
||||
return
|
||||
|
||||
# Should throw a TypeError when tyring to obtain GML from a
|
||||
# non-geometry field.
|
||||
qs = City.objects.all()
|
||||
|
@ -548,13 +547,9 @@ class GeoQuerySetTest(TestCase):
|
|||
if postgis:
|
||||
self.assertIn('<gml:pos srsDimension="2">', City.objects.gml(version=3).get(name='Pueblo').gml)
|
||||
|
||||
@skipUnlessDBFeature("has_kml_method")
|
||||
def test_kml(self):
|
||||
"Testing KML output from the database using GeoQuerySet.kml()."
|
||||
# Only PostGIS and Spatialite (>=2.4.0-RC4) support KML serialization
|
||||
if not (postgis or (spatialite and connection.ops.kml)):
|
||||
self.assertRaises(NotImplementedError, State.objects.all().kml, field_name='poly')
|
||||
return
|
||||
|
||||
# Should throw a TypeError when trying to obtain KML from a
|
||||
# non-geometry field.
|
||||
qs = City.objects.all()
|
||||
|
@ -567,7 +562,7 @@ class GeoQuerySetTest(TestCase):
|
|||
self.assertEqual('<Point><coordinates>-104.609252,38.255001</coordinates></Point>', ptown.kml)
|
||||
|
||||
# Only PostGIS has support for the MakeLine aggregate.
|
||||
@skipUnlessDBFeature("has_make_line_method")
|
||||
@skipUnlessDBFeature("supports_make_line_aggr")
|
||||
def test_make_line(self):
|
||||
"Testing the `make_line` GeoQuerySet method."
|
||||
# Ensuring that a `TypeError` is raised on models without PointFields.
|
||||
|
@ -578,7 +573,7 @@ class GeoQuerySetTest(TestCase):
|
|||
ref_line = GEOSGeometry('LINESTRING(-95.363151 29.763374,-96.801611 32.782057,-97.521157 34.464642,174.783117 -41.315268,-104.609252 38.255001,-95.23506 38.971823,-87.650175 41.850385,-123.305196 48.462611)', srid=4326)
|
||||
self.assertEqual(ref_line, City.objects.make_line())
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_num_geom_method")
|
||||
def test_num_geom(self):
|
||||
"Testing the `num_geom` GeoQuerySet method."
|
||||
# Both 'countries' only have two geometries.
|
||||
|
@ -605,7 +600,7 @@ class GeoQuerySetTest(TestCase):
|
|||
for c in City.objects.num_points():
|
||||
self.assertEqual(1, c.num_points)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_point_on_surface_method")
|
||||
def test_point_on_surface(self):
|
||||
"Testing the `point_on_surface` GeoQuerySet method."
|
||||
# Reference values.
|
||||
|
@ -641,8 +636,7 @@ class GeoQuerySetTest(TestCase):
|
|||
if oracle:
|
||||
self.assertRaises(TypeError, State.objects.reverse_geom)
|
||||
|
||||
@no_mysql
|
||||
@no_oracle
|
||||
@skipUnlessDBFeature("has_scale_method")
|
||||
def test_scale(self):
|
||||
"Testing the `scale` GeoQuerySet method."
|
||||
xfac, yfac = 2, 3
|
||||
|
@ -692,11 +686,9 @@ class GeoQuerySetTest(TestCase):
|
|||
ref = fromstr('MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))')
|
||||
self.assertTrue(ref.equals_exact(Country.objects.snap_to_grid(0.05, 0.23, 0.5, 0.17).get(name='San Marino').snap_to_grid, tol))
|
||||
|
||||
@skipUnlessDBFeature("has_svg_method")
|
||||
def test_svg(self):
|
||||
"Testing SVG output using GeoQuerySet.svg()."
|
||||
if mysql or oracle:
|
||||
self.assertRaises(NotImplementedError, City.objects.svg)
|
||||
return
|
||||
|
||||
self.assertRaises(TypeError, City.objects.svg, precision='foo')
|
||||
# SELECT AsSVG(geoapp_city.point, 0, 8) FROM geoapp_city WHERE name = 'Pueblo';
|
||||
|
@ -707,7 +699,7 @@ class GeoQuerySetTest(TestCase):
|
|||
self.assertEqual(svg1, City.objects.svg().get(name='Pueblo').svg)
|
||||
self.assertEqual(svg2, City.objects.svg(relative=5).get(name='Pueblo').svg)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_transform_method")
|
||||
def test_transform(self):
|
||||
"Testing the transform() GeoQuerySet method."
|
||||
# Pre-transformed points for Houston and Pueblo.
|
||||
|
@ -730,8 +722,7 @@ class GeoQuerySetTest(TestCase):
|
|||
self.assertAlmostEqual(ptown.x, p.point.x, prec)
|
||||
self.assertAlmostEqual(ptown.y, p.point.y, prec)
|
||||
|
||||
@no_mysql
|
||||
@no_oracle
|
||||
@skipUnlessDBFeature("has_translate_method")
|
||||
def test_translate(self):
|
||||
"Testing the `translate` GeoQuerySet method."
|
||||
xfac, yfac = 5, -23
|
||||
|
@ -744,7 +735,7 @@ class GeoQuerySetTest(TestCase):
|
|||
self.assertAlmostEqual(c1[0] + xfac, c2[0], 5)
|
||||
self.assertAlmostEqual(c1[1] + yfac, c2[1], 5)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_unionagg_method")
|
||||
def test_unionagg(self):
|
||||
"Testing the `unionagg` (aggregate union) GeoQuerySet method."
|
||||
tx = Country.objects.get(name='Texas').mpoly
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.gis.geos import HAS_GEOS
|
||||
from django.contrib.gis.tests.utils import mysql, no_mysql, no_oracle
|
||||
from django.contrib.gis.tests.utils import mysql, no_oracle
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
|
||||
if HAS_GEOS:
|
||||
|
@ -36,7 +36,7 @@ class RelatedGeoModelTest(TestCase):
|
|||
self.assertEqual(st, c.state)
|
||||
self.assertEqual(Point(lon, lat), c.location.point)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_transform_method")
|
||||
def test03_transform_related(self):
|
||||
"Testing the `transform` GeoQuerySet method on related geographic models."
|
||||
# All the transformations are to state plane coordinate systems using
|
||||
|
@ -80,7 +80,7 @@ class RelatedGeoModelTest(TestCase):
|
|||
for ref_val, e_val in zip(ref, e):
|
||||
self.assertAlmostEqual(ref_val, e_val, tol)
|
||||
|
||||
@no_mysql
|
||||
@skipUnlessDBFeature("has_unionagg_method")
|
||||
def test04b_related_union_aggregate(self):
|
||||
"Testing the `unionagg` GeoQuerySet aggregates on related geographic models."
|
||||
# This combines the Extent and Union aggregates into one query
|
||||
|
|
|
@ -21,14 +21,6 @@ def no_oracle(func):
|
|||
return no_backend(func, 'oracle')
|
||||
|
||||
|
||||
def no_postgis(func):
|
||||
return no_backend(func, 'postgis')
|
||||
|
||||
|
||||
def no_mysql(func):
|
||||
return no_backend(func, 'mysql')
|
||||
|
||||
|
||||
# Shortcut booleans to omit only portions of tests.
|
||||
_default_db = settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'].rsplit('.')[-1]
|
||||
oracle = _default_db == 'oracle'
|
||||
|
|
Loading…
Reference in New Issue