heh, this is actually some dumb numpy thing, check this out: >>> from numpy.ma.core import exp >>> x = exp(1.0) >>> a = x == x >>> a True >>> a is True False >>>
there’s your problem, the recipe fixes if you just say this: class InexactFloat(TypeDecorator): impl = Float def compare_values(self, x, y): return bool(x == y) On Nov 19, 2013, at 8:30 PM, Michael Bayer <mike...@zzzcomputing.com> wrote: > > On Nov 19, 2013, at 8:10 PM, Seth P <spadow...@gmail.com> wrote: > >> I have an issue where, I believe due to floating-point representation >> issues, reassigning the same value to a floating-point field causes >> SQLAlchemy to think the value has been modified, and therefore emits a >> gratuitous UPDATE. (This is particularly problematic when using the >> versioning mixin, >> http://docs.sqlalchemy.org/en/rel_0_8/orm/examples.html?highlight=versioned.) >> This doesn't happen all the time, e.g. in the example below the problem >> disappears if exp is imported from math rather than from numpy.ma.core. Am I >> doing something wrong? If not, is there a way to tell SQLAlchemy that a >> particular Float column (or even all Float columns) should not be considered >> modified if the new and old values differ by less than some threshold? Is >> there a way to provide a custom comparison function used for this testing? >> Apologies if I missed something about this in the docs. > > well floating points are an inexact representation, so you are hitting that > issue. If you used Numeric() instead, the floats would be coerced to Decimal > objects on the Python side, these don’t perform as well as floats but are an > exact, immutable representation - but you still can have variability in how > these come back from SQLite. > > You can in fact change the comparison function used by the unit of work to > compare for a change, just use a TypeDecorator: > > class InexactFloat(TypeDecorator): > impl = Float > > def compare_values(self, x, y): > return round(x) == round(y) # or whatever > > class A(Base): > __tablename__ = 'a' > id = Column(Integer, primary_key=True) > value = Column(InexactFloat) > > > > > >> >> from sqlalchemy import Column, Integer, Float, create_engine >> from sqlalchemy.ext.declarative import declarative_base >> from sqlalchemy.orm import create_session >> from numpy.ma.core import exp >> >> sqlite = 'sqlite:///C:\\temp1.db' >> engine = create_engine(sqlite, echo=True) >> Base = declarative_base(bind=engine) >> >> class A(Base): >> __tablename__ = 'a' >> id = Column(Integer, primary_key=True) >> value = Column(Float) >> >> if __name__ == '__main__': >> x = exp(1.0) >> print " x = %0.60f" % x >> Base.metadata.drop_all() >> Base.metadata.create_all() >> session = create_session(bind=engine, autocommit=False) >> a = A(id=1, value=x) >> session.add(a) >> session.commit() >> print "a.value = %0.60f" % a.value >> print "session.is_modified(a) = %s" % session.is_modified(a) # False >> a.value = x >> print "a.value = %0.60f" % a.value >> print "session.is_modified(a) = %s" % session.is_modified(a) # True >> session.commit() # UPDATE >> session.close() >> >> Thanks, >> >> Seth >> >> -- >> You received this message because you are subscribed to the Google Groups >> "sqlalchemy" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to sqlalchemy+unsubscr...@googlegroups.com. >> To post to this group, send email to sqlalchemy@googlegroups.com. >> Visit this group at http://groups.google.com/group/sqlalchemy. >> For more options, visit https://groups.google.com/groups/opt_out. >
signature.asc
Description: Message signed with OpenPGP using GPGMail