AH ok thanks for the tip. I'll poke around with that and see what I can 
come up with.


Thanks for your help

On Wednesday, June 13, 2012 3:11:56 PM UTC-4, Michael Bayer wrote:
>
> there's not a lot of moving parts in SQLA here.  the first step would be, 
> figure out how this needs to render with psycopg2 directly.  If you can get 
> it to work with psycopg2 alone, passing the desired value as a bound 
> parameter, then we can work out what the type engine needs to do or not.
>
>
> On Jun 13, 2012, at 3:07 PM, Tom Willis wrote:
>
> Well I had written another type to emulate appengines Key, basically an 
> object that will serialize/deserialize itself to json when asked and what I 
> thought were all the mechanics needed for type coercion in sa.
>
>
>     from sqlalchemy import types as sqltypes
>     from sqlalchemy import UnicodeText
>     import json
>
>     class Key(object):
>         """
>         identifier very similar to appengines key
>     
>         serialization via json
>     
>         _provider is set when session is created and we're assuming it's 
> done once
>         """
>         _provider = None
>     
>         def __init__(self, kind, id, ns=None):
>             self._identity = dict(kind=str(kind),
>                                   id=int(id),
>                                   ns=ns)
>     
>         @property
>         def kind(self):
>             return self._identity["kind"]
>     
>         @property
>         def id(self):
>             return self._identity["id"]
>     
>         @property
>         def ns(self):
>             return self._identity["ns"]
>     
>         def __repr__(self):
>             return "Key(%s)" % self.serialize()
>     
>         def serialize(self):
>             return json.dumps(self._identity)
>     
>         @classmethod
>         def deserialize(cls, k):
>             value = json.loads(k)
>             return cls(**value)
>     
>         def get(self):
>             assert self._provider, "_provider not set"
>             return self._provider.get_for_key(self)
>     
>     class KEY(sqltypes.TypeEngine):
>         """
>         Key Ref type for use in columns
>         """
>         __visit_name__ = UnicodeText.__visit_name__
>
>         def __init__(self, kind=None):
>             self._kind = kind
>     
>         def _assert_kind(self, value):
>             if value and self._kind:
>                 assert value.kind == self._kind,\
>                        "%s != %s" % (value.kind, self._kind)
>     
>         def bind_processor(self, dialect):
>             def x(value):
>                 self._assert_kind(value)
>                 if value is not None:
>                     return value.serialize()
>                 else:
>                     return value
>             return x
>     
>         def result_processor(self, dialect, coltype):
>             def x(value):
>                 self._assert_kind(value)
>                 if value is not None:
>                     return Key.deserialize(value)
>                 else:
>                     return None
>             return x
>
>
> So that + the code I wrote previously I figured I could do something like 
> this...
>
>
>         class T(B):
>             __tablename__ = "t"
>             id = Column(Integer, primary_key=True)
>             v = ListColumn(KEY)
>
>         kv = Key(**dict(kind=B.__name__, id=1, ns=1))
>         kv2 = Key(**dict(kind=B.__name__, id=2, ns=2))
>         t = T()
>         t.v = [kv, kv2]
>         S.add(t)
>         S.commit()
>
>         self.assert_(S.query(T).filter_by(v=kv).count())
>         self.assert_(S.query(T).filter_by(v=kv2).count())
>
>
> Running this I get a statement error because whats passed in to the 
> ListComparator is str(Key) not Key.serialize() which I thought would have 
> happened due to the KEY class.
>
> E       DataError: (DataError) malformed array literal: "{Key({"kind": 
> "Base", "ns": 1, "id": 1})}"
> E       LINE 4: WHERE t.v && '{Key({"kind": "Base", "ns": 1, "id": 1})}') 
> AS...
> E                            ^
> E        'SELECT count(*) AS count_1 \nFROM (SELECT t.v AS t_v, t.id AS 
> t_id \nFROM t \nWHERE t.v && %(v_1)s) AS anon_1' {'v_1': u'{Key({"kind": 
> "Base", "ns": 1, "id": 1})}'}
>
>
> if I temporarily make the comparator call serialize() on what is passed in 
> I get a different DataError
>
>     def do_execute(self, cursor, statement, parameters, context=None):
> >       cursor.execute(statement, parameters)
> E       DataError: (DataError) malformed array literal: "{{"kind": "Base", 
> "ns": 1, "id": 1}}"
> E       LINE 4: WHERE t.v && '{{"kind": "Base", "ns": 1, "id": 1}}') AS 
> anon...
> E                            ^
> E        'SELECT count(*) AS count_1 \nFROM (SELECT t.v AS t_v, t.id AS 
> t_id \nFROM t \nWHERE t.v && %(v_1)s) AS anon_1' {'v_1': u'{{"kind": 
> "Base", "ns": 1, "id": 1}}'}
>
>
> So now it's not escaped properly which I guess I can accept but it seems 
> like I'm doing it wrong and really there's some other piece that should 
> know how to make an array literal out of what is passed to it somewhere. 
> But as I said in my original email, I don't think I fully grok all the 
> moving parts in sqla. 
> On Wednesday, June 13, 2012 2:34:38 PM UTC-4, Michael Bayer wrote:
>>
>> I wouldn't be too optimistic about an SO response on this one as the 
>> "guts of SQLA" questions all hang out there until I answer them anyway.
>>
>> haven't had time to play with this, but if the question regards the 
>> "insides" of the ARRAY, ARRAY accepts another SQLAlchemy data type as the 
>> argument for the elements, which does whatever is needed to get the type 
>> along to the database.
>>
>> psycopg2 itself may or may not handle all the built-in types correctly 
>> here; for example, an ARRAY of dates or intervals might have quirks.   
>> psycopg2 also has its own type coercion customization mechanics which may 
>> or may not need to be involved.
>>
>> What specifically is the type that is failing ?
>>
>>
>>
>> On Jun 13, 2012, at 2:24 PM, Tom Willis wrote:
>>
>> I went ahead and asked this on stackoverflow if anyone would like to have 
>> a stab at it.
>>
>>
>> http://stackoverflow.com/questions/11021020/emulating-appengine-list-property-with-postgresql-array-and-sqlalchemy
>>
>> On Tuesday, June 12, 2012 7:34:42 AM UTC-4, Tom Willis wrote:
>>>
>>> Hello, 
>>> I'm hoping that some of the functionality I actually like in appengine 
>>> datastore can be duplicated in postgresql via sqlalchemy. However I'm not 
>>> quite grokking how all the moving pieces for a dialect(if that's the right 
>>> term) fit together.
>>>
>>> On appengine there are list properties or repeated properties. This is 
>>> simply an array of values that has a custom behavior for the "=" in a 
>>> query. In that an object with a property named my_list who's value is 
>>> [1,2,3] will be true in the following where clauses...
>>>
>>> where my_list=1
>>> where my_list=2
>>> where my_list=3
>>>
>>> I feel like it is possible to get the same kind of behavior on 
>>> postgresql with the ARRAY column type, but I'm having trouble finding 
>>> examples of how one might do this.
>>>
>>> Here's the code I have so far.
>>>
>>> from sqlalchemy import Column
>>> from sqlalchemy.dialects.postgresql import ARRAY
>>> from sqlalchemy.orm.properties import ColumnProperty
>>> from sqlalchemy.orm import column_property
>>>
>>>
>>>
>>> class ListComparator(ColumnProperty.ColumnComparator):
>>>     """
>>>     other operators as they make sense
>>>
>>>     optimization: override set operators for one filter instead of
>>>     multiples and'd
>>>
>>>     todo: non-string types?
>>>     """
>>>     def __eq__(self, other):
>>>         return self.__clause_element__().op("&&")(u"{%s}" % other)
>>>
>>>
>>> def ListColumn(*args, **kw):
>>>     """
>>>     makes a column of an array of types args[0]
>>>     and uses ListComparator to emulate appengine list property
>>>     """
>>>     if not isinstance(args[0], ARRAY):
>>>         largs = list(args)
>>>         largs[0] = ARRAY(args[0])
>>>         args = tuple(largs)
>>>     else:
>>>         raise ValueError("%s is an array which is not allowed" % args[0])
>>>
>>>     return column_property(Column(*args, **kw),
>>>                            comparator_factory=ListComparator)
>>>
>>>
>>> example usage....
>>>
>>>
>>> class T(B):
>>>     __tablename__ = "t"
>>>     id = Column(Integer, primary_key=True)
>>>      v = ListColumn(Integer)
>>>
>>>
>>> t = T()
>>> t.v = [1, 2]
>>> S.add(t)
>>> S.commit()
>>> S.query(T).count()
>>> S.query(T).filter(T.v==1).count()
>>> S.query(T).filter_by(v =1).count()
>>>
>>>
>>>
>>>
>>> An array of strings or ints works just fine for this, but any other data 
>>> types I would have to convert to strings and in some cases escape for the 
>>> array literal syntax in postgresql. It seems like I'm missing some piece of 
>>> the puzzle in regards to how all the types in sqla relate to each other for 
>>> queries. Is it the dialect the drives that, is there behavior there for 
>>> ARRAY that I'm somehow missing or trying to duplicate?
>>>
>>> Thanks in advance for any information you have. 
>>>
>>>
>>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msg/sqlalchemy/-/wLflA-VmBmEJ.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> To unsubscribe from this group, send email to 
>> sqlalchemy+unsubscr...@googlegroups.com.
>> For more options, visit this group at 
>> http://groups.google.com/group/sqlalchemy?hl=en.
>>
>>
>>
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To view this discussion on the web visit 
> https://groups.google.com/d/msg/sqlalchemy/-/qX1bnfLFB5MJ.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/sqlalchemy/-/MS2M9g2uwrMJ.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to