graphene-sqlalchemy
graphene-sqlalchemy copied to clipboard
geoalchemy2 support
Help! geoalchemy2 support!
class CoordinateMixin():
location = db.Column('location', Geography(geometry_type='POINT' ,srid=4326, spatial_index=True, dimension=2), doc='gps coordinate')
Exception: Don't know how to convert the SQLAlchemy field user.location (<class 'sqlalchemy.sql.schema.Column'>)
I'v already add the following code in schema file.
from geoalchemy2 import Geography
from graphene_sqlalchemy.converter import get_column_doc, convert_sqlalchemy_type
from app.graphql.extesions import CoordinateJSON
@convert_sqlalchemy_type.register(Geography)
def convert_column_to_coordinatejson(type, column, registry=None):
return graphene.Field(CoordinateJSON, description=get_column_doc(column))
schema = graphene.Schema(query=RootQuery, types=types, mutation=MyMutation)
Please let me know how to import CoordinateJSON, also can anyone provide solution for how it works.
@keyeMyria What you have won't work, because you haven't told GraphQL how the underlying type should be represented.
Here's a way to do it, with geometries instead of geographies, but it should be applicable to geography type with minor changes. This implementation assumes that you want to represent the geometry or geography as its WKT; you can sub the CoordinateJSON output instead, as you prefer.
I imported graphene_sqlalchemy like so, for easier typing:
import graphene_sqlalchemy as gsqa
First, you need to define a scalar type that will represent the geometry:
class Geometry_WKT(graphene.Scalar):
'''Geometry WKT custom type.'''
@staticmethod
def serialize(geom):
return engine.scalar(geom.ST_AsText())
@staticmethod
def parse_literal(node):
if isinstance(node, graphql.language.ast.StringValue):
return engine.scalar(geoalchemy2.func.ST_GeomFromText(node.value))
@staticmethod
def parse_value(value):
return engine.scalar(geoalchemy2.func.ST_GeomFromText(value))
Then, you define the conversion using the convert_sqlalchemy_type decorator:
@gsqa.converter.convert_sqlalchemy_type.register(geoalchemy2.Geometry)
def _convert_geometry(thetype, column, registry=None):
return Geometry_WKT(description=gsqa.converter.get_column_doc(column),
required=not(gsqa.converter.is_column_nullable(column))
That's it! Your Geometry outputs will now be represented as WKT. You can do the same with the Geography type, instead. And, if you wish to use CoordinateJSON, just set up the CoordinateJSON conversion in the scalar type's methods, instead of using ST_AsText() or ST_GeomFromText().
Strange, I have a piece of code that used to work but doesn't anymore, I wonder what changed here (I also used Geography). I never wrote any code to do conversion.
Strange, I have a piece of code that used to work but doesn't anymore, I wonder what changed here (I also used
Geography). I never wrote any code to do conversion.
I just landed here today with the same issue: something that was working before doesn't work any more. In my case, all this mess raises when I install a 3rd party package sqlalchemy-utils. And I mean when I install, even when not using it at all in the codebase, it blows up with this graphene-sqlalchemy errors.
I am trying the code suggested by @flewellyn and am getting a long exception traceback ending with
File ".../.tox/py37/lib/python3.7/site-packages/graphql/type/typemap.py", line 87, in reducer
if type_.name in map_:
AttributeError: 'Geometry_WKT' object has no attribute 'name'
If I try adding a name attribute to the Geometry_WKT class, I get a similarly long error traceback ending with
File ".../.tox/py37/lib/python3.7/site-packages/graphql/type/typemap.py", line 127, in reducer
type_, field_name, field.type
AssertionError: LocationObject.location field type must be Output Type but got: <Geometry_WKT object at 0x7ff6829a7390>.
I am not familiar enough with the inner workings of Graphene/GraphQL to know what to do about either of these tracebacks. Can anyone confirm if this approach still works for them? Also, what is engine supposed to be?
If anyone still interested I figured out a way to get the geometry content using shapely-geojson (0.0.1) and graphene-sqlalchemy (2.3.0)
import graphene_sqlalchemy as gsqa
from shapely_geojson import dumps
from geoalchemy2.shape import to_shape
class Geometry(graphene.ObjectType):
geojson = graphene.Field(graphene.String)
srid = graphene.Int(required=True)
def resolve_srid(self, info):
return self.srid
def resolve_geojson(self, info):
return dumps(to_shape(self))
@gsqa.converter.convert_sqlalchemy_type.register(geoalchemy2.Geometry)
def _convert_geometry(thetype, column, registry=None):
return Geometry
class Address(SQLAlchemyObjectType):
class Meta:
interfaces = (relay.Node, )
model = schemas.Address # a schema with geometry = Column(Geometry(srid=4326), nullable=True)
Result is like:
"geometry": {
"geojson": "{\"type\": \"Point\", \"coordinates\": [5.605069, 47.440551]}",
"srid": 4326
}
(as far as I can tell the geojson string is matching posgraphile output).