This question already has an answer here:
Creating container relationship in declarative SQLAlchemy
(1 answer)
Closed 3 years ago.
I have already finished a good bit of my python/Elixir interface on my existing database. I am now considering to drop Elixir and move everything into pure SQLAlchemy, most likely wanting to use Declarative methods.
I am not sure where to even start with this particular inheritance relationship. I don't think sqlalchemy performs inheritance in this manner (or as "magically"), and I am a bit confused how the same would look in sqlalchemy:
This is a polymorphic multi-table join, with each class mapped to its own database table. When finished, another class (not included here) will have a OneToMany with 'Comp'. The Comp subclasses have a Primary Key that is a Foreign key to Comp.id.
class Comp(Entity):
using_options(inheritance='multi')
parent = ManyToOne('Assembly', onupdate='cascade', ondelete='set null')
quantity = Field(Numeric(4), default=1)
def __repr__(self):
return "<Comp>"
## If not familiar with Elixir, each of the following "refid" point to a different
## table depending on its class. This is the primary need for polymorphism.
class CompAssm(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Assembly', onupdate='cascade', ondelete='set null')
def __repr__(self):
return "<CompAssm>"
class CompItem(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Item', onupdate='cascade')
def __repr__(self):
return "<CompItem>"
class CompLabor(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Labor', onupdate='cascade')
def __repr__(self):
return "<CompLabor>"
I think this is the general direction, but may still need tweaking.
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Assembly(Base):
__tablename__ = 'assembly'
assm_id = Column(Integer, primary_key=True)
children = relationship('Comp')
### other assembly stuff
class Comp(Base):
__tablename__ = 'components'
id = Column(Integer, primary_key=True)
comp_type = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': comp_type}
parent = Column(Integer, ForeignKey('assembly.assm_id'))
quantity = Column(Integer)
class CompAssm(Comp):
__tablename__ = 'compassm'
__mapper_args__ = {'polymorphic_identity': 'compassm'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('assembly.assm_id'))
class CompItem(Comp):
__tablename__ = 'compitem'
__mapper_args__ = {'polymorphic_identity': 'compitem'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('items.id'))
class CompLabor(Comp):
__tablename__ = 'complabor'
__mapper_args__ = {'polymorphic_identity': 'complabor'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('labors.id'))
Related
Using SQLAlchemy I have three models: Parent1, Parent2, and Child, where Parent1 has one-to-one relationship with Parent2, and both of them has the same relationship with Child. Here are they:
from extensions import db_session
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", backref="parent1", uselist=False)
child = relationship("Child", backref="parent1", uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
child = relationship("Child", backref="parent2", uselist=False)
class Child(Base):
id = Column(Integer, primary_key=True)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
parent2_id = Column(Integer, ForeignKey('parent2.id'))
What I am trying to achieve is to fill Child table with its parents' foreign keys.
So, when I execute this:
parent1 = Parent1(name="Adil")
parent1.parent2 = Parent2(name="Aisha")
parent1.child = Child()
db_session.add(parent1)
db_session.commit()
to the parents tables it inserts data as needed, however to the Child table it inserts data like this:
Child
id parent1_id parent2_id
1 1 NULL
How to properly set relationships, so that on any insert to the Parent1->Parent2 tables it also inserts its ids as foreign keys to Child table?
What I want to achieve is:
Child
id parent1_id parent2_id
1 1 1
I came up with this solution. I removed declared model Child and created association table between two models and added secondary parameter filling with assocation table name:
Base = declarative_base()
child = Table('child', Base.metadata,
Column('parent1_id', Integer, ForeignKey('parent1.id')),
Column('parent2_id', Integer, ForeignKey('parent2.id')))
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", secondary='child', uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
I have the following objects and relations defined. This is actually quite a simple case, and I am providing all those fields just to show why I believe inhalation and injection anesthesia should be defined by two different classes.
class InhalationAnesthesia(Base):
__tablename__ = "inhalation_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
concentration = Column(Float)
concentration_unit = Column(String)
duration = Column(Float)
duration_unit = Column(String)
class TwoStepInjectionAnesthesia(Base):
__tablename__ = "twostep_injection_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
solution_concentration = Column(Float)
solution_concentration_unit = Column(String)
primary_dose = Column(Float)
primary_rate = Column(Float)
primary_rate_unit = Column(String)
secondary_rate = Column(Float)
secondary_rate_unit = Column(String)
class Operation(Base):
__tablename__ = "operations"
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('inhalation_anesthesias.id'))
anesthesia = relationship("InhalationAnesthesia", backref="used_in_operations")
I would, however, like to define the anesthetic attribute of the Operation class in such a way that any Operation object can point to either a TwoStepInjectionAnesthesia object or an InhalationAnesthesia object.
How can I do that?
I suggest you to use inheritance. It's very, very well explained in SqlAlchemy docs here and here
My recommendation is to create an Anesthesia class and make both InhalationAnesthesia and TwoStepInjectionAnesthesia inherit from it. It's your call to decide which type of table inheritance use:
single table inheritance
concrete table inheritance
joined table inheritance
The most common forms of inheritance are single and joined table,
while concrete inheritance presents more configurational challenges.
For your case I'm asuming joined table inheritance is the election:
class Anesthesia(Base)
__tablename__ = 'anesthesias'
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
# ...
# every common field goes here
# ...
discriminator = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
The purpose of discriminator field:
... is to act as the discriminator, and stores
a value which indicates the type of object represented within the row.
The column may be of any datatype, though string and integer are the
most common.
__mapper_args__'s polymorphic_on key define which field use as discriminator.
In children classes (below), polymorphic_identity key define the value that will be stored in the polymorphic discriminator column for instances of the class.
class InhalationAnesthesia(Anesthesia):
__tablename__ = 'inhalation_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'inhalation'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
class TwoStepInjectionAnesthesia(Anesthesia):
__tablename__ = 'twostep_injection_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'twostep_injection'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
Finally the Operation class may reference the parent table Anesthesia with a typical relationship:
class Operation(Base):
__tablename__ = 'operations'
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('anesthesias.id'))
anesthesia = relationship('Anesthesia', backref='used_in_operations')
Hope this is what you're looking for.
I'm quite new to SQLAlchemy (and I do not have much experience with databases in general). I'm trying to traversere two many-to-many relationships. Given a parent, how can I get all unique grandchildren?
parent_child_table = Table('parent_child', Base.metadata,
Column('parent_id', Integer, ForeignKey('parent.id')),
Column('child_id', Integer, ForeignKey('child.id'))
)
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=parent_child_table,
backref="parents")
child_grandchild_table = Table('child_grandchild', Base.metadata,
Column('child_id', Integer, ForeignKey('child.id')),
Column('grandchild_id', Integer, ForeignKey('grandchild.id'))
)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
grandchildren = relationship("Grandchild",
secondary=child_grandchild_table,
backref="children")
class Grandchild(Base):
__tablename__ = 'grandchild'
id = Column(Integer, primary_key=True)
Thanks! This problem is giving me a headache...
The most straight-forard way:
# my_parent = ... (instance of Parent)
q = (session.query(Grandchild)
.join(Child, Grandchild.children)
.join(Parent, Child.parents)
.filter(Parent.id == my_parent.id)
)
sqlalchemy will return only unique Grandchild instances (although the SQL query does not filter duplicates out).
I am using sqlalchemy to create a structure that resembles a graph, so that there are several types of nodes and links joining them. The nodes are defined like this:
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
type = Column(Unicode)
__mapper_args__ = {'polymorphic_on': type}
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
class ThruNode(Node):
__tablename__ = 'thrunode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'thru'}
class OutputNode(Node):
__tablename__ = 'outputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'output'}
Now I want to create a Link table which would looks something like this:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.id', where='type IN ("input", "thru")'))
output = Column(Integer, ForeignKey('node.id', where='type IN ("thru", "output")'))
The bit I'm struggling with is how to do the where part of it, since as I've written it is not valid in sqlalchemy. I had though of using a CheckConstraint or a ForeignKeyConstraint, but I can't see how either of them could actually be used to do this.
I haven't tryed it nor am I an expert of this, but shouldn't this work?
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('thrunode.id'))
output = Column(Integer, ForeignKey('outputnode.id'))
First I had another idea that maybe you could have used different names for the ids and than use those, kind of like:
instead of:
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
this:
class ThruNode(Node):
[...]
thrunode_id = Column(Integer, ForeignKey('node.id'), primary_key=True)
[...]
and then:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.thrunode_id'))
output = Column(Integer, ForeignKey('node.outputnode_id'))
I got the idea from here sqlalchemy docs: declarative.html#joined-table-inheritance
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.