From 5aa9b18e0bc7828f202f21edb3c17b0704e92091 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Sun, 29 Sep 2019 15:29:24 +0200 Subject: [PATCH 1/2] Fix state of field for geo field to 'base' It was previously state `manual` Usage of `manual` is for custom fields created from the interface such as fields prefixed by `x_`. In commit odoo/odoo@547118d manual field don't have an xmlid anymore. Thus it had the side effect to fail when loading definition of vector layers from an XML file as an XMLID is required to reference the field. The XMLID would not be found. Fixes #206 --- base_geoengine/fields.py | 1 - 1 file changed, 1 deletion(-) diff --git a/base_geoengine/fields.py b/base_geoengine/fields.py index a62cab4ce..0c2275364 100644 --- a/base_geoengine/fields.py +++ b/base_geoengine/fields.py @@ -39,7 +39,6 @@ def column_type(self): 'dim': 2, 'srid': 3857, 'gist_index': True, - 'manual': True, } def convert_to_column(self, value, record, values=None): From 3460f5820012825744da2269ca9945b8c5edef8b Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 1 Oct 2019 09:36:31 +0200 Subject: [PATCH 2/2] Fix geometry field creation missing configurations After changing the field in state `base` instead of `manual`. Those fields were created without configuration. srid would be 0 gemetry type in geometry_columns table would be GEOMETRY instead of POINT, LINESTRING, ... This fixes the creation of geometry to use AddGeometryColumn again. --- base_geoengine/fields.py | 112 +++++++++++++++++++----------------- base_geoengine/geo_db.py | 39 +++++++++++++ base_geoengine/geo_model.py | 33 ----------- 3 files changed, 98 insertions(+), 86 deletions(-) diff --git a/base_geoengine/fields.py b/base_geoengine/fields.py index 0c2275364..b2620ff48 100644 --- a/base_geoengine/fields.py +++ b/base_geoengine/fields.py @@ -5,8 +5,10 @@ from operator import attrgetter from odoo import fields, _ +from odoo.tools import sql from .geo_helper import geo_convertion_helper as convert +from .geo_db import create_geo_column logger = logging.getLogger(__name__) try: @@ -94,26 +96,6 @@ def load_geo(cls, wkb): """Load geometry into browse record after read was done""" return wkbloads(wkb, hex=True) if wkb else False - def create_geo_column(self, cr, col_name, table, model): - """Create a columns of type the geom""" - try: - cr.execute("SELECT AddGeometryColumn( %s, %s, %s, %s, %s)", - (table, - col_name, - self.srid, - self.geo_type, - self.dim)) - self._create_index(cr, table, col_name) - except Exception: - cr.rollback() - logger.exception('Cannot create column %s table %s:', - col_name, table) - raise - finally: - cr.commit() - - return True - def entry_to_shape(self, value, same_type=False): """Transform input into an object""" shape = convert.value_to_shape(value) @@ -124,63 +106,87 @@ def entry_to_shape(self, value, same_type=False): self.geo_type.lower())) return shape - def _postgis_index_name(self, table, col_name): - return "%s_%s_gist_index" % (table, col_name) - - def _create_index(self, cr, table, col_name): - if self.gist_index: - try: - cr.execute("CREATE INDEX %s ON %s USING GIST ( %s )" % - (self._postgis_index_name(table, col_name), - table, - col_name)) - except Exception: - cr.rollback() - logger.exception( - 'Cannot create gist index for col %s table %s:', - col_name, table) - raise - finally: - cr.commit() - - def update_geo_column(self, cr, col_name, table, model): + def update_geo_db_column(self, model): """Update the column type in the database. """ + cr = model._cr query = ("""SELECT srid, type, coord_dimension FROM geometry_columns WHERE f_table_name = %s AND f_geometry_column = %s""") - cr.execute(query, (table, col_name)) + cr.execute(query, (model._table, self.name)) check_data = cr.fetchone() if not check_data: raise TypeError( - "geometry_columns table seems to be corrupted. " - "SRID check is not possible") + "geometry_columns table seems to be corrupted." + " SRID check is not possible") if check_data[0] != self.srid: raise TypeError( - "Reprojection of column is not implemented" - "We can not change srid %s to %s" % ( + "Reprojection of column is not implemented." + " We can not change srid %s to %s" % ( self.srid, check_data[0])) - if check_data[1] != self.geo_type: + elif check_data[1] != self.geo_type: raise TypeError( - "Geo type modification is not implemented" - "We can not change type %s to %s" % ( + "Geo type modification is not implemented." + " We can not change type %s to %s" % ( check_data[1], self.geo_type)) - if check_data[2] != self.dim: + elif check_data[2] != self.dim: raise TypeError( - "Geo dimention modification is not implemented" - "We can not change dimention %s to %s" % ( + "Geo dimention modification is not implemented." + " We can not change dimention %s to %s" % ( check_data[2], self.dim)) if self.gist_index: cr.execute( "SELECT indexname FROM pg_indexes WHERE indexname = %s", - (self._postgis_index_name(table, col_name),)) + (self._postgis_index_name(model._table, self.name),)) index = cr.fetchone() if index: return True - self._create_index(cr, table, col_name) + self._create_index(cr, model._table, self.name) return True + def update_db_column(self, model, column): + """ Create/update the column corresponding to ``self``. + + For creation of geo column + + :param model: an instance of the field's model + :param column: the column's configuration (dict) + if it exists, or ``None`` + """ + # the column does not exist, create it + + if not column: + create_geo_column( + model._cr, + model._table, + self.name, + self.geo_type, + self.srid, + self.dim, + self.string) + return + + if column['udt_name'] == self.column_type[0]: + return + + self.update_geo_db_column(model) + + if column['udt_name'] in self.column_cast_from: + sql.convert_column( + model._cr, model._table, self.name, self.column_type[1]) + else: + newname = (self.name + '_moved{}').format + i = 0 + while sql.column_exists( + model._cr, model._table, newname(i) + ): + i += 1 + if column['is_nullable'] == 'NO': + sql.drop_not_null(model._cr, model._table, self.name) + sql.rename_column(model._cr, model._table, self.name, newname(i)) + sql.create_column(model._cr, model._table, self.name, self.column_type[1], self.string) + class GeoLine(GeoField): """Field for POSTGIS geometry Line type""" diff --git a/base_geoengine/geo_db.py b/base_geoengine/geo_db.py index 24d68bfe4..cddf984d6 100644 --- a/base_geoengine/geo_db.py +++ b/base_geoengine/geo_db.py @@ -5,8 +5,10 @@ from odoo import _ from odoo.exceptions import MissingError +from odoo.tools import sql logger = logging.getLogger('geoengine.sql') +_schema = logging.getLogger('odoo.schema') def init_postgis(cr): @@ -45,3 +47,40 @@ def init_postgis(cr): "CREATE EXTENSION postgis_topology;\n" ) ) + + +def create_geo_column( + cr, tablename, columnname, geotype, srid, dim, comment=None): + """ Create a geometry column with the given type. + + :params: srid: geometry's projection srid + :params: dim: geometry's dimension (2D or 3D) + """ + cr.execute("SELECT AddGeometryColumn( %s, %s, %s, %s, %s)", + (tablename, + columnname, + srid, + geotype, + dim)) + if comment: + cr.execute('COMMENT ON COLUMN "{}"."{}" IS %s'.format( + tablename, columnname), (comment,)) + _schema.debug( + "Table %r: added geometry column %r of type %s", + tablename, columnname, geotype) + + +def _postgis_index_name(table, col_name): + return "%s_%s_gist_index" % (table, col_name) + + +def create_geo_index(cr, columnname, tablename): + """ Create the given index unless it exists. """ + indexname = _postgis_index_name(tablename, columnname) + if sql.index_exists(cr, indexname): + return + cr.execute("CREATE INDEX %s ON %s USING GIST ( %s )" % + (indexname, + tablename, + columnname)) + _schema.debug("Table %r: created index %r (%s)", tablename, indexname, args) diff --git a/base_geoengine/geo_model.py b/base_geoengine/geo_model.py index 018477cc6..8568dc232 100644 --- a/base_geoengine/geo_model.py +++ b/base_geoengine/geo_model.py @@ -19,37 +19,6 @@ class GeoModel(models.AbstractModel): # Array of ash that define layer and data to use _georepr = [] - @api.model_cr_context - def _auto_init(self): - """Initialize the columns in dB and Create the GIST index - only create and update supported - - We override the base methid because creation of fields in DB is not - actually delegated to the field it self but to the ORM _auto_init - function - """ - cr = self._cr - - geo_fields = {} - for f_name, field in self._fields.items(): - if field.type.startswith('geo_'): - geo_fields[f_name] = field - res = super()._auto_init() - if self._abstract: - return res - - # Create geo columns - column_data = tools.table_columns(cr, self._table) - - for f_name, geo_field in geo_fields.items(): - if geo_field.compute and not geo_field.store: - continue - fct = geo_field.create_geo_column - if f_name in column_data: - fct = geo_field.update_geo_column - fct(cr, f_name, self._table, self._name) - return res - @api.model def fields_get(self, allfields=None, attributes=None): """Add geo_type definition for geo fields""" @@ -110,8 +79,6 @@ def set_field_real_name(in_tuple): 'default_extent': view.default_extent or DEFAULT_EXTENT, 'default_zoom': view.default_zoom, } - # XXX still the case ? - # TODO find why context in read does not work with webclient for layer in view.raster_layer_ids: layer_dict = layer.read()[0] res['geoengine_layers']['backgrounds'].append(layer_dict)