On Aug 13, 2010, at 10:45 AM, Dan wrote: > Unfortunately still getting the same result: > > http://paste.pocoo.org/show/249801/ > > The test snippet shows that the modified set is not actually saved to > the database.
that code snippet is not complete (doesn't create a Session, doesn't add Post to it, doesn't commit() or flush() the session but then removes it so I guess maybe its a scoped_session, don't know) so I don't actually know what you're doing. The test case below adds your assertion, uses the Session properly, and works fine. The previous test I pasted also works (if I bothered to write out a full test for it, you can be sure I ran it). from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import * Base = declarative_base() metadata = Base.metadata engine = create_engine('sqlite://', echo=True) from sqlalchemy import types class DenormalizedText(types.TypeDecorator): """ Stores denormalized primary keys that can be accessed as a set. :param coerce: coercion function that ensures correct type is returned :param separator: separator character """ impl = types.Text def __init__(self, coerce=int, separator=" ", **kwargs): self.coerce = coerce self.separator = separator super(DenormalizedText, self).__init__(**kwargs) def bind_processor(self, dialect): def process(value): if value is not None: items = [str(item).strip() for item in value] value = self.separator.join(item for item in items if item) return value return process def result_processor(self, dialect, coltype): def process(value): if not value: return set() return set(self.coerce(item) \ for item in value.split(self.separator)) return process def copy_value(self, value): return set(value) def is_mutable(self): return True class Post(Base): __tablename__ = "posts" id = Column(Integer, primary_key=True) votes = Column(DenormalizedText) def __init__(self, *args, **kwargs): super(Post, self).__init__(*args, **kwargs) self.votes = self.votes or set() Base.metadata.create_all(engine) session = sessionmaker(engine)() post = Post() assert post.votes == set([]) session.add(post) session.commit() post.votes.add(1) assert 1 in post.votes session.commit() post_id = post.id # close out transaction, session entirely, even # though commit expires everything anyway session.close() post = session.query(Post).get(post_id) assert 1 in post.votes > > On Aug 13, 3:29 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: >> On Aug 13, 2010, at 10:24 AM, Dan wrote: >> >> >> >> >> >>> On Aug 13, 3:17 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: >>>> On Aug 13, 2010, at 10:01 AM, Dan wrote: >> >>>>> I have created a custom type in order to store denormalized PKs in a >>>>> TEXT field. The idea is that the text is converted back and forth from >>>>> a set of integers: >> >>>>> http://paste.pocoo.org/show/249784/ >> >>>> this is unrelated, but the code is incorrect there, should be >> >>>> def process(value): >>>> if value is not None: >>>> items = [str(item).strip() for item in value] >>>> value = self.separator.join(item for item in items if item) >> >>>> otherwise, you must implement copy_value() on your type. Here, the value >>>> isn't being copied so there's nothing to compare to. >> >>> Yes, sorry for the typo. Realized myself once I'd posted. >> >>>> Usually you're supposed to mixin MutableType which will raise >>>> notimplemented for copy_value(). I guess still more docs are needed >>>> since you were misled by the is_mutable() method. >> >>> I've tried the same thing with the MutableType mixin with the same >>> result, i.e: >> >>> class DenormalizedText(types.TypeDecorator, types.MutableType): >> >> MutableType would be first. But again this only just so the >> NotImplementedError lets you know copy_value() is needed. I could make >> the default copy_value() raise if is_mutable() is true...though it pains me >> to add more method calls... >> >> from sqlalchemy import * >> >> from sqlalchemy.ext.declarative import declarative_base >> from sqlalchemy.orm import * >> >> Base = declarative_base() >> metadata = Base.metadata >> engine = create_engine('sqlite://', echo=True) >> >> from sqlalchemy import types >> >> class DenormalizedText(types.TypeDecorator): >> """ >> Stores denormalized primary keys that can be >> accessed as a set. >> >> :param coerce: coercion function that ensures correct >> type is returned >> >> :param separator: separator character >> """ >> >> impl = types.Text >> >> def __init__(self, coerce=int, separator=" ", **kwargs): >> >> self.coerce = coerce >> self.separator = separator >> >> super(DenormalizedText, self).__init__(**kwargs) >> >> def bind_processor(self, dialect): >> >> def process(value): >> if value is not None: >> items = [str(item).strip() for item in value] >> value = self.separator.join(item for item in items if item) >> return value >> return process >> >> def result_processor(self, dialect, coltype): >> def process(value): >> if not value: >> return set() >> return set(self.coerce(item) \ >> for item in value.split(self.separator)) >> return process >> >> def copy_value(self, value): >> return set(value) >> >> def is_mutable(self): >> >> return True >> >> class Post(Base): >> __tablename__ = "posts" >> id = Column(Integer, primary_key=True) >> votes = Column(DenormalizedText) >> >> def __init__(self, *args, **kwargs): >> super(Post, self).__init__(*args, **kwargs) >> self.votes = self.votes or set() >> >> Base.metadata.create_all(engine) >> >> session = sessionmaker(engine)() >> >> post = Post() >> post.votes.add(3) >> >> session.add(post) >> session.commit() >> >> print "-----------------------" >> post.votes.add(5) >> session.commit() >> >> >> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "sqlalchemy" group. >>> To post to this group, send email to sqlalch...@googlegroups.com. >>> To unsubscribe from this group, send email to >>> sqlalchemy+unsubscr...@googlegroups.com. >>> For more options, visit this group >>> athttp://groups.google.com/group/sqlalchemy?hl=en. > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalch...@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 post to this group, send email to sqlalch...@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.