sqlalchemy mapping multiple entries to the same backref - python

Perhaps this is a design issue but I have a case where I have a class that defines a default part, and alternate_parts.
class Entity(Base):
__tablename__ = "entity"
id = Column(Integer, primary_key=True)
part_id = Column(Integer, ForeignKey('part.id'))
part = relationship("Part", backref="entities")
alternate_parts = relationship("Part", secondary="associate_entity_to_parts", backref="entities")
...
class Part(Base):
__tablename__ = "part"
id = Column(Integer, primary_key=True)
...
This throws an error:
sqlalchemy.exc.ArgumentError: Error creating backref 'entities' on relationship 'Entity.alternate_parts': property of that name exists on mapper 'Mapper|Parts|part'
The purpose of this is that I want to be able to reverse look up any entities that refer to this part including ones where they are alternates. The reason for storing the alternates in this way is that this way I don't have to to store some kind of a "default" part id somewhere.
Any suggestions?

Related

SQLAlchemy Handling Multiple Paths In One Relationship

Please note: this question is related but separate from my other currently open question SQLAlchemy secondary join relationship on multiple foreign keys.
The SQLAlchemy documentation describes handling multiple join paths in a single class for multiple relationships:
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(String)
billing_address_id = Column(Integer, ForeignKey("address.id"))
shipping_address_id = Column(Integer, ForeignKey("address.id"))
billing_address = relationship("Address")
shipping_address = relationship("Address")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
street = Column(String)
city = Column(String)
state = Column(String)
zip = Column(String)
Within the same section the documentation shows three separate ways to define the relationship:
billing_address = relationship("Address", foreign_keys=[billing_address_id])
billing_address = relationship("Address", foreign_keys="[Customer.billing_address_id]")
billing_address = relationship("Address", foreign_keys="Customer.billing_address_id")
As you can see in (1) and (2) SQLAlchemy allows you to define a list of foreign_keys. In fact, the documentation explicitly states:
In this specific example, the list is not necessary in any case as there’s only one Column we need: billing_address = relationship("Address", foreign_keys="Customer.billing_address_id")
But I cannot determine how to use the list notation to specify multiple foreign keys in a single relationship.
For the classes
class PostVersion(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
tag_1_id = db.Column(db.Integer, db.ForeignKey("tag.id"))
tag_2_id = db.Column(db.Integer, db.ForeignKey("tag.id"))
tag_3_id = db.Column(db.Integer, db.ForeignKey("tag.id"))
tag_4_id = db.Column(db.Integer, db.ForeignKey("tag.id"))
tag_5_id = db.Column(db.Integer, db.ForeignKey("tag.id"))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String(127))
I have tried all of the following:
tags = db.relationship("Tag", foreign_keys=[tag_1_id, tag_2_id, tag_3_id, tag_4_id, tag_5_id]) resulting in
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship AnnotationVersion.tags - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
tags = db.relationship("Tag", foreign_keys="[tag_1_id, tag_2_id, tag_3_id, tag_4_id, tag_5_id]") resulting in
sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper|AnnotationVersion|annotation_version, expression '[tag_1_id, tag_2_id, tag_3_id, tag_4_id, tag_5_id]' failed to locate a name ("name 'tag_1_id' is not defined"). If this is a class name, consider adding this relationship() to the class after both dependent classes have been defined.
And many others variations on the list style, using quotes inside and outside, using Table names and Class names.
I've actually solved the problem in the course of this question. Since there seems to be no direct documentation, I'll answer it myself instead of deleting this question.
The key is to define the relationship on a primary join and specify the uselist parameter.
tags = db.relationship("Tag", primaryjoin="or_(PostVersion.tag_1_id==Tag.id,"
"PostVersion.tag_2_id==Tag.id, PostVersion.tag_3_id==Tag.id,"
"PostVersion.tag_4_id==Tag.id, PostVersion.tag_5_id==Tag.id)",
uselist=True)

Using sqlalchemy to define relationships in MySQL

I am in the process of working with sqlalchemy and MySQL to build a database. I am currently having trouble defining a specific relationship between two tables.
class Experiment_Type(Base):
__tablename__='experiment_types'
id = Column(Integer, primary_key=True)
type = Column(String(100))
class Experiments(Base):
__tablename__ = 'experiments'
id = Column(Integer, primary_key=True)
type = Column(String(100))
sub_id = Column(Integer, ForeignKey('experiment_types.id'))
experiments = relationship('Experiments',
primaryjoin="and_(Experiment_Type.id == Experiments.sub_id,
'Experiments.type' == 'Experiment_Type.type')",
backref=backref('link'))
What I want to do is have values of sub_id in experiments match the id in experiment_types based on type (if an entry in experiment_types of type = 'type1' has id = 1, then an entry in experiments with type = 'type1' should have a sub_id = 1). I am not even sure if this is the best way to approach defining the relationship in this situation
so any advice is welcome.
The current error message is this:
sqlalchemy.exc.ArgumentError: Could not locate any relevant foreign key columns for primary join condition '0 = 1' on relationship Experiments.experiments. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation.
The whole point of setting up relationships in relational dbs is to not have to duplicate data across tables. Just do something like this:
class ExperimentType(Base):
__tablename__='experiment_types'
id = Column(Integer, primary_key=True)
name = Column(String(100))
class Experiments(Base):
__tablename__ = 'experiments'
id = Column(Integer, primary_key=True)
description = Column(String(100))
type_id = Column(Integer, ForeignKey('experiment_types.id'))
type = relationship("ExperimentType")
Then, if you do need to display the experiment type stuff later, can access it with something like:
exp = session.query(Experiment).first()
print exp.type.name

SQLAlchemy InvalidRequestError when using composite foreign keys

My table relationships in SQLAlchemy have gotten quite complex, and now I'm stuck at this error no matter how I configure my relationship.
I'm a bit new to SQLAlchemy so I'm not sure what I'm doing wrong, but I keep getting the same error no matter what I do.
I have a hierarchy of tables, all inheriting from 'Node', which is a table with self-referential id and parent_id columns. All of this works. Now I have another Node table 'Information', which contains a composite primary key that is referenced by a different Node table, 'Widget'.
Base = declarative_base()
class Node(Base):
__tablename__ = 'Node'
id = Column(Integer, primary_key=True)
parentid = Column(Integer, ForeignKey('Node.ID')
type = Column(Text(50))
children = relationship('Node')
__mapper_args__ = {
'polymorphic_identity': 'Node',
'polymorphic_on': type
}
class Information(Node):
__tablename__ = 'Information'
id = Column(Integer, ForeignKey('Node.ID'), primary_key=True)
Name = Column(Text, primary_key=True)
Number = Column(Float, primary_key=True)
Widgets = relationship('Widget', backref='ThisInformation')
__mapper_args__ = {'polymorphic_identity': 'Information'}
class Widget(Node):
__tablename__ = 'Widget'
id = Column(Integer, ForeignKey('Node.ID'), primary_key=True)
Name = Column(Text)
UnitType = Column(Text)
Amount = Column(Float)
_Number = Column('Number', Float)
__table_args__ = (ForeignKeyConstraint(
['Name', 'Number'],
['Information.Name', 'Information.Number']),
{})
__mapper_args__ = {'polymorphic_identity': 'Widget'}
I was worried this would give me circular reference issues, but instead it gives me this error:
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: Could not determine join condition between parent/child tables on relationship Widget.Information - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
I have tried adding foreign_keys arguments to the relationship, both on Widget and Information side, but I get the exact same error. Can anyone help?
After a lot of searching I finally found a really simple answer in this answer to another question.
All I did was add 'inherit_condition': (id == Node.id) to the mapper in the classes that inherit from Node so that it looked like this:
__mapper_args__ = {'polymorphic_identity': 'Information',
'inherit_condition': (id == Node.id)}
And it works perfectly.

SQLAlchemy foreign key lazy loading

I have a basic one to many relationship:
class Term(Base):
__tablename__ = 'term'
id = Column(Integer, primary_key=True)
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
term = Column(Integer, ForeignKey('term.id'))
But when I load the Node object, access the "term" property, I just get the numeric term id, not the Term object.
node = session.query(Node).filter(Node.id == 1).one()
print node.term # 123
How do I get Foreign Key fields to lazy load the object?
Thanks very much.
Ben
because your term attribute is a Column, sqlalchemy maps it as that column's value. You can get sqlalchemy to actually load the referent row by using relationship:
from sqlalchemy.orm import relationship
class Term(Base):
__tablename__ = 'term'
id = Column(Integer, primary_key=True)
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
term = Column(Integer, ForeignKey('term.id'))
related_term = relationship(Term, backref="nodes")
Because my_node.related_term looks a bit odd, I tend to prefer a naming convention of having the column called table_column instead of just table, so that I can also name the relationship attribute after the table, instead of inventing some other, odd name.
Use the returned value of node.term for a new query, to get the related objects:
node = session.query(Node).filter(Node.id == 1).one()
related_term = session.query(Term).filter(Term.id == node.term).one()

How to set an SQLAlchemy relationship up to access a field value instead of the related instance

The following totally incomplete snippet defines a basic SQLAlchemy relationship using declarative syntax...
Base = declarative_base()
class Movie(Base):
__tablename__ = 'movies'
id = Column(Integer, primary_key=True)
name = Column(String)
director = relationship("People", uselist = False)
class People(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String, nullable = false)
To access the director name it would be something like:
assert isinstance(movie, Movie) # <-- retrieved with query or whatever
director_name = movie.director.name
If, for convenience, I always want the director relationship to just give me the director's name, rather than a People instance, how do you do this? eg: it should work just like this:
assert isinstance(movie, Movie)
director_name = movie.director # <-- should get the string directly
I'm 99% sure I've done this before but can't find any reference code or documentation on it anymore. I'm going a bit crazy trying to locate it. Stack Overflow will be a good/permanent reference location for the answer.
The association proxy is used for all kinds of "object reference-> attribute reference" styles of transformation on the Python side. Docs have been newly updated and rewritten:
http://www.sqlalchemy.org/docs/orm/extensions/associationproxy.html
What if you use property?
class Movie(Base):
__tablename__ = 'movies'
id = Column(Integer, primary_key=True)
name = Column(String)
_director = relationship("People", uselist = False)
#property
def director_name(self):
return self._director.name

Categories

Resources