[sqlalchemy] Overwriting __init__() after class definition breaks sqlalchemy with declarative
Hi, I've written a class decorator to define a boilerplate __init__ on some of my models that inherit from a declarative_base superclass. The problem is that sqlalchemy.orm.instrumentation._generate_init() has already installed an __init__ and when I overwrite that, things break with object has no attribute '_sa_instance_state' exceptions. I've provided a sample below. It actually throws a different exception than my code but I think the root issue is the same, that is, my __init__ replaced the generated one and so the ClassManager events are not being emitted. Is there some blessed way to add an __init__ method after a class is already defined or is that just impossible with sqlalchemy's metaprogramming environment? Love the library. Very powerful and excellent documentation. Thanks. Eric $ python Python 2.7.10 (default, May 26 2015, 04:16:29) [GCC 5.1.0] on linux2 Type help, copyright, credits or license for more information. import psycopg2 import sqlalchemy psycopg2.__version__ '2.6 (dt dec pq3 ext lo64)' conn = psycopg2.connect(dbname=dispatch_dev) cur = conn.cursor() cur.execute(SELECT version();) cur.fetchone() ('PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 5.1.0, 64-bit',) sqlalchemy.__version__ '0.9.7' from collections import OrderedDict from decimal import Decimal from sqlalchemy import Column, Integer, Numeric from sqlalchemy.ext.declarative import declarative_base def licensed(licenses): ... def decorate(cls): ... def ___init__(self, **kwargs): ... for k, v in self.licenses.items(): ... kwargs.setdefault('{}_license_rate'.format(k), v) ... super(cls, self).__init__(**kwargs) ... cls.__init__ = ___init__ ... for k, v in licenses.items(): ... licenses[k] = Decimal(v) ... cls.licenses = licenses ... for license in licenses: ... setattr(cls, '{}_license_rate'.format(license), Column( Numeric, nullable=False)) ... return cls ... return decorate ... Base = declarative_base() @licensed(OrderedDict((('foo', 100), ('bar', 150 ... class Instance_Link(Base): ... __tablename__ = 'instance_link' ... id = Column(Integer, primary_key=True) ... Instance_Link(foo_license_rate=50) Traceback (most recent call last): File stdin, line 1, in module File stdin, line 6, in ___init__ File (...)/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py, line 526, in _declarative_constructor setattr(self, k, kwargs[k]) File (...)/env/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py, line 225, in __set__ self.impl.set(instance_state(instance), AttributeError: 'NoneType' object has no attribute 'set' -- 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/d/optout.
Re: [sqlalchemy] Overwriting __init__() after class definition breaks sqlalchemy with declarative
This works. Thank you for the quick response and great libraries (I use Mako as well). Eric On Fri, Aug 14, 2015 at 9:07 AM, Mike Bayer mike...@zzzcomputing.com wrote: this code is incorrect from a Python perspective. You're removing the original `__init__` method entirely and it is never called; the attempt to call it using super() just calls object.__init__.SQLAlchemy is already decorating the __init__ method of the mapped class so you can't just throw it away, you can decorate it but you need to make sure its still called. Here is a plain demonstration without any SQLAlchemy: def licensed(): def decorate(cls): def ___init__(self, **kwargs): print magic new init! super(cls, self).__init__(**kwargs) cls.__init__ = ___init__ return cls return decorate @licensed() class SomeClass(object): def __init__(self): print normal init! SomeClass() Only magic new init! is printed. SomeClass.__init__ is never called. Here's the correct way to decorate a function in this context: def licensed(licenses): def decorate(cls): orig_init = cls.__init__ def ___init__(self, **kwargs): for k, v in self.licenses.items(): kwargs.setdefault('{}_license_rate'.format(k), v) orig_init(self, **kwargs) cls.__init__ = ___init__ On 8/14/15 2:41 AM, Eric Atkin wrote: Hi, I've written a class decorator to define a boilerplate __init__ on some of my models that inherit from a declarative_base superclass. The problem is that sqlalchemy.orm.instrumentation._generate_init() has already installed an __init__ and when I overwrite that, things break with object has no attribute '_sa_instance_state' exceptions. I've provided a sample below. It actually throws a different exception than my code but I think the root issue is the same, that is, my __init__ replaced the generated one and so the ClassManager events are not being emitted. Is there some blessed way to add an __init__ method after a class is already defined or is that just impossible with sqlalchemy's metaprogramming environment? Love the library. Very powerful and excellent documentation. Thanks. Eric $ python Python 2.7.10 (default, May 26 2015, 04:16:29) [GCC 5.1.0] on linux2 Type help, copyright, credits or license for more information. import psycopg2 import sqlalchemy psycopg2.__version__ '2.6 (dt dec pq3 ext lo64)' conn = psycopg2.connect(dbname=dispatch_dev) cur = conn.cursor() cur.execute(SELECT version();) cur.fetchone() ('PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 5.1.0, 64-bit',) sqlalchemy.__version__ '0.9.7' from collections import OrderedDict from decimal import Decimal from sqlalchemy import Column, Integer, Numeric from sqlalchemy.ext.declarative import declarative_base def licensed(licenses): ... def decorate(cls): ... def ___init__(self, **kwargs): ... for k, v in self.licenses.items(): ... kwargs.setdefault('{}_license_rate'.format(k), v) ... super(cls, self).__init__(**kwargs) ... cls.__init__ = ___init__ ... for k, v in licenses.items(): ... licenses[k] = Decimal(v) ... cls.licenses = licenses ... for license in licenses: ... setattr(cls, '{}_license_rate'.format(license), Column( Numeric, nullable=False)) ... return cls ... return decorate ... Base = declarative_base() @licensed(OrderedDict((('foo', 100), ('bar', 150 ... class Instance_Link(Base): ... __tablename__ = 'instance_link' ... id = Column(Integer, primary_key=True) ... Instance_Link(foo_license_rate=50) Traceback (most recent call last): File stdin, line 1, in module File stdin, line 6, in ___init__ File (...)/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py , line 526, in _declarative_constructor setattr(self, k, kwargs[k]) File (...)/env/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py, line 225, in __set__ self.impl.set(instance_state(instance), AttributeError: 'NoneType' object has no attribute 'set' -- 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/d/optout. -- You received this message because you are subscribed to a topic in the Google Groups sqlalchemy group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/sqlalchemy/u_WdnuCSvCU/unsubscribe. To unsubscribe from this group and all its topics, send an email to sqlalchemy+unsubscr
Re: [sqlalchemy] override relationship in subclass
Yeah sorry I missed that. conversion is an attribute on Measured_Source. So the intent is that a Production_Load is a Load with its own additional attributes over Load as well as a constraint that its source is a Measured_Source which has its own attribute extensions over Source. One of the goals here is to add that constraint enforcement. I was able to make it work with the following hybrid_method and hybrid_method.expression, but the isinstance(Production_Load.source, Measured_Source) enforcement is missing. Eric {{{ class Production_Load(Load): __tablename__ = 'production_load' __mapper_args__ = { 'polymorphic_identity':'production_load' } id = Column(Integer, ForeignKey('load.id'), primary_key=True) top = Column(Numeric, nullable=False) bottom = Column(Numeric, nullable=False) @hybrid_method def delta(self): return (self.top-self.bottom)*self.source.conversion if self.source else None @gdelta.expression def delta(self): # not sure about the performance here return (self.top-self.bottom)*select([Measured_Source.conversion]).where(Measured_Source.id==self.source_id).label('delta') }}} On Wednesday, February 12, 2014 7:17:08 AM UTC-7, Michael Bayer wrote: On Feb 11, 2014, at 9:38 PM, Eric Atkin eat...@certusllc.us javascript: wrote: Hi, I want to override a relationship in a subclass to relate to a subclass of the base attributes' related class. Perhaps an example of how I thought it should work: {{{ class Load(Base): __tablename__ = 'load' __mapper_args__ = { 'polymorphic_identity':'load', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) source_id = Column(Integer, ForeignKey('source.id')) source = relationship('Source') class Production_Load(Load): __tablename__ = 'production_load' __mapper_args__ = { 'polymorphic_identity':'production_load' } id = Column(Integer, ForeignKey('load.id'), primary_key=True) source_id = Column(Integer, ForeignKey('measured_source.id')) source = relationship('Measured_Source') class Source(Base): __tablename__ = 'source' __mapper_args__ = { 'polymorphic_identity':'source', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) class Measured_Source(Source): __tablename__ = 'measured_source' __mapper_args__ = { 'polymorphic_identity':'measured_source' } id = Column(Integer, ForeignKey('source.id'), primary_key=True) }}} As you can see, we have Load.source - Source and I want Production_Load.source - Measured_Source, but when I import the models, I get the following warning: {...}/env/lib/python2.7/site-packages/sqlalchemy/orm/properties.py:1028: SAWarning: Warning: relationship 'source' on mapper 'Mapper|Production_Load|production_load' supersedes the same relationship on inherited mapper 'Mapper|Load|load'; this can cause dependency issues during flush and when I try to use Production_Load.source (class level attr) in a query, I get the following error: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Production_Load.source has an attribute 'conversion' Is such a thing possible, even with a re-factor of the models? “conversion” sounds like an attribute name on your end, but generally being able to supersede a relationship like that when the inheritance is not “concrete” is not supported. you’d need to name it to something else. -- 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.
Re: [sqlalchemy] override relationship in subclass
@gdelta.expression is a typo. Should be @delta.expression. On Wednesday, February 12, 2014 11:53:05 AM UTC-7, Eric Atkin wrote: Yeah sorry I missed that. conversion is an attribute on Measured_Source. So the intent is that a Production_Load is a Load with its own additional attributes over Load as well as a constraint that its source is a Measured_Source which has its own attribute extensions over Source. One of the goals here is to add that constraint enforcement. I was able to make it work with the following hybrid_method and hybrid_method.expression, but the isinstance(Production_Load.source, Measured_Source) enforcement is missing. Eric {{{ class Production_Load(Load): __tablename__ = 'production_load' __mapper_args__ = { 'polymorphic_identity':'production_load' } id = Column(Integer, ForeignKey('load.id'), primary_key=True) top = Column(Numeric, nullable=False) bottom = Column(Numeric, nullable=False) @hybrid_method def delta(self): return (self.top-self.bottom)*self.source.conversion if self.source else None @gdelta.expression def delta(self): # not sure about the performance here return (self.top-self.bottom)*select([Measured_Source.conversion]).where(Measured_Source.id==self.source_id).label('delta') }}} On Wednesday, February 12, 2014 7:17:08 AM UTC-7, Michael Bayer wrote: On Feb 11, 2014, at 9:38 PM, Eric Atkin eat...@certusllc.us wrote: Hi, I want to override a relationship in a subclass to relate to a subclass of the base attributes' related class. Perhaps an example of how I thought it should work: {{{ class Load(Base): __tablename__ = 'load' __mapper_args__ = { 'polymorphic_identity':'load', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) source_id = Column(Integer, ForeignKey('source.id')) source = relationship('Source') class Production_Load(Load): __tablename__ = 'production_load' __mapper_args__ = { 'polymorphic_identity':'production_load' } id = Column(Integer, ForeignKey('load.id'), primary_key=True) source_id = Column(Integer, ForeignKey('measured_source.id')) source = relationship('Measured_Source') class Source(Base): __tablename__ = 'source' __mapper_args__ = { 'polymorphic_identity':'source', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) class Measured_Source(Source): __tablename__ = 'measured_source' __mapper_args__ = { 'polymorphic_identity':'measured_source' } id = Column(Integer, ForeignKey('source.id'), primary_key=True) }}} As you can see, we have Load.source - Source and I want Production_Load.source - Measured_Source, but when I import the models, I get the following warning: {...}/env/lib/python2.7/site-packages/sqlalchemy/orm/properties.py:1028: SAWarning: Warning: relationship 'source' on mapper 'Mapper|Production_Load|production_load' supersedes the same relationship on inherited mapper 'Mapper|Load|load'; this can cause dependency issues during flush and when I try to use Production_Load.source (class level attr) in a query, I get the following error: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Production_Load.source has an attribute 'conversion' Is such a thing possible, even with a re-factor of the models? “conversion” sounds like an attribute name on your end, but generally being able to supersede a relationship like that when the inheritance is not “concrete” is not supported. you’d need to name it to something else. -- 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.
[sqlalchemy] override relationship in subclass
Hi, I want to override a relationship in a subclass to relate to a subclass of the base attributes' related class. Perhaps an example of how I thought it should work: {{{ class Load(Base): __tablename__ = 'load' __mapper_args__ = { 'polymorphic_identity':'load', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) source_id = Column(Integer, ForeignKey('source.id')) source = relationship('Source') class Production_Load(Load): __tablename__ = 'production_load' __mapper_args__ = { 'polymorphic_identity':'production_load' } id = Column(Integer, ForeignKey('load.id'), primary_key=True) source_id = Column(Integer, ForeignKey('measured_source.id')) source = relationship('Measured_Source') class Source(Base): __tablename__ = 'source' __mapper_args__ = { 'polymorphic_identity':'source', 'polymorphic_on':'polymorphic_type', } id = Column(Integer, primary_key=True) polymorphic_type = Column(Text, nullable=False) class Measured_Source(Source): __tablename__ = 'measured_source' __mapper_args__ = { 'polymorphic_identity':'measured_source' } id = Column(Integer, ForeignKey('source.id'), primary_key=True) }}} As you can see, we have Load.source - Source and I want Production_Load.source - Measured_Source, but when I import the models, I get the following warning: {...}/env/lib/python2.7/site-packages/sqlalchemy/orm/properties.py:1028: SAWarning: Warning: relationship 'source' on mapper 'Mapper|Production_Load|production_load' supersedes the same relationship on inherited mapper 'Mapper|Load|load'; this can cause dependency issues during flush and when I try to use Production_Load.source (class level attr) in a query, I get the following error: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Production_Load.source has an attribute 'conversion' Is such a thing possible, even with a re-factor of the models? Thanks, Eric -- 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.