Ordering SQLAlchemy relationship by another field, not by primary key - python

I have the following model using sqlalchemy in a pyramid application:
class Issue(base_iop):
__tablename__ = 'issues'
issueid = Column(BIGINT, primary_key=True, name="ISSUEID")
issuenum = Column(VARCHAR(20), name="ISSUENUM")
status = Column(VARCHAR(32), name="STATUS")
datetime = Column(TIMESTAMP, name="ISSUETIME")
class Related(base_iop):
__tablename__ = 'related'
relationid = Column(BIGINT, primary_key=True, name="ROWSTAMP")
parent_num = Column(VARCHAR(20), ForeignKey('issue.ISSUENUM'),name="RECORDKEY")
children_num = Column(VARCHAR(20), ForeignKey('issue.ISSUENUM'), name="RELATEDKEY")
issues = relationship(iop, foreign_keys=[child_num])
I can get the related issues of an issue just fine using: the issues attribute of the related table:
for related in db.query(Issue).all()[0].issues:
print related.status
However, I didn't find the solution to order the issues by the datetime attribute. If the Related had the datetime attribute like this, it should be pretty straightforward using the order_by in the relationship:
class Issue(base_iop):
__tablename__ = 'issues'
issueid = Column(BIGINT, primary_key=True, name="ISSUEID")
issuenum = Column(VARCHAR(20), name="ISSUENUM")
status = Column(VARCHAR(32), name="STATUS")
datetime = Column(TIMESTAMP, name="ISSUETIME")
children = relationship("Related", foreign_keys="[Related.parent_num]", backref="parent", order_by="[Related.datetime]")
class Related(base_iop):
__tablename__ = 'related'
relationid = Column(BIGINT, primary_key=True, name="ROWSTAMP")
parent_num = Column(VARCHAR(20), ForeignKey('issue.ISSUENUM'),name="RECORDKEY")
children_num = Column(VARCHAR(20), ForeignKey('issue.ISSUENUM'), name="RELATEDKEY")
datetime = Column(TIMESTAMP, name="ISSUETIME")
issues = relationship(iop, foreign_keys=[child_num])
How do I order the related issues by another field, not by primary key like is it now?

relationship accept the keyword argument order_by to handle your default ordering of the relation.
it should be somthing like:
issues = relationship(iop, foreign_keys=[child_num], order_by="Issue.datetime")
Note that backref function has the same keyword, if you need to handle that.
Here you can read more: http://docs.sqlalchemy.org/en/rel_1_0/orm/tutorial.html#building-a-relationship on this topic

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)

How do you use the `secondary` kwarg to fit this situation in SqlAlchemy?

I have a situation where a user can belong to many courses, and a course can contain many users. I have it modeled in SqlAlchemy like so:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
class Course(Base):
__tablename__ = 'courses'
id = Column(Integer, primary_key=True)
archived = Column(DateTime)
class CourseJoin(Base):
__tablename__ = 'course_joins'
id = Column(Integer, primary_key=True)
# Foreign keys
user_id = Column(Integer, ForeignKey('users.id'))
course_id = Column(Integer, ForeignKey('courses.id'))
In the system, we have the ability to "archive" a course. This is marked by a datetime field on the course model. I would like to give the User model a relationship called course_joins that only contains CourseJoins where the respective Course hasn't been archived. I'm trying to use the secondary kwarg to accomplish this like so:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
course_joins = relationship('CourseJoin',
secondary='join(Course, CourseJoin.course_id == Course.id)',
primaryjoin='and_(CourseJoin.user_id == User.id,'
'Course.archived == None)',
order_by='CourseJoin.created')
However I'm getting this error:
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: FROM expression expected
I believe this is the exact usecase for the secondary kwarg of relationship(), but I'm not sure what's going on.
If you really just have many-to-many relationship (plus created) column, I think the right way to define the relationship is:
courses = relationship(
'Course',
secondary='course_joins',
primaryjoin='users.c.id == course_joins.c.user_id',
secondaryjoin='and_(courses.c.id == course_joins.c.course_id, courses.c.archived == None)',
order_by='course_joins.c.created',
viewonly=True,
)
and use it like:
u1 = User(courses=[Course()])
session.add(u1)
u2 = User(courses=[Course(archived=datetime.date(2013, 1, 1))])
session.add(u2)
Otherwise, just drop the secondary completely and add your other condition to primaryjoin:
courses = relationship(
'CourseJoin',
primaryjoin=\
'and_(users.c.id == course_joins.c.user_id, '
'courses.c.id == course_joins.c.course_id, '
'courses.c.archived == None)',
order_by='course_joins.c.created',
)

Sqlalchemy - how to get an object, and filter which rows in the child class

I’m new to Sqlalchemy, and in need of some help.
i have a model, with a one to many relation:
class Metnadev(DeclarativeBase):
__tablename__ = 'metnadev'
#personal info
id = Column(Integer, autoincrement=True, primary_key=True)
first_name = Column(Unicode(255))
last_name = Column(Unicode(255))
birth_day = Column(Date)
activitys = relationship("Activity", backref="metnadev")
class Activity(DeclarativeBase):
__tablename__ = 'activity'
id = Column(Integer, autoincrement=True, primary_key=True)
metnadev_id = Column(Integer, ForeignKey('metnadev.id'))
location = Column(Unicode(255))
visible = Column(Boolean,default=True)
When I do
metnadev = DBSession.query(Metnadev).filter_by(id=kw['id']).one()
I get the object, with the child, great.
I want to get the object, but only get the rows from the child class, where visible == True
I searched but I’m not sure how to do it,
Thanks for the help
This section of the documentation has your answer: http://docs.sqlalchemy.org/en/rel_0_6/orm/relationships.html#building-query-enabled-properties
class Metnadev(DeclarativeBase):
#...
#property
def activities(self):
return object_session(self).query(Activity).filter_by(visible=True).with_parent(self).all()
There's a couple ways you can do this.
For a one-off, you can just run a second query:
from sqlalchemy import and_
activities = Activity.query.filter(and_(Activity.metnadev_id == kw['id'], Activity.visible==True)).all()
You can change the relationship so only visible items are returned:
activities = relationship("Activity",
primaryjoin="and_(Activity.metnadev_id==Metnadev.id, "
"Activity.visible==True)")
If you need more control you can join the tables, but it sounds like the relationship configuration would work for you. Let me know if that's not the case.
Hope that helps!

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

SQLAlchemy many-to-many relationship on declarative tables

I have the following tables defined declaratively (very simplified version):
class Profile(Base):
__tablename__ = 'profile'
id = Column(Integer, primary_key = True)
name = Column(String(65), nullable = False)
def __init__(self, name):
self.name = name
class Question(Base):
__tablename__ = 'question'
id = Column(Integer, primary_key = True)
description = Column(String(255), nullable = False)
number = Column(Integer, nullable = False, unique = True)
def __init__(self, description, number):
self.description = description
self.number = number
class Answer(Base):
__tablename__ = 'answer'
profile_id = Column(Integer, ForeignKey('profile.id'), primary_key = True)
question_id = Column(Integer, ForeignKey('question.id'), primary_key = True)
value = Column(Integer, nullable = False)
def __init__(self, profile_id, question_id, value):
self.profile_id = profile_id
self.question_id = question_id
self.value = value
Profile is linked to Question via a many-to-many relationship. In the linking table (Answer) I need to store a value for the answer.
The documentation says I need to use an association object to do this but it's confusing me and I can't get it to work.
How do I define the many-to-many relationship for the Profile and Question tables using Answer as the intermediary table?
The documentation says I need to use
an association object to do this but
it's confusing me and I can't get it
to work.
That's right. And the Answer class is your association object as it maps to the association table 'answer'.
How do I define the many-to-many
relationship for the Profile and
Question tables using Answer as the
intermediary table?
The code you've presented in your question is correct. It only needs additional information about relationships on the ORM level:
from sqlalchemy.orm import relationship
...
class Profile(Base):
__tablename__ = 'profile'
...
answers = relationship("Answer", backref="profile")
...
class Question(Base):
__tablename__ = 'question'
...
answers = relationship("Answer", backref="question")
...
Also, you shouldn't setup values for profile_id and question_id in your Answer's init function, because it's the ORM that's responsible for setting them accordingly based on you assignments to relationship attributes of your objects.
You may be interested in reading documentation for declarative, especially the part about configuring relationships. Reading about working with related objects may be helpful as well.

Categories

Resources