create emailer version 1

This commit is contained in:
msosic97
2022-04-11 15:09:06 +02:00
parent 94c4245c8a
commit 37541f19a0
4850 changed files with 715130 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper
from .features import DatabaseFeatures
from .introspection import MySQLIntrospection
from .operations import MySQLOperations
from .schema import MySQLGISSchemaEditor
class DatabaseWrapper(MySQLDatabaseWrapper):
SchemaEditorClass = MySQLGISSchemaEditor
# Classes instantiated in __init__().
features_class = DatabaseFeatures
introspection_class = MySQLIntrospection
ops_class = MySQLOperations

View File

@@ -0,0 +1,44 @@
from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
from django.db.backends.mysql.features import DatabaseFeatures as MySQLDatabaseFeatures
from django.utils.functional import cached_property
class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures):
has_spatialrefsys_table = False
supports_add_srs_entry = False
supports_distance_geodetic = False
supports_length_geodetic = False
supports_area_geodetic = False
supports_transform = False
supports_null_geometries = False
supports_num_points_poly = False
unsupported_geojson_options = {"crs"}
@cached_property
def empty_intersection_returns_none(self):
return (
not self.connection.mysql_is_mariadb
and self.connection.mysql_version < (5, 7, 5)
)
@cached_property
def supports_geometry_field_unique_index(self):
# Not supported in MySQL since https://dev.mysql.com/worklog/task/?id=11808
return self.connection.mysql_is_mariadb
@cached_property
def django_test_skips(self):
skips = super().django_test_skips
if not self.connection.mysql_is_mariadb and self.connection.mysql_version < (
8,
0,
0,
):
skips.update(
{
"MySQL < 8 gives different results.": {
"gis_tests.geoapp.tests.GeoLookupTest.test_disjoint_lookup",
},
}
)
return skips

View File

@@ -0,0 +1,37 @@
from MySQLdb.constants import FIELD_TYPE
from django.contrib.gis.gdal import OGRGeomType
from django.db.backends.mysql.introspection import DatabaseIntrospection
class MySQLIntrospection(DatabaseIntrospection):
# Updating the data_types_reverse dictionary with the appropriate
# type for Geometry fields.
data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
data_types_reverse[FIELD_TYPE.GEOMETRY] = "GeometryField"
def get_geometry_type(self, table_name, description):
with self.connection.cursor() as cursor:
# In order to get the specific geometry type of the field,
# we introspect on the table definition using `DESCRIBE`.
cursor.execute("DESCRIBE %s" % self.connection.ops.quote_name(table_name))
# Increment over description info until we get to the geometry
# column.
for column, typ, null, key, default, extra in cursor.fetchall():
if column == description.name:
# Using OGRGeomType to convert from OGC name to Django field.
# MySQL does not support 3D or SRIDs, so the field params
# are empty.
field_type = OGRGeomType(typ).django
field_params = {}
break
return field_type, field_params
def supports_spatial_index(self, cursor, table_name):
# Supported with MyISAM/Aria, or InnoDB on MySQL 5.7.5+/MariaDB 10.2.2+
storage_engine = self.get_storage_engine(cursor, table_name)
if storage_engine == "InnoDB":
return self.connection.mysql_version >= (
(10, 2, 2) if self.connection.mysql_is_mariadb else (5, 7, 5)
)
return storage_engine in ("MyISAM", "Aria")

View File

@@ -0,0 +1,127 @@
from django.contrib.gis.db import models
from django.contrib.gis.db.backends.base.adapter import WKTAdapter
from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations
from django.contrib.gis.db.backends.utils import SpatialOperator
from django.contrib.gis.geos.geometry import GEOSGeometryBase
from django.contrib.gis.geos.prototypes.io import wkb_r
from django.contrib.gis.measure import Distance
from django.db.backends.mysql.operations import DatabaseOperations
from django.utils.functional import cached_property
class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
name = "mysql"
geom_func_prefix = "ST_"
Adapter = WKTAdapter
@cached_property
def mariadb(self):
return self.connection.mysql_is_mariadb
@cached_property
def mysql(self):
return not self.connection.mysql_is_mariadb
@cached_property
def select(self):
return self.geom_func_prefix + "AsBinary(%s)"
@cached_property
def from_text(self):
return self.geom_func_prefix + "GeomFromText"
@cached_property
def gis_operators(self):
operators = {
"bbcontains": SpatialOperator(
func="MBRContains"
), # For consistency w/PostGIS API
"bboverlaps": SpatialOperator(func="MBROverlaps"), # ...
"contained": SpatialOperator(func="MBRWithin"), # ...
"contains": SpatialOperator(func="ST_Contains"),
"crosses": SpatialOperator(func="ST_Crosses"),
"disjoint": SpatialOperator(func="ST_Disjoint"),
"equals": SpatialOperator(func="ST_Equals"),
"exact": SpatialOperator(func="ST_Equals"),
"intersects": SpatialOperator(func="ST_Intersects"),
"overlaps": SpatialOperator(func="ST_Overlaps"),
"same_as": SpatialOperator(func="ST_Equals"),
"touches": SpatialOperator(func="ST_Touches"),
"within": SpatialOperator(func="ST_Within"),
}
if self.connection.mysql_is_mariadb:
operators["relate"] = SpatialOperator(func="ST_Relate")
return operators
disallowed_aggregates = (
models.Collect,
models.Extent,
models.Extent3D,
models.MakeLine,
models.Union,
)
@cached_property
def unsupported_functions(self):
unsupported = {
"AsGML",
"AsKML",
"AsSVG",
"Azimuth",
"BoundingCircle",
"ForcePolygonCW",
"GeometryDistance",
"LineLocatePoint",
"MakeValid",
"MemSize",
"Perimeter",
"PointOnSurface",
"Reverse",
"Scale",
"SnapToGrid",
"Transform",
"Translate",
}
if self.connection.mysql_is_mariadb:
unsupported.remove("PointOnSurface")
unsupported.update({"GeoHash", "IsValid"})
if self.connection.mysql_version < (10, 2, 4):
unsupported.add("AsGeoJSON")
elif self.connection.mysql_version < (5, 7, 5):
unsupported.update({"AsGeoJSON", "GeoHash", "IsValid"})
return unsupported
def geo_db_type(self, f):
return f.geom_type
def get_distance(self, f, value, lookup_type):
value = value[0]
if isinstance(value, Distance):
if f.geodetic(self.connection):
raise ValueError(
"Only numeric values of degree units are allowed on "
"geodetic distance queries."
)
dist_param = getattr(
value, Distance.unit_attname(f.units_name(self.connection))
)
else:
dist_param = value
return [dist_param]
def get_geometry_converter(self, expression):
read = wkb_r().read
srid = expression.output_field.srid
if srid == -1:
srid = None
geom_class = expression.output_field.geom_class
def converter(value, expression, connection):
if value is not None:
geom = GEOSGeometryBase(read(memoryview(value)), geom_class)
if srid:
geom.srid = srid
return geom
return converter

View File

@@ -0,0 +1,83 @@
import logging
from django.contrib.gis.db.models import GeometryField
from django.db import OperationalError
from django.db.backends.mysql.schema import DatabaseSchemaEditor
logger = logging.getLogger("django.contrib.gis")
class MySQLGISSchemaEditor(DatabaseSchemaEditor):
sql_add_spatial_index = "CREATE SPATIAL INDEX %(index)s ON %(table)s(%(column)s)"
sql_drop_spatial_index = "DROP INDEX %(index)s ON %(table)s"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry_sql = []
def skip_default(self, field):
# Geometry fields are stored as BLOB/TEXT, for which MySQL < 8.0.13 and
# MariaDB < 10.2.1 don't support defaults.
if (
isinstance(field, GeometryField)
and not self._supports_limited_data_type_defaults
):
return True
return super().skip_default(field)
def column_sql(self, model, field, include_default=False):
column_sql = super().column_sql(model, field, include_default)
# MySQL doesn't support spatial indexes on NULL columns
if isinstance(field, GeometryField) and field.spatial_index and not field.null:
qn = self.connection.ops.quote_name
db_table = model._meta.db_table
self.geometry_sql.append(
self.sql_add_spatial_index
% {
"index": qn(self._create_spatial_index_name(model, field)),
"table": qn(db_table),
"column": qn(field.column),
}
)
return column_sql
def create_model(self, model):
super().create_model(model)
self.create_spatial_indexes()
def add_field(self, model, field):
super().add_field(model, field)
self.create_spatial_indexes()
def remove_field(self, model, field):
if isinstance(field, GeometryField) and field.spatial_index:
qn = self.connection.ops.quote_name
sql = self.sql_drop_spatial_index % {
"index": qn(self._create_spatial_index_name(model, field)),
"table": qn(model._meta.db_table),
}
try:
self.execute(sql)
except OperationalError:
logger.error(
"Couldn't remove spatial index: %s (may be expected "
"if your storage engine doesn't support them).",
sql,
)
super().remove_field(model, field)
def _create_spatial_index_name(self, model, field):
return "%s_%s_id" % (model._meta.db_table, field.column)
def create_spatial_indexes(self):
for sql in self.geometry_sql:
try:
self.execute(sql)
except OperationalError:
logger.error(
"Cannot create SPATIAL INDEX %s. Only MyISAM and (as of "
"MySQL 5.7.5) InnoDB support them.",
sql,
)
self.geometry_sql = []