An alias of a sqlalchemy class? - python

This is my base class
class Product(Base):
__tablename__ = 'PRODUCT'
__table_args__ = {'quote':False}
...
id = Column(Integer, name='id_prod', primary_key=True)
type = Column(String(100),name='id_typ_prod')
__mapper_args__ = {'polymorphic_on': type}
So, naturally we have a number of classes that extends from this Product, e.g. Phone and Cable, each of them maps to its own table.
class Phone (Product):
__tablename__ = 'PHONE'
...
Now for some reasons now I want to create a 'alias' class, a class that does not have a corresponding table in database. Something like this:
class VapourWare(Product):
...
If I do
class VapourWare(Product):
__tablename__ = 'PRODUCT'
__mapper_args__ = {'polymorphic_identity':'VapourWare'}
It seems to work. But is it the right or recommended way? I am repeating __tablename__ = 'PRODUCT' here.

To some degree it depends on what you're trying to achieve, but from your example it seems that what you're trying to do is called Single-Table Inheritence in the SA Docs. The example listed on the linked page seems very much like your example, with Employee == Product and Manager == VapourWare (insert Dilbert joke here).

Related

Performance issue with multiple table references to one

Database model (simplified):
class Document(Base):
__tablename__ = "document"
id = Column(Integer, primary_key=True)
...
#classmethod
def link(cls):
col = Column(Integer, ForeignKey(cls.id))
rel = relationship(cls, uselist=False, lazy='selectin', foreign_keys=[col])
return col, rel
class File(Base):
__tablename__ = "document_file"
id = Column(Integer, primary_key=True)
document_id = Column(Integer, ForeignKey(Document.id))
document = relationship( Document, back_populates="files" )
...
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
...
class B(Base):
__tablename__ = "b"
id = Column(Integer, primary_key=True)
a_id = Column(Integer, ForeignKey(A.id))
a = relationship(A, back_populates="a")
...
doc1_id, doc1 = Document.link()
doc2_id, doc2 = Document.link()
....
class C(Base):
__tablename__ = "c"
id = Column(Integer, primary_key=True)
b_id = Column(Integer, ForeignKey(B.id))
b = relationship(B, back_populates="a")
...
doc_id, doc = Document.link()
...
There is entity Document used in domain-specific entities. It has complex hierarchical structure with several one-to-many layers. It's fetched as a single root object (class A in the example), then lazy-loading of SQLAlchemy does its magic.
In a single one-to-many case SQLAlchemy detects it and loads whole list of objects in a single request to database. But in given case this optimization is not working: Document instances are fetched one-by-one. With 10k+ objects it becomes very slow.
Two ways to solve this:
Use selectinload policy for whole hierarchy. Drawback is I need a whole hierarchy after a check of several objects. Moreover, sometimes I can't use an additional query with populate_existing command because the objects are already modified.
Use SQLAlchemy mapper data to scan for references to Document and fetch it to the dictionary with a single request. But I can't just assign fetched objects to properties managed by SQLAlchemy, so this technique is not uniform with ordinary access to objects.
Two questions:
Is there some another optimization technique?
Is there a more efficient solution in terms of database model architecture?

sqlalchemy mapping multiple entries to the same backref

Perhaps this is a design issue but I have a case where I have a class that defines a default part, and alternate_parts.
class Entity(Base):
__tablename__ = "entity"
id = Column(Integer, primary_key=True)
part_id = Column(Integer, ForeignKey('part.id'))
part = relationship("Part", backref="entities")
alternate_parts = relationship("Part", secondary="associate_entity_to_parts", backref="entities")
...
class Part(Base):
__tablename__ = "part"
id = Column(Integer, primary_key=True)
...
This throws an error:
sqlalchemy.exc.ArgumentError: Error creating backref 'entities' on relationship 'Entity.alternate_parts': property of that name exists on mapper 'Mapper|Parts|part'
The purpose of this is that I want to be able to reverse look up any entities that refer to this part including ones where they are alternates. The reason for storing the alternates in this way is that this way I don't have to to store some kind of a "default" part id somewhere.
Any suggestions?

How to use a column name from mixin class inside backref(.., order_by=..) with SQLAlchemy?

I modify the Base class to include three default columns which all my tables have:
class Base(object):
id = Column(Integer, primary_key=True)
date_created = Column(DateTime, default=func.current_timestamp())
date_modified = Column(DateTime, default=func.current_timestamp(),
onupdate=func.current_timestamp())
I have a one-to-many relationship between two columns:
class User(Base):
__tablename__ = 'users'
name = Column(Text)
password = Column(Text)
items = relationship("Item", backref=
backref('user', order_by=date_modified),
cascade="all, delete, delete-orphan")
class Item(Base):
__tablename__ = 'items'
user_id = Column(Integer, ForeignKey('users.id'))
title = Column(Text)
This used to work fine, if I had date_created and date_modified columns explicitly defined within each table's class. However, when inheriting from the Base, it does not work and I get the following error:
NameError: name 'date_modified' is not defined
How can I sort the backref relationship using order_by=column_from_mixin (order_by=date_modified)?
Thank you.
You can use either of these:
backref('user', order_by=lambda: User.date_modified)
backref('user', order_by='User.date_modified')
Class attribute could be accessed using the class name, User.date_modified, but the class itself is still not defined at that point. Providing callable (the second case is converted to callable internally) postpones name resolution till the mappings are first used, at which point all the mapped classes are defined.

How can I determine the type (e.g. many-to-one) of a dynamic SQLAlchemy relationship?

Suppose I have the following SQLAlchemy classes defined:
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
computers = relationship('Computer', backref=backref('owner', lazy='dynamic'))
class Computer(Base):
__tablename__ = 'computer'
id = Column(Integer, primary_key=True)
ownerid = Column(Integer, ForeignKey('person.id'))
Suppose further that I have accessed the lazy query object this way:
relation = getattr(Computer, 'owner')
How can I determine if relation refers to a single instance of Person (that is, in a many-to-one relationship, like in this example), or if relation refers to a collection of instances (like in a one-to-many relationship)? In other words, how can I determine the relationship type of a dynamic SQLAlchemy relationship object?
If we suppose model = Computer and relation = 'owner' as in the question, then the following attribute is True if and only if the relation is a list of instances as opposed to a single instance:
model._sa_class_manager[relation].property.uselist
You can then use this to test whether or not to call the one() method on the result of getattr(model, relation):
if model._sa_class_manager[relation].property.uselist:
related_instances = getattr(model, relation)
else:
related_instance = getattr(model, relation).one()
I am not confident, however, that this is the best solution.

How to set an SQLAlchemy relationship up to access a field value instead of the related instance

The following totally incomplete snippet defines a basic SQLAlchemy relationship using declarative syntax...
Base = declarative_base()
class Movie(Base):
__tablename__ = 'movies'
id = Column(Integer, primary_key=True)
name = Column(String)
director = relationship("People", uselist = False)
class People(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String, nullable = false)
To access the director name it would be something like:
assert isinstance(movie, Movie) # <-- retrieved with query or whatever
director_name = movie.director.name
If, for convenience, I always want the director relationship to just give me the director's name, rather than a People instance, how do you do this? eg: it should work just like this:
assert isinstance(movie, Movie)
director_name = movie.director # <-- should get the string directly
I'm 99% sure I've done this before but can't find any reference code or documentation on it anymore. I'm going a bit crazy trying to locate it. Stack Overflow will be a good/permanent reference location for the answer.
The association proxy is used for all kinds of "object reference-> attribute reference" styles of transformation on the Python side. Docs have been newly updated and rewritten:
http://www.sqlalchemy.org/docs/orm/extensions/associationproxy.html
What if you use property?
class Movie(Base):
__tablename__ = 'movies'
id = Column(Integer, primary_key=True)
name = Column(String)
_director = relationship("People", uselist = False)
#property
def director_name(self):
return self._director.name

Categories

Resources