I have the following tables configured:
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String)
class Gadget(Base):
__tablename__ = "gadget"
id = Column(Integer, primary_key=True)
brand = Column(String)
class UserGadget(Base):
__tablename__ = "user_gadget"
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
gadget_id = Column(Integer, ForeignKey('gadget.id'), primary_key=True)
user = relationship("User", backref=backref('userGadgets', order_by=user_id))
gadget = relationship("Gadget", backref=backref('userGadgets', order_by=gadget_id))
class GadgetComponent(Base):
__tablename__ = "gadget_component"
id = Column(String, primary_key=True)
gadget_id = Column(Integer,ForeignKey('gadget.id'))
component_maker = Column(String)
host = relationship("Gadget", backref=backref('components', order_by=id))
class ComponentUsingMetal(Base):
__tablename__ = "component_metal"
id = Column(Integer, primary_key=True)
component_id = Column(Integer, ForeignKey('GadgetComponent.id'))
metal = Column(String)
component = relationship("GadgetComponent", backref=backref('gadgetComponentMetals', order_by=id))
On doing the following query:
session.query(User).join("userGadgets", "gadget", "components","gadgetComponentMetals").filter(ComponentUsingMetal.metal == 'iron') , component_metal gets attached to the query twice giving an error 'table name component_metal specified more than once'.
Any idea what I am doing wrong?
I traced the problem down to the following line in selectable.py:
froms = [f for f in froms if f not in toremove] This line removes the tables which are already covered by joins so that FROM clause doesn't have the same table specified more than once. The line didn't remove component_metal even though toremove had it meaning that I had two different Table objects for the same db table. And then I noticed the import for component_metal's class, ComponentUsingMetal, looked different. The others imports looked like:
from myschema import GadgetComponent
from myschema import Gadget
from python.myschema ComponentUsingMetal
As soon as fixed the import the issue went away.
Related
I'm learning flask and i'm looking to implement a many to many realtionship. I searched on internet but there are different ways,
this is what i have tried so far
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
theory_id = db.Column(db.Integer, db.ForeignKey(Course.id))
I'm not sure this is the right way to do it. I'm having issues with delete endpoint. I think that i should add on delete cascade but i don't know how to do it.
there are some sites that add relationships to the table association, so the association table looks like that.
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
course_id = db.Column(db.Integer, db.ForeignKey(Course.id))
course = db.relationship(Course, backref="course",cascade='all,
delete')
group = db.relationship(Group, backref="group", cascade='all,
delete`)
there are another examples where they are including a relationship field in both tables like that:
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
courses = db.relationship("Course", secondary=course_groups,
back_populates="courses")
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
groups = db.relationship("Group", secondary=course_groups,
back_populates="groups")
So i'm confused, which one is the most correct ?
I am new to SQLAlchemy and I am having troubles implementing the concept of relationships into my logic.
I have two tables: Offices and Departments where departments include the office it exists in.
I tried to apply the information present in the documentation https://www.tutorialspoint.com/sqlalchemy/sqlalchemy_orm_building_relationship.htm but I am having struggles understanding where should I insert the Relationship and if back_populates attribute is actually useful in this case.
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", back_populates="departments")
def __init__(self, name):
self.name = name
Also, will I have to define anything related to the office in the department's __init__ in order to save it when I try inserting new rows into the table?
You can specify them relationship in both models
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
departments = relationship("Departments", back_populates='office')
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", back_populates="departments")
After that you can achieve this (assume department and office are objects)
department.offices # this will refer to office
office.departments # this will refer to departments set (department is one of them)
The alternative is backref. It will create reverse relationship by itself, so
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", backref="departments")
will do the same the first does
I got 3 tables
class Article_Comment(Base):
__tablename__ = 'article_comment'
article_id = Column(Integer, ForeignKey('article.id'), primary_key=True)
comment_id = Column(Integer, ForeignKey('comment.id'), primary_key=True)
child = relationship("Comment", lazy="joined", innerjoin=True)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True)
title = Column(String)
children = relationship("Article_Comment", lazy="joined", innerjoin=True)
class Comment(Base):
__tablename__ = 'comment'
id = Column(Integer, primary_key=True)
text = Column(String)
I need to get specific Article with Comments. I do this like this:
Session = sessionmaker(bind=engine)
session = Session()
result = session.query(Article, Comment).join(Article_Comment).join(Comment).filter(Article_Comment.article_id == Article.id).filter(Article_Comment.comment_id == Comment.id).filter(Article.title=='music').all()
for i, j in result:
print i.title, j.text
But I want to make this query without using .join.
Can someone help me?
May be, I need to remake relationships?
My thx to univerio
Here is the full answer
set up a relationship on Article, comments = relationship(Comment, secondary=Article_Comment.__table__, lazy="joined")
session.query(Article).filter(Article.title=='music').one().comments
I have rather simple models like these:
TableA2TableB = Table('TableA2TableB', Base.metadata,
Column('tablea_id', BigInteger, ForeignKey('TableA.id')),
Column('tableb_id', Integer, ForeignKey('TableB.id')))
class TableA(Base):
__tablename__ = 'TableA'
id = Column(BigInteger, primary_key=True)
infohash = Column(String, unique=True)
url = Column(String)
tablebs = relationship('TableB', secondary=TableA2TableB, backref='tableas')
class TableB(Base):
__tablename__ = 'TableB'
id = Column(Integer, primary_key=True)
url = Column(String, unique=True)
However, sqla generates queries like
SELECT "TableB".id, "TableB".url AS "TableB_url" FROM "TableB", "TableA2TableB"
WHERE "TableA2TableB".tableb_id = "TableB".id AND "TableA2TableB".tablea_id = 408997;
But why is there a cartesian product in the query when the attributes selected are those in TableB? TableA2TableB shouldn't be needed.
Thanks
As it is right now, there is a backref relationship in TableB (tableas) and it's loaded because the default loading mode is set to select.
You may want to change the TableA.tablebs to
tablebs = relationship('TableB', secondary=TableA2TableB, backref='tableas', lazy="dynamic")
Im using sqlalchemy to design a forum style website. I started knocking out the design but everytime I try to test it with a few inserts, it dumps a brick;
NoForeignKeysError: Could not determine join condition between parent/child
tables on relationship Thread.replies - there are no foreign keys linking
these tables. Ensure that referencing columns are associated with a
ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
Here are my "models"
from sqlalchemy import Integer, Column, String, create_engine, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, backref
from .database import Base # declarative base instance
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True)
email = Column(String, unique=True)
threads = relationship("Thread", backref="user")
posts = relationship("Post", backref="user")
class Post(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True)
name = Column(String)
body = Column(String)
author = Column(Integer, ForeignKey("user.id"))
class Thread(Base):
__tablename__ = "thread"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
replies = relationship("Post", backref="thread")
author_id = Column(Integer, ForeignKey("user.id"))
board_id = Column(Integer, ForeignKey("board.id"))
class Board(Base):
__tablename__ = "board"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
threads = relationship("Thread", backref="board")
category_id = Column(Integer, ForeignKey("category.id"))
class Category(Base):
__tablename__ = "category"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
threads = relationship("Board", backref="category")
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
session_factory = sessionmaker(bind=engine)
session = session_factory()
Your Post model has no thread reference. Add a column to Post referencing the Thread a post belongs to:
class Post(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True)
name = Column(String)
body = Column(String)
author = Column(Integer, ForeignKey("user.id"))
thread_id = Column(Integer, ForeignKey('thread.id'))
We can't use the name thread because that's what the Post.replies relationship will add to retrieved Thread instances.
This is a One to Many relationship as documented in the SQLAlchemy Relationship Configuration documentation.
You should add a field in the Post model that reads:
thread_id = Column(Integer, ForeignKey("thread.id"), nullable=True, default=None)
How is SQLAlchemy supposed to know how this relationship you defined supposed to link a thhread to a post? That's why you should have a foreign key from a post to its thread. You can allow it to be null, if it does not belong to a thread, it depends on your use case.