With this models:
class Ball(Base):
__tablename__ = 'balls'
id = Column(Integer, primary_key=True)
field_id = Column(Integer, ForeignKey('fields.id'))
field = relationship("Field", back_populates="fields")
class Field(Base):
__tablename__ = 'fields'
id = Column(Integer, primary_key=True)
nickname = Column(String)
fields= relationship("Ball", order_by=Ball.id, back_populates="field")
I'm trying to write query to access Field.nickname and Ball.field_id. With use of
result = session.query(Field, Ball).all()
print(result)
I'm able to retrieve
(<Field(id=1, nickname=stadium>, <Ball(id=1, field_id=newest)>), (<Field(id=1, nickname=stadium>, <Ball(id=2, field_id=oldest)>
but when using
result = session.query(Field).join(Ball).all()
print(result)
I'm getting empty list [], doesn't matter if I'm applying any filters or not. According to official docs, join would come in handy if dealing with two tables, so this way I would be able to filter for the same id in both and I guess it would came in handy when displaying query results in jinja template.
You have a typo in your Field class - back_populates='fields' doesn't match up with the attribute name in Ball.
Try changing it to back_populates='field' so that it matches the attribute defined in Ball. With the relationship defined correctly sqlalchemy should be able to do the join automatically.
Related
Query Statement: Get Children that have their Names start with 'A'. Link Them With their Parents.
Schema:
I Have a One-to-Many Relationship. Parent(id, Name, children(rel->child)) -> Child(id, Name)
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
Name = Column(String)
children = relationship("Child", lazy='joined')
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
Name = Column(String)
Want to Filter the Relationship Object. (ie, the List of Objects you get when an instantiated Parent.children) is accessed.
Eg: [Parent(id=1, Name='Parent1' children=[Child(id='1', Name='AChild1'), Child(id='2', Name='Child2')] Needs to be Filtered to: [Parent(id=1, Name='Parent1' children=[Child(id='1', Name='AChild1')] when the resulting query is accessed.
How do I write a Statement to get the Above Result?
A solution that Filters them Once Loaded, I want to Filter them While Loading.
Perhaps I should've googl-fu'ed Harder, but this was the result of some searching.
From SQLAlchemy Documentation:
When we use contains_eager(), we are constructing ourselves the SQL that will be used to populate collections. From this, it naturally follows that we can opt to modify what values the collection is intended to store, by writing our SQL to load a subset of elements for collections or scalar attributes.
Resulting in the Statement:
db.Query(Parent).join(Parent.children)\
.filter(Parent.children.any(Child.Name.like("A%")))\
.options(contains_eager(Parent.children))
I am setting up a Sqlalchemy mapper for a sqlite database. My User class has a non-nullable relationship with my Team class. The code I already have is as follows:
class Team(Base):
__tablename__ = 'teams'
team_id = Column(Integer, primary_key=True)
# Using Integer as holder for boolean
is_local = Column(Integer, default=0)
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
team_id = Column(Integer, ForeignKey(Team.team_id), default=1, nullable=False)
team = relationship('Team')
is_local = Column(Integer, default=0)
I would like to establish that the value of User.is_local is by default the value of Team.is_local for the User's linked team.
However, after the creation of the User, I would still like the ability to modify the user's is_local value without changing the values of the team or any other user on the team.
So if I were to execute
faraway = Team(is_local=1)
session.add(faraway)
session.commit()
u = User(team=faraway)
session.add(u)
session.commit()
print(bool(u.is_local))
The result should be True
So far, I have tried context-sensitive default functions as suggested by https://stackoverflow.com/a/36579924, but I have not been able to find the syntax allowing me to reference Team.is_local
Is there a simple way to do this?
The first suggestion from SuperShoot, using a sql expression as the default appears to work. Specifically,
is_local = Column(Integer, default=select([Team.is_local]).where(Team.team_id==team_id))
gives me the logic I require.
I used SQLAlchemy to create a SQLite database which stores bibliographic data of some document, and I want to query the author number of each document.
I know how to do this in raw SQL, but how can I achieve the same result using SQLAlchemy? It is possible without using join?
Here is the classes that I have defined:
class WosDocument(Base):
__tablename__ = 'wos_document'
document_id = Column(Integer, primary_key=True)
unique_id = Column(String, unique=True)
......
authors = relationship('WosAuthor', back_populates='document')
class WosAuthor(Base):
__tablename__ = 'wos_author'
author_id = Column(Integer, primary_key=True, autoincrement=True)
document_unique_id = Column(String, ForeignKey('wos_document.unique_id'))
document = relationship('WosDocument', back_populates='authors')
last_name = Column(String)
first_name = Column(String)
And my goal is to get the same result as this SQL query does:
SELECT a.unique_id, COUNT(*)
FROM wos_document AS a
LEFT JOIN wos_author AS b
ON a.unique_id = b.document_unique_id
GROUP BY a.unique_id
I tried the codes below:
session.query(WosDocument.unique_id, len(WosDocument.authors)).all()
session.query(WosDocument.unique_id, func.count(WosDocument.authors)).all()
The first line raised an error, the second line doesn't give me the desired result, it return only one row and I don't recognize what it is:
[('000275510800023', 40685268)]
Since WosDocument Object has a one-to-many relationship authors, I supposed that I can query the author number of each document without using join explicitly, but I can't find out how to do this with SQLAlchemy.
Can you help me? Thanks!
If you have written the right relation in your model. Then the query would be like:
db.session.query(ParentTable.pk,func.count('*').label("count")).join(Childtable).group_by(ParentTable).all()
The detail of the document of the join() is
https://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.join
If you don't join() explictly you would need to deal with something like parent.relations as a field.
I would like to build a relationship between instances of a single table in Sqlalchemy using the declarative method.
I have a table called Hex, which represent hexagons on a map. I would like a table called Link which links two hexes together. The backref "links" on each instance should return at most 6 links, meaning a unique constraint on the hex edge type. Additionally, I'm trying to use the association object so I can have methods on this link class.
So:
Given Hex A and Hex B
A -> B (A.links should contain a link to B)
B -> A (B.links should contain the same link object to A)
My question:
What kind of relationship is this called? I've tried other methods but this example I've demonstrated doesn't have a parent child relationship.
Edit:
I tried this configuration:
class Link(Base, Record):
__tablename__ = 'link'
id = Column(Integer, primary_key=True)
hex_a_id = Column(Integer, ForeignKey('hex.id'), index=True)
hex_a = relationship('Hex', backref=backref('links', lazy='dynamic'), foreign_keys='Link.hex_a_id')
hex_b_id = Column(Integer, ForeignKey('hex.id'), index=True)
hex_b = relationship('Hex', backref=backref('links', lazy='dynamic'), foreign_keys='Link.hex_b_id')
type = Column(PythonEnum(HexEdge, 'id'), nullable=False)
__table_args__ = (UniqueConstraint('hex_a_id', 'hex_b_id', name='_link_uc'), )
and got this error:
sqlalchemy.exc.ArgumentError: Error creating backref 'links' on relationship 'Link.hex_b': property of that name exists on mapper 'Mapper|Hex|hex'
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.