SqlAlchemy relationship selecting columns - python

I am trying to find a way to select columns with an sqlalchemy relationships:
I have this two tables:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
label = Column(String)
children = relationship("Child", back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('public.parent.id'))
parent = relationship("Parent", back_populates="children")
when i use
db.query( models.Parent).first()
i get the parent object with the list of Children as i was expecting, but what i would like to do is to select only few columns like that:
db.query( models.Parent.id, models.Parent.children)
in this case it doesn't work an i get the following error:
Could not locate column in row for column Children

You can use options with load_only() function.
stmt = select(Parent)
results = await db.execute(
stmt
.options(load_only(Parent.id))
.options(selectinload(Parent.children))
)
*If you want picking some columns, just attach load_only() like this.
.options(selectinload(Parent.children).load_only(Child.id, Child.name))
Yes, I tested execute() but, query() will work.
session.query(User).options(load_only(User.name, User.fullname))
see also:
https://docs.sqlalchemy.org/en/14/orm/loading_columns.html#sqlalchemy.orm.load_only

Related

SQLAlchemy: Accessing a related field in column_property

I have two models, Parent and Child with a one-to-many relationship between them, and I'm trying to create a column_property in Child that concatenates a field from Parent with a field from Child. I know this can be done easily enough using a view, but I wanted to see if it was possible using the ORM.
My mapping looks like this:
class Parent(Base):
__tablename__ = 'parent'
id = Column(INTEGER(11), primary_key=True)
parent_name = Column(String(255), nullable=False)
class Child(Base):
__tablename__ = 'child'
id = Column(INTEGER(11), primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
child_name = Column(String(255))
parent = relationship('Parent', foreign_keys='Child.parent_id', backref='parent', lazy=False)
combined_name = column_property(
Parent.parent_name + '_' + child_name
)
__table_args__ = (
UniqueConstraint('parent_id', 'child_name', name='_parent_child_uc'),
)
With this setup, session.query(Child).all() is selecting from both Parent and Child, and joining Parent to Child because of lazy=False, and the joined table gets aliased as parent_1. I want the query to just select from Child and use a join, but if I set lazy=True it doesn't do the join. If I use parent.parent_name + '_' + child_name instead of Parent.parent_name + '_' + child_name, I get AttributeError: parent_name. Is it possible to refer to the related field parent_name through the parent relationship in the column_property, or do I need to explore another approach?

SQLAlchemy Adjacency List - Constrain parent_id not equal to ID

I'm implementing an Adjacency List in SQL Alchemy which I have working. It's the basic example here of Node. I have it working.
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('node.id'))
data = Column(String(50))
children = relationship("Node")
But, I want to enforce a constraint where the parent_id != id. That is, a row can not be its own parent. I am not sure how to enforce this. Do I need to use a #validates or is there a DB constraint I can set up on the column(s).
You could use either #validates or a db constraint. The constraint would look like this:
import sqlalchemy as sa
class Node(Base):
__tablename__ = 'node'
id = sa.Column(sa.Integer, primary_key=True)
parent_id = sa.Column(sa.Integer, sa.ForeignKey('node.id'))
data = sa.Column(sa.String(50))
children = orm.relationship("Node")
__table_args__ = (sa.CheckConstraint('parent_id != id'),)

SQLAlchemy find all duplicates in children

I've been looking for solutions to this but I couldn't find anything on finding duplicates with SQLAlchemy.
I have a parent-child type relationship, and I'm looking to find all the duplicates in the children on a specific column.
I tried iterating over each parent and counting on the column, but it gave me results that didn't make sense.
parents = session.query(parent).all()
for parent in parents:
dups = session.query(child).filter_by(parentid=parent.id).group_by(child.foo_column).count()
if dups > 0:
# do action on duplicates
How can I get the duplicate children, or is there even a single query that could return all the duplicates?
EDIT:
Table definitions:
class parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
class child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parentid = Column(Integer, ForeignKey('parent.id'))
foo_column = Column(Integer, ForeignKey('foo.id'))
parent = relationship('parent',
backref=backref('children'))
foo = relationship('foo')
The foo_column I'm interested in contains just integer id's, so a duplicate would just be where foo1.id == foo2.id.
What you are trying to achieve requires a self join. Think of how you would do it in SQL. Your query would look something like:
SELECT child_1.id as dup1, child_2.id as dup2
FROM child as child_1 JOIN child as child_2
ON child_1.parentid = child_2.parentid
WHERE child_1.foo_column = child_2.foo_column;
Translating that to SQL Alchemy is straightforward:
child_2 = aliased(child)
dups = session.query(child).
join(child_2, child.parentid == child_2.parentid).
filter(child.foo_column == child_2.foo_column).
with_entities(child.id.label('dup1'), child_2.id.label('dup2'))

Many-to-many relations from table to itself in SQLAlchemy

I'm using SQLAlchemy to represent a relationship between authors. I'd like to have authors related to other authors (coauthorshp), with extra data in the relation, such that with an author a I can find their coauthors.
How this is done between two different objects is this:
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(80))
child = relationship('Child', backref='parent_assocs')
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship('Association', backref='parent')
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
but how would I do this in my case?
The nature of a coauthorship is that it is bidirectional. So, when you insert the tuple (id_left, id_right) into the coauthorship table through a coauthoship object, is there a way to also insert the reverse relation easily? I'm asking because I want to use association proxies.
if you'd like to literally have pairs of rows in association, that is, for every id_left, id_right that's inserted, you also insert an id_right, id_left, you'd use an attribute event to listen for append events on either side, and produce an append in the other direction.
If you just want to be able to navigate between Parent/Child in either direction, just a single row of id_left, id_right is sufficient. The examples in the docs regarding this kind of mapping illustrate the whole thing.

query to hierarchical mapper

I have two models, related with many-to-many, one of them is hierarchical model:
#hierarchical model
class Tag(Base):
__tablename__ = "tags"
id = Column(Integer, primary_key=True)
name = Column(String)
Tag.parent_id = Column(Integer, ForeignKey(Tag.id, ondelete='CASCADE'))
Tag.childs = relationship(Tag, backref=backref('parent', remote_side=[Tag.id]),
cascade="all, delete")
class Subject(Base):
__tablename__ = "subjects"
id = Column(Integer, primary_key=True, doc="ID")
name = Column(String)
tags = relationship(Tag, secondary="tags_subjects", backref="subjects")
#many-to-many relations model
class TagsSubjects(Base):
__tablename__ = "tags_subjects"
id = Column(Integer, primary_key=True)
tag_id = Column(Integer, ForeignKey("tags.id"))
subject_id = Column(Integer, ForeignKey("subjects.id"))
So, I'll try to explain what I want to do... I want to make one (or several) query, for search all Subject's objects,
that have 'name' field value like 'foo' OR that has related tags having names with values like 'foo'
OR that has related tags, that has one or more parents (or above by hierarchy) tag with 'name' value like 'foo'
I've tried to do somethis like this:
>>> subjects = session.query(Subject).filter(or_(
Subject.name.ilike('%{0}%'.format('foo')),
Subject.tags.any(
Tag.name.ilike('%{0}%'.format('foo')))
)).order_by(Subject.name).all()
But it isn't correct and "flat" query, without hierarchical feature :(
How to do this by SQLAlchemy's API?
Thanks!
P.S. I'm using SQLite backend

Categories