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.
Related
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}
I am trying to save a list of VLAN IDs per network port and also per network circuit. The list itself is something like this:
class ListOfVlanIds(Base):
__tablename__ = 'listofvlanids'
id = Column(Integer, primary_key=True)
listofvlanids_name = Column('listofvlanids_name', String, nullable = True)
And I then have a Port
class Port(Base):
__tablename__ = 'ports'
id = Column(Integer, primary_key=True)
listofvlanids_id = Column('listofvlanids_id', ForeignKey('ListOfVlanIds.id'), nullable = True)
and a Circuit:
class Circuit(Base):
__tablename__ = 'circuits'
id = Column(Integer, primary_key=True)
listofvlanids_id = Column('listofvlanids_id', ForeignKey('ListOfVlanIds.id'), nullable = True)
Running code like this results (for me) in a sqlalchemy.exc.NoReferencedTableError error on the ForeignKey.
Looking for the error I read I should add a relationship back from the list. I haven't found a way (or an example) where I can build this from both Port and Circuit. What am I missing?
Creating a list table for Ports and Circuits just moves the problem downstream, since a VLAN ID is it's own table... I'd love to be able to use ORM, instead of having to write (a lot of) SQL by hand.
ForeignKey expects a table and column name, not model and attribute name, so it should be ForeignKey('listofvlanids.id').
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')
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()
Suppose I have 3 classes in SQLALchemy: Topic, Tag, Tag_To_Topic.
Is it possible to write something like:
new_topic = Topic("new topic")
Topics.tags = ['tag1', 'tag2', 'tag3']
Which I would like to automatically insert 'tag1', 'tag2' and 'tag3' in Tag table, and also insert the correct relationship between new_topic and these 3 tags in Tag_To_Topic table.
So far I haven't been able to figure out how to do this because of many-to-many relationship. (If it was a one-to-many, it would be very easy, SQLAlchemy would does it by default already. But this is many-to-many.)
Is this possible?
Thanks, Boda Cydo.
Fist of all you could simplify your many-to-many relation by using association_proxy.
Then, I would leave the relation as it is in order not to interfere with what SA does:
# here *tag_to_topic* is the relation Table object
Topic.tags = relation('Tag', secondary=tag_to_topic)
And I suggest that you just create a simple wrapper property that does the job of translating the string list to the relation objects (you probably will rename the relation). Your Tags class would look similar to:
class Topic(Base):
__tablename__ = 'topic'
id = Column(Integer, primary_key=True)
# ... other properties
def _find_or_create_tag(self, tag):
q = Tag.query.filter_by(name=tag)
t = q.first()
if not(t):
t = Tag(tag)
return t
def _get_tags(self):
return [x.name for x in self.tags]
def _set_tags(self, value):
# clear the list first
while self.tags:
del self.tags[0]
# add new tags
for tag in value:
self.tags.append(self._find_or_create_tag(tag))
str_tags = property(_get_tags,
_set_tags,
"Property str_tags is a simple wrapper for tags relation")
Then this code should work:
# Test
o = Topic()
session.add(o)
session.commit()
o.str_tags = ['tag1']
o.str_tags = ['tag1', 'tag4']
session.commit()