In SQLAlchemy, I'd like to have a class that automatically creates hybrid properties to expose attributes from a specific child table. Consider this structure:
class Address(Model):
id = Column(Integer, primary_key=True)
street = Column(String)
number = Column(Integer)
valid_from = Column(DateTime)
valid_to = Column(DateTime)
person_id = Column(Integer, ForeignKey('person.id'))
person = relationship('Person', backref=backref('addresses', lazy='dynamic')
class Person(db.Model, HybridPropertyGeneratorMixin):
data_class = Address
id = Column(Integer, primary_key=True)
#property
def current_address(self):
return self.addresses.order_by(desc(Address.valid_from))[0]
#hybrid_property
def city(cls):
return self.current_address.city
#city.expression
def city(cls):
return select([Address.name]). \
where(cls.id==Address.person_id). \
where(Address.valid_to == None).as_scalar()
What I'm trying to do is define a mixin that would automatically look at the attributes of data_class and generate hybrid attributes and expressions from the data_class's attributes. For example, I want to automatically define the hybrid property and expression for city, state, street, etc.
UPDATE
Wasn't clear enough in what I originally wanted to do. See above for an update as to why I want to automatically generate hybrid properties and expressions.
You can override the special method __getattr__ to get the attribute from current_address if it's not an attribute of person.
class Person(db.Model):
# ...
def __getattr__(self, item):
return getattr(self.current_address, item)
Related
I'm trying to set a relationship attribute inside the parent model (Articles) and return the most recent object of the RelevantFlag. The Articles and RelevantFlag models are a one-to-many relationship thus my models look like this:
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
relevant_flag = db.relationship(
'RelevantFlag',
backref="Article",
lazy='dynamic'
)
# Get the latest RelevantFlag model object
def getLatestRelevance(self):
return Article.query.join(
RelevantFlag
).filter(
RelevantFlag.id == self.id
).order_by(
Article.id.desc()
).first()
def setRelevance(self, state):
# Set the boolean to what's passed in 'state'
def __repr__(self):
return '<Article {}>'.format(self.id)
class RelevantFlag(db.Model):
id = db.Column(db.Integer, primary_key=True)
article_id = db.Column(db.Integer, db.ForeignKey('article.id'))
state = db.Column(db.Boolean)
My question is, how do I write a setter to change the RelevantFlag's state attribute inside the Articles model?
If I understood correctly you want to change a column value by accessing a one-to-many relationship?
def setRelevance(self, state):
# Set the boolean to what's passed in 'state'
self.relevant_flag.state = state
I have the following objects and relations defined. This is actually quite a simple case, and I am providing all those fields just to show why I believe inhalation and injection anesthesia should be defined by two different classes.
class InhalationAnesthesia(Base):
__tablename__ = "inhalation_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
concentration = Column(Float)
concentration_unit = Column(String)
duration = Column(Float)
duration_unit = Column(String)
class TwoStepInjectionAnesthesia(Base):
__tablename__ = "twostep_injection_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
solution_concentration = Column(Float)
solution_concentration_unit = Column(String)
primary_dose = Column(Float)
primary_rate = Column(Float)
primary_rate_unit = Column(String)
secondary_rate = Column(Float)
secondary_rate_unit = Column(String)
class Operation(Base):
__tablename__ = "operations"
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('inhalation_anesthesias.id'))
anesthesia = relationship("InhalationAnesthesia", backref="used_in_operations")
I would, however, like to define the anesthetic attribute of the Operation class in such a way that any Operation object can point to either a TwoStepInjectionAnesthesia object or an InhalationAnesthesia object.
How can I do that?
I suggest you to use inheritance. It's very, very well explained in SqlAlchemy docs here and here
My recommendation is to create an Anesthesia class and make both InhalationAnesthesia and TwoStepInjectionAnesthesia inherit from it. It's your call to decide which type of table inheritance use:
single table inheritance
concrete table inheritance
joined table inheritance
The most common forms of inheritance are single and joined table,
while concrete inheritance presents more configurational challenges.
For your case I'm asuming joined table inheritance is the election:
class Anesthesia(Base)
__tablename__ = 'anesthesias'
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
# ...
# every common field goes here
# ...
discriminator = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
The purpose of discriminator field:
... is to act as the discriminator, and stores
a value which indicates the type of object represented within the row.
The column may be of any datatype, though string and integer are the
most common.
__mapper_args__'s polymorphic_on key define which field use as discriminator.
In children classes (below), polymorphic_identity key define the value that will be stored in the polymorphic discriminator column for instances of the class.
class InhalationAnesthesia(Anesthesia):
__tablename__ = 'inhalation_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'inhalation'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
class TwoStepInjectionAnesthesia(Anesthesia):
__tablename__ = 'twostep_injection_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'twostep_injection'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
Finally the Operation class may reference the parent table Anesthesia with a typical relationship:
class Operation(Base):
__tablename__ = 'operations'
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('anesthesias.id'))
anesthesia = relationship('Anesthesia', backref='used_in_operations')
Hope this is what you're looking for.
I have a one-to-one relationship between a Rule and a RuleStats object. I am trying to modify the last_updated field on the RuleStats when any field on the Rule is updated. If they were not two separate classes it would look something like this.
class Rule(Base):
__tablename__ = 'rules'
def __init__(self, **kwargs):
Base.__init__(self, **kwargs)
self.stats = RuleStats(rule=self)
id = Column(Integer, autoincrement=True, primary_key=True)
description = Column(String, nullable=False, default='')
# ... more fields
last_updated = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
But how do I do it if I have two separate objects,
class Rule(Base):
__tablename__ = 'rules'
def __init__(self, **kwargs):
Base.__init__(self, **kwargs)
self.stats = RuleStats(rule=self)
id = Column(Integer, autoincrement=True, primary_key=True)
description = Column(String, nullable=False, default='')
# ... more fields
stats = relationship('RuleStats', uselist=False, backref='rule', cascade='all, delete-orphan')
class RuleStats(Base):
__tablename__ = 'rule_stats'
def __init__(self, **kwargs):
Base.__init__(self, **kwargs)
rule_id = Column(Integer, ForeignKey('rules.id'), primary_key=True)
last_updated = Column(DateTime, default=datetime.now, nullable=False)
I think the simplest approach would be
Make function Rule.update() and do updates only through this specific function in your application. Example:
from sqlalchemy.orm.attributes import set_attribute
class Rule:
def update(self, **kwargs):
"""Update instance attributes and bump last_modified date in stats"""
for key, value in kwargs.items():
set_attribute(self, key value)
self.stats.last_updated = now()
Then:
rule.update(description="Python rocks!")
If you want to make it transparent to the user, so that RuleState is always updated, even though you poke Rule attributes directly, you could use Python properties for this. This approach, however, leads more complex code base and I could not recommend it outhand.
Alternatively, you can use database triggers to execute the update on the database side. Update trigger example for PostgreSQL.
There most likely exists an method doing this internally in advanced SQLAlchemy, but I don't have enough insight to tell where to start poking.
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.
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