SQLAlchemy ForeignKey can't find table - python

Getting this error when I try to instantiate the ConsumerAdvice class.
Foreign key associated with column 'tbConsumerAdvice.ConsumerAdviceCategory_ID'
could not find table 'tbConsumerAdviceCategories' with which to generate a
foreign key to target column 'ID_ConsumerAdviceCategories'
class ConsumerAdviceCategory(Base):
__tablename__ = 'tbConsumerAdviceCategories'
__table_args__ = {'schema':'dbo'}
ID_ConsumerAdviceCategories = Column(INTEGER, Sequence('idcac'),\
primary_key=True)
Name = Column(VARCHAR(50), nullable=False)
def __init__(self,Name):
self.Name = Name
def __repr__(self):
return "< ConsumerAdviceCategory ('%s') >" % self.Name
class ConsumerAdvice(Base):
__tablename__ = 'tbConsumerAdvice'
__table_args__ = {'schema':'dbo'}
ID_ConsumerAdvice = Column(INTEGER, Sequence('idconsumeradvice'),\
primary_key=True)
ConsumerAdviceCategory_ID = Column(INTEGER,\
ForeignKey('tbConsumerAdviceCategories.ID_ConsumerAdviceCategories'))
Name = Column(VARCHAR(50), nullable=False)
Category_SubID = Column(INTEGER)
ConsumerAdviceCategory = relationship("ConsumerAdviceCategory",\
backref=backref('ConsumerAdvices'))
def __init__(self,Name):
self.Name = Name
def __repr__(self):
return "< ConsumerAdvice ('%s') >" % self.Name

Define the FK including schema: dbo.tbConsumerAdviceCategories.ID_ConsumerAdviceCategories

I also hit this error. In my case the root cause was that I attempted to define different sqlalchemy base classes:
Base1 = declarative_base(cls=MyBase1)
Base1.query = db_session.query_property()
Base2 = declarative_base(cls=MyBase2)
Base2.query = db_session.query_property()
I had a ForeignKey relationship from one class that derives from Base1 to another class that derives from Base2. This didn't work -- I got a similar NoReferencedTableError. Apparently classes must derive from the same Base class in order to know about each other.
Hope this helps someone.

That didn't solve my problem, I had to use.
ConsumerAdviceCategory_ID = Column(INTEGER,
ForeignKey('tbConsumerAdviceCategories.ID_ConsumerAdviceCategories',
schema='dbo'))

Related

SQLAlchemy how to create a relationship or mapping of a table's "type" row to the table model or mixin

I need to set the entity_type_id as a column value when I persist a row to a generic table of various entity_types. I should be able to load the entity_type_id for every specific instance at instantiation time because it is accessible via a simple select statement. I'd like to have that id automatically retrieved/set at the class (or instance) level without executing a query and/or manually setting to every time I persist a row of an "entity_type".
I tried an entity_type_id #property on the mixin that returns the id of the entity_type using the object_session but for reasons I don't fully understand the orm still inserts null as the entity_type_id value when I commit/flush the session. (my guess is having the "property" itself isn't the same thing as setting the attribute value on the instance and/or causing an issue because the column name from the base class has the same name)
Here's a slimmed down version of the relevant models in my schema:
class EntityType(Base):
__tablename__ = 'entity_type'
id = Column(UUID(as_uuid=True), primary_key=True, server_default=FetchedValue())
table_name = Column(String, nullable=False)
ui_label = Column(Text, unique=True, nullable=False)
entry_key = Column(Text, unique=True, nullable=False)
Base class model:
class TrackedEntity(Base):
#declared_attr
def __tablename__(cls):
return convert(cls.__name__)
__table_args__ = (
UniqueConstraint('entity_type_id', 'label'),
)
id = Column(UUID(as_uuid=True), primary_key=True, server_default=FetchedValue())
entity_type_id = Column('entity_type_id', ForeignKey('entity_type.id'))
label = Column('label', String, nullable=False)
entity_type = relationship('EntityType')
polymorphic_discriminator = column_property(select([EntityType.table_name]).where(EntityType.id == entity_type_id).as_scalar())
#declared_attr
def entity_type_label(cls):
return association_proxy('entity_type', 'label')
#declared_attr
def __mapper_args__(cls):
if cls.__name__ == 'TrackedEntity':
return {
"polymorphic_on": cls.polymorphic_discriminator,
"polymorphic_identity": cls.__tablename__
}
else:
return {"polymorphic_identity": cls.__tablename__}
Children class mixin:
class TrackedEntityMixin(object):
# noinspection PyMethodParameters
#declared_attr
def id(cls) -> Column:
return Column(ForeignKey('tracked_entity.id'), primary_key=True)
#gets me the id but isn't very helpful like this, still needs to be manually set like child.entity_type_id = child._entity_type_id
#property
def _entity_type_id(self):
return object_session(self). \
scalar(
select([EntityType.id]).
where(EntityType.table_name == self.__tablename__)
)
A child class model:
class DesignedMolecule(TrackedEntityMixin, TrackedEntity):
extra = Column('extra', String)
parents = relationship('TrackedEntity', secondary='mix_dm_parent_entity')

SQLAlchemy - accessing columns of child model through parent model (one-to-one relation)

I have two models and one-to-one relationship between them. I would like to access columns of ItemData through Item (e.g. item = Item(); item.value). I tried to overwrote __getattr__, but this method is intensively used by SQLAlachemy base model. Any help appreciated.
class Item(Model):
__tablename__ = "item_data"
id = Column(Integer, primary_key=True)
data = relationship("ItemData", back_populates="_item", uselist=False,
foreign_keys="ItemData._item_id")
class ItemData(Model):
__tablename__ = "items_data"
id = Column(Integer, primary_key=True)
_item_id = Column(Integer, ForeignKey("items.id"))
_item = relationship("Item", back_populates="data",
foreign_keys=[_item_id])
value = Column(Integer)
I solved the problem by writing class decorator which should be apply to Item.
from sqlalchemy.ext.hybrid import hybrid_property
def direct_data_getter(cls):
'''Provide direct access to attributes of data.'''
fields = set(cls.data_cls.__mapper__.columns.keys()) - set(dir(cls))
for field in fields:
method = hybrid_property(
lambda self, field=field: getattr(self.data, field)
)
setattr(cls, field, method)
return cls

Python + SqlAlchemy 'init' with single table inheritance

got problems with a Sqlalchemy thing.
I've defined my database, relevant parts as per below...
class Person(Base):
__tablename__ = 'PERSON'
#
id = Column(Integer, primary_key=True)
person_type = Column(String(32), nullable=False)
name = Column(String(50))
address = Column(String(120))
phonenum = Column(String(20))
__mapper_args__ = {'polymorphic_on':person_type}
#
class Student(Person):
__mapper_args__ = {'polymorphic_identity': 'Student'}
dob = Column(Date)
I've other subclasses of 'Person' too. I'm having problems with the way the 'init' should look. My last attempt was..
for 'Person'..
def __init__(self, a_name, a_address, a_phonenum, a_person_type = None):
self.name = a_name
self.address = a_address
self.phonenum = a_phonenum
and for 'Student'..
def __init__ (self, a_name, a_address,a_phonenum, a_dob=None, a_stud_caregiver_id=None, a_person_type = 'Student'):
self.name = a_name
self.address = a_address
self.phonenum = a_phonenum
self.dob = a_dob
self.stud_caregiver_id = a_stud_caregiver_id
self.person_type = a_person_type
but though this creates the database ok, when I come to 'init' my students, I'm getting messages along the lines of...
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: Class object expected, got 'Table('PERSON', MetaData(bind=None)....
I've tried with/without the person_type parameter .. but really, I'm just shooting in the dark.
I've obviously done something dumb, but what? Thanks!

With SQLAlchemy, how do I make a dynamic relation?

I have a SyncEntities class (shown below).
I have several other classes (such as CommodityTypes also shown below) related to the SyncEntities class.
All of my Base subclasses have this column uuidKey = Column(String, primary_key=True)
Assume se is an instance of SyncEntities.
se.entityKind is the name of a Base subclass.
How do I query for an object that is in the se.entityKind class filtering for se.uuidKey?
class SyncEntities(Base):
__tablename__ = 'SyncEntities'
uuidKey = Column(String, primary_key=True)
dateCreated = Column(DateTime, index=True)
dateModified = Column(DateTime, index=True)
dateSynced = Column(DateTime, index=True)
username = Column(String)
entityKind = Column(String)
deleted = Column(Boolean)
def __init__(self, entity, security):
self.uuidKey = newUUID()
self.dateCreated = security.now
self.dateModified = security.now
self.dateSynced = security.then
self.username = security.username
self.entityKind = entity.__tablename__
self.deleted = False
def modified(self, security):
self.dateModified = security.now
self.username = security.username
class CommodityTypes(Base):
__tablename__ = 'CommodityTypes'
uuidKey = Column(String, ForeignKey('SyncEntities.uuidKey'), primary_key=True)
myName = Column(String, unique = True)
sortKey = Column(Integer, unique = True)
mySyncEntity = relationship("SyncEntities")
def __init__(self, security, myName, sortKey):
self.syncEntity = SyncEntities(self, security)
self.uuidKey = self.syncEntity.uuidKey
self.myName = myName
self.sortKey = sortKey
The structure here is similar, though not quite the same, as a "polymorphic association", and you can read about this pattern over at this blog post: http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/ . It's an old post but the example at http://techspot.zzzeek.org/files/2007/discriminator_on_association.py was added later as an updated example.
This case is a little different in that an object like CommodityTypes only refers to a single SyncEntities, not multiple as in the usual polymorphic association. The SyncEntities also can only refer to a single type of related object since you have entityKind on it locally.
I would note that a potential problem with this design is that you could have rows in other tables that have a uuidKey pointing to a particular SyncEntities instance, but are not of a type that matches "entityKind". If the relationship between CommodityTypes and SyncEntities is actually one-to-one, that changes everything - this pattern is really simple joined table inheritance and you'd use the patterns described at http://docs.sqlalchemy.org/en/rel_0_7/orm/inheritance.html.
You also don't have backrefs between the target and SyncEntities, which is often a way to automate these styles of lookup. But you can still approximate things using a lookup of entityKind types to classes:
def lookup_related(se):
types = {
'commodity':CommodityTypes,
'foobar':FooBarTypes
}
cls = types[se.entityKind]
session = object_session(se)
return session.query(cls).filter(cls.mySyncEntity==se).all()
here's a mixin that could do it also, using a backref:
class HasSyncEntity(object):
entity_kind = None
"subclasses need to populate this"
#declared_attr
def uuidKey(cls):
return Column(String, ForeignKey("SyncEntities.uuidKey"), primary_key=True)
#declared_attr
def mySyncEntity(cls):
return relationship("SyncEntities", backref="_%s_collection" % cls.entity_kind)
CommodityTypes becomes:
class CommodityTypes(HasSyncEntity, Base):
entity_kind = "commodity"
# ...
You then add a method like this to SyncEntities, which looks up the appropriate backref, and you're done:
def get_related(self):
return getattr(self, "_%s_collection" % self.entityKind)

SQLAlchemy: avoiding repetition in declarative style class definition

I'm using SQLAlchemy, and many classes in my object model have the same two attributes: id and (integer & primary key), and name (a string). I'm trying to avoid declaring them in every class like so:
class C1(declarative_base()):
id = Column(Integer, primary_key = True)
name = Column(String)
#...
class C2(declarative_base()):
id = Column(Integer, primary_key = True)
name = Column(String)
#...
What's a good way to do that? I tried using metaclasses but it didn't work yet.
You could factor out your common attributes into a mixin class, and multiply inherit it alongside declarative_base():
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
class IdNameMixin(object):
id = Column(Integer, primary_key=True)
name = Column(String)
class C1(declarative_base(), IdNameMixin):
__tablename__ = 'C1'
class C2(declarative_base(), IdNameMixin):
__tablename__ = 'C2'
print C1.__dict__['id'] is C2.__dict__['id']
print C1.__dict__['name'] is C2.__dict__['name']
EDIT: You might think this would result in C1 and C2 sharing the same Column objects, but as noted in the SQLAlchemy docs, Column objects are copied when originating from a mixin class. I've updated the code sample to demonstrate this behavior.
Could you also use the Column's copy method? This way, fields can be defined independently of tables, and those fields that are reused are just field.copy()-ed.
id = Column(Integer, primary_key = True)
name = Column(String)
class C1(declarative_base()):
id = id.copy()
name = name.copy()
#...
class C2(declarative_base()):
id = id.copy()
name = name.copy()
#...
I think I got it to work.
I created a metaclass that derives from DeclarativeMeta, and made that the metaclass of C1 and C2. In that new metaclass, I simply said
def __new__(mcs, name, base, attr):
attr['__tablename__'] = name.lower()
attr['id'] = Column(Integer, primary_key = True)
attr['name'] = Column(String)
return super().__new__(mcs, name, base, attr)
And it seems to work fine.

Categories