How to define grandparent for adjacency relationships - sqlalchemy

class Node(Base):
...
id = Column(Numeric, primary_key=True)
parent_id = Column(Numeric, ForeignKey('nodes.id'))
parent = relation("Node", lazy="joined", remote_side=id)
grandparent = ?
I can not use hybrid_property because it doesn't define a target mapper class.
How can I set grandparent relation with column_property, relation, association_proxy, or other way?
Thanks in advance for any response.

It's works on SQLAlchemy==0.7.9
class Node(Base):
...
grandparent_id = association_proxy('parent','parent_id')
grandparent = association_proxy('parent','parent')

Related

light request version in sqlalchemy

I have the following models:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", backref="parent")
...
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
...
There are more attributes for each class. The children are always created first. In my api, I want to send POST requests that creates a Parent with x amount of children. For just a few children, I want the complete child object with all attributes to be in the request body. But there could be a use case where a parent has 100 children. For those, I want to implement a "light" version, where in the request body there would only be the id of each child, instead of the entire object with all attributes for each child.
Is there a built-in way or an example or an official term for such a behavior? I was googling light requests sqlalchemy but choulnd't really find much.
I think that there is not built-in mechanism of what you desire, because it is not a responsibility of a request how your objects are serialized. For your case I would implement something like that:
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
other_attr = Column(String)
def to_dict(self, is_light = False):
if is_light:
return {"id": seld.id}
else:
return {"id": self.id, "other_attr": self.other_attr}

Query for a specific child of a parent in SQLAlchemy

I'm trying to write a query that will return exactly one child object from a parent.
My model is as follows:
class Parent (Base):
__tablename__ = 'parents'
id = Column(Integer, primary_key=True)
name = Column(String)
children_id = Column(Integer, ForeignKey('children.id'))
childern = relationship('Child', back_populates='parents')
class Child (Base):
__tablename__ = 'children'
id = Column(Integer, primary_key=True)
name = Column(String)
parent = relationship('Parent', back_populates='children')
Consider this example: there are three parents with their children:
Alice: Zander, Ygrite, Xaiver
Bob: Will, Ulric, Tammy
Chris: Will, Sam, Xaiver
Notice that Chris has coincidentally named some of his children the same as the others, even though they are distinct children.
If I want to find specifically Chris's child Xavier my first instinct is to either query for Parent Chris then iterate over his children until I find Xavier or to query for all children named Xavier then to iterate over them until I find the one with parent Chris.
However, this solution does not scale and definitely doesn't use SQLAlchemy's capabilities.
In continuing to research this challenge I came across this question and the accepted response: SQLAlchery query filter on child attribute
I tested this query...
s.query(Child).join(Parent, Child.parent).filter(Child.name == 'Xavier').filter(Parent.name == 'Chris')
...and it does exactly what I want it to do.

SQLAlchemy, fields for private variables

I am using a class with property and setter decorators. I'm wondering why I must reference the private variable name _child instead of child in the example below. Shouldn't I indirectly access _child through the setter/getter when accessing child
Minimal example below:
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parents'
id = Column(Integer, primary_key=True)
child = relationship('Child', back_populates='parent')
child_id = Column(Integer,ForeignKey('child.id'))
def __init__(self):
self._child= Child()
#property
def child(self):
return self._child
#child.setter
def child(self, child):
print('child set')
self._child= child
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent = relationship('Parent', back_populates='child')
def __init__(self):
pass
if __name__ == '__main__':
child = Child()
parent = Parent()
parent.child = child
This throws the following error:
sqlalchemy.exc.InvalidRequestError: Mapper 'Mapper|Parent|parents' has no property 'child'
If I replace
child = relationship('Child', back_populates='parent')
# and
parent = relationship('Parent', back_populates='child')
with
_child = relationship('Child', back_populates='parent')
# and
parent = relationship('Parent', back_populates='_child')
everything works
You're shadowing the relationship child with a property of the same name. It's the same as if you didn't have the relationship at all, so back_populates="child" cannot find a relationship called child, because it doesn't exist.
Judging by your example alone, you don't need the property at all.

Sqlalchemy event listener and relation issue

I have a problem of using event listener with the relation model, my model class is a self referenced table:
class Distributor(Base):
__tablename__ = "distributors"
id = Column(Integer, primary_key=True)
name = Column(String, nullable = False)
upline_id = Column(Integer, ForeignKey('distributors.id'))
upline = relationship('Distributor', remote_side=id, backref=backref('downlines'))
and I'm tring to register a listener on the event of adding to the downlines collection:
def my_append_listener(target, value, initiator):
branch_develop = len(target.downlines)
and this line:
event.listen(Distributor.downlines, 'append', my_append_listener)
will gives an error: AttributeError: type object 'Distributor' has no attribute 'downlines'
but it is ok to write something like:
george = Distributor("george", None)
george.downlines = [Distributor("downlineUser")]
and I also found that if I rewrite the relationship to this:
downlines = relationship('Distributor', backref=backref('upline', remote_side=id))
everything runs perfectly. Could someone tell me what's wrong in the code?
The downlines backref only exists as an attribute on an instance of the Distributor model. It is not an attribute of the model definition which you use as the target for your listener.
Fortunately, it doesn't make much difference which side you set the listener on. Defining the relationship on the parent (downlines) and listening for the append event will fire your handler whether you append to the downlines or set the upline of a Distributor instance.

SQLAlchemy only loads collection, not backref when eagerloading

For example (eagerload/joinedload do the same thing):
session = Session()
parents = session.query(Parent).options(joinedload(Parent.children)).all()
session.close()
print parents[0].children # This works
print parents[0].children[0].parent # This gives a lazy loading error
Adding the following loop before closing the session works (and doesn't hit the DB):
for p in parents:
for c in p.children:
c.parent
Which is pretty dumb. Is there a way to alter the original query so that it loads both sides of the relation without adding more joins in the output SQL?
update In case it's relevant; here's the mapping
class Parent(Entity):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
children = relation("Child", backref="parent")
class Child(Entity):
__tablename__ = "child"
id = Column(Integer, primary_key=True)
parentId = Column(Integer, ForeignKey("parent.id"), index=True)
That's what contains_eager() option is for. Try the following:
parents = session.query(Parent).options(joinedload(Parent.children),
contains_eager('children.parent')).all()