Difficulty serializing Geography column type using sqlalchemy marshmallow
Question: I an trying to use Marshmallow to do do deserialize and serialise SQLAlchemy
objects but run into a problem when dealing with Geography fields in the ORM.

Firstly the model:

    
    
    class Address(db.Model, TableColumnsBase):
        __tablename__ = 'address'
        addressLine1 = db.Column(String(255), nullable=True, info="Street address, company name, c/o")
        addressLine2 = db.Column(String(255), nullable=True, info="Apartment, suite, unit, building floor, etc")
        countryCode = db.Column(String(255), nullable=True, info="Country code such as AU, US etc")
        suburb = db.Column(String(255), nullable=True, info="Users suburb such as Elizabeth Bay")
        postcode = db.Column(String(32), nullable=True, info="Users postcode such as 2011 for Elizabeth Bay")
        state = db.Column(String(64), info="State for user such as NSW")
        user_presence = one_to_many('UserPresence', backref = 'user', lazy='select', cascade='all, delete-orphan')
    
        location = Column(Geography(geometry_type='POINT', srid=4326))
        discriminator = Column('type', String(50))
        __mapper_args__ = {'polymorphic_on': discriminator}
    
        def as_physical(self):
            pa = {   # TODO - stub, make it proper
                "latitude": 2.1,
                "longitude": 1.1,
                "unitNumber": '1',
                "streetNumber": self.addressLine1,
                "streetName": self.addressLine2,
                "streetType": 'street',
                "suburb": self.suburb,
                "postcode": self.postcode
            }
            return pa
    

Secondly the marshmallow schema:

    
    
    class AddressSchema(ModelSchema):
        class Meta:
            model = Address
            sqla_session 
    

Then the error when trying to run:

    
    
    /Users/james/.virtualenvs/trustmile-api-p2710/bin/python /Users/james/Documents/workspace/trustmile-backend/trustmile/tests/postgis_scratch.py
    Traceback (most recent call last):
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/tests/postgis_scratch.py", line 1, in <module>
        from app import db
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/__init__.py", line 54, in <module>
        from app.api.consumer_v1 import bp as blueprint
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/api/__init__.py", line 4, in <module>
        import courier_v1
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/api/courier_v1/__init__.py", line 5, in <module>
        from .routes import routes
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/api/courier_v1/routes.py", line 10, in <module>
        from .api.nearestNeighbours_latitude_longitude import NearestneighboursLatitudeLongitude
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/api/courier_v1/api/__init__.py", line 5, in <module>
        from app.ops import requesthandler
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/ops/requesthandler.py", line 1, in <module>
        from app.ops.consumer_operations import cons_ops, ConsumerOperationsFactory
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/ops/consumer_operations.py", line 19, in <module>
        from app.users.serialize import ApplicationInstallationSchema, UserAddressSchema, ConsumerUserSchema, CourierUserSchema
      File "/Users/james/Documents/workspace/trustmile-backend/trustmile/app/users/serialize.py", line 40, in <module>
        class AddressSchema(ModelSchema):
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow/schema.py", line 116, in __new__
        dict_cls=dict_cls
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/schema.py", line 57, in get_declared_fields
        declared_fields = mcs.get_fields(converter, opts)
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/schema.py", line 90, in get_fields
        include_fk=opts.include_fk,
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/convert.py", line 77, in fields_for_model
        field = self.property2field(prop)
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/convert.py", line 95, in property2field
        field_class = self._get_field_class_for_property(prop)
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/convert.py", line 153, in _get_field_class_for_property
        field_cls = self._get_field_class_for_column(column)
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/convert.py", line 123, in _get_field_class_for_column
        return self._get_field_class_for_data_type(column.type)
      File "/Users/james/.virtualenvs/trustmile-api-p2710/lib/python2.7/site-packages/marshmallow_sqlalchemy/convert.py", line 145, in _get_field_class_for_data_type
        'Could not find field column of type {0}.'.format(types[0]))
    marshmallow_sqlalchemy.exceptions.ModelConversionError: Could not find field column of type <class 'geoalchemy2.types.Geography'>.
    

I've tried a few overrides on the location field however to no avail. Any help
much appreciated.

**Updated Jan 24 2016**

This is my code as it stands as the moment with a simpler model:

The SQLAlchemyModel:

    
    
    class Location(db.Model, TableColumnsBase):
        __tablename__ = "location"
        loc = db.Column(Geography(geometry_type='POINT', srid=4326))
    

The marshmallow_sqlalchemy schema object

    
    
    class LocationSchema(ModelSchema):
        loc = GeographySerializationField(attribute='loc')
        class Meta:
            model = Location
            sqla_session = db.session
            model_converter = GeoConverter
    

Other plumbing as per suggestions:

    
    
    class GeoConverter(ModelConverter):
        SQLA_TYPE_MAPPING = ModelConverter.SQLA_TYPE_MAPPING.copy()
        SQLA_TYPE_MAPPING.update({
            Geography: fields.Str
        })
    
    
    class GeographySerializationField(fields.Field):
        def _serialize(self, value, attr, obj):
            if value is None:
                return value
            else:
                if isinstance(value, Geography):
                    return json.dumps({'latitude': db.session.scalar(geo_funcs.ST_X(value)), 'longitude': db.session.scalar(geo_funcs.ST_Y(value))})
                else:
                    return None
    
        def _deserialize(self, value, attr, data):
            """Deserialize value. Concrete :class:`Field` classes should implement this method.
    
            :param value: The value to be deserialized.
            :param str attr: The attribute/key in `data` to be deserialized.
            :param dict data: The raw input data passed to the `Schema.load`.
            :raise ValidationError: In case of formatting or validation failure.
            :return: The deserialized value.
    
            .. versionchanged:: 2.0.0
                Added ``attr`` and ``data`` parameters.
            """
            if value is None:
                return value
            else:
                if isinstance(value, Geography):
                    return {'latitude': db.session.scalar(geo_funcs.ST_X(value)), 'longitude': db.session.scalar(geo_funcs.ST_Y(value))}
                else:
                    return None
    

In running the code:

    
    
    from app.users.serialize import *
    from app.model.meta.schema import Location
    l = LocationSchema()
    loc = Location(27.685994, 85.317815)
    r = l.load(loc)
    print r
    

The result I get is:

    
    
    UnmarshalResult(data={}, errors={u'_schema': [u'Invalid input type.']})
    


Answer: You can override ModelConverter class and specify custom mapping for your
geography field. See Jair Perrut's answer here [How to use marshmallow to
serialize a custom sqlalchemy
field?](https://stackoverflow.com/questions/33160762/how-to-use-marshmallow-
to-serialize-a-custom-sqlalchemy-
field/34864669#34864669?newreg=150e656b5a134fb6856878052ca459b0)

    
    
    from marshmallow_sqlalchemy import ModelConverter
    from marshmallow import fields
    
    class GeoConverter(ModelConverter):
        SQLA_TYPE_MAPPING = ModelConverter.SQLA_TYPE_MAPPING.copy()
        SQLA_TYPE_MAPPING.update({
            Geography: fields.Str
        })
    
    class Meta:
        model = Address
        sqla_session = session
        model_converter = GeoConverter
    

