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 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 "", line 1, in > File "", line 6, in ___init__ > File > "(...)/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py" > , lin
[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 "", line 1, in File "", 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] 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 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
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 > > 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.