Now I sort the data in the database by its attribute 1.
If there is a tie of different items with same value of attribute 1, the data seems to be sorted by its id.
However, I would like to break the tie by sorting by desc(id). How could I change the default sorting criteria of the database if there is a tie?
Thanks!
Update
Since version 1.1 the order_by parameter in the mapper configuration has been deprecated. Instead Query.order_by must be used.
db.query(User).order_by(User.fullname)
# or in desc order
db.query(User).order_by(User.fullname.desc())
I left here the original answer for historial purposes:
This is possible by means of the mapper configuration.
If you have a user table and want to retrieve the records always ordered by fullname something like this should works:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
password = Column(String)
__mapper_args__ = {
"order_by": fullname,
}
def __repr__(self):
return f"<User(id='{self.id}', name='{self.name}', fullname='{self.fullname}')>"
Default order_by is ascending, if you want to reverse the order this can be used:
__mapper_args__ = {
"order_by": fullname.desc(),
}
The order is entirely determined by the database, not SQLAlchemy. With plain SQL you just add additional ORDER BY clauses, in SQLAlchemy ORM you do the same by chaining order_by methods. For example:
for eq in session.query(Equipment).order_by(Equipment.model_id).order_by(Equipment.name).all():
print (eq)
Whichever is left-most is the primary sort.
Related
How can I automatically truncate string values in a data model across many attributes, without explicitly defining a #validates method for each one?
My current code:
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import validates
class MyModel:
__tablename__ = 'my_model'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(40), nullable=False, unique=True)
# I can "force" truncation to my model using "validates"
# I'd prefer not to use this solution though...
#validates('name')
def validate_code(self, key, value):
max_len = getattr(self.__class__, key).prop.columns[0].type.length
if value and len(value) > max_len:
value = value[:max_len]
return value
My concern is that my ORM will span many tables and fields and there's a high risk of oversight in including attributes in string length validation. In simpler words, I need a solution that'll scale. Ideally, something in my session configuration which'll automatically truncate strings that are too long...
You could create a customised String type that automatically truncates its value on insert.
import sqlalchemy.types as types
class LimitedLengthString(types.TypeDecorator):
impl = types.String
def process_bind_param(self, value, dialect):
return value[:self.impl.length]
def copy(self, **kwargs):
return LimitedLengthString(self.impl.length)
class MyModel:
__tablename__ = 'my_model'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(LimitedLengthString(40), nullable=False, unique=True)
The extended type will still create VARCHAR(40) in the database, so it should be possible to replace String(40) with LimitedLengthString(40)* in your code without a database migration.
* You might want to choose a shorter name.
Query Statement: Get Children that have their Names start with 'A'. Link Them With their Parents.
Schema:
I Have a One-to-Many Relationship. Parent(id, Name, children(rel->child)) -> Child(id, Name)
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
Name = Column(String)
children = relationship("Child", lazy='joined')
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
Name = Column(String)
Want to Filter the Relationship Object. (ie, the List of Objects you get when an instantiated Parent.children) is accessed.
Eg: [Parent(id=1, Name='Parent1' children=[Child(id='1', Name='AChild1'), Child(id='2', Name='Child2')] Needs to be Filtered to: [Parent(id=1, Name='Parent1' children=[Child(id='1', Name='AChild1')] when the resulting query is accessed.
How do I write a Statement to get the Above Result?
A solution that Filters them Once Loaded, I want to Filter them While Loading.
Perhaps I should've googl-fu'ed Harder, but this was the result of some searching.
From SQLAlchemy Documentation:
When we use contains_eager(), we are constructing ourselves the SQL that will be used to populate collections. From this, it naturally follows that we can opt to modify what values the collection is intended to store, by writing our SQL to load a subset of elements for collections or scalar attributes.
Resulting in the Statement:
db.Query(Parent).join(Parent.children)\
.filter(Parent.children.any(Child.Name.like("A%")))\
.options(contains_eager(Parent.children))
Question
How do I get a list of all primary key columns of a model class and all its parents in the polymorphic hierarchy, so I can use it in a session.query().group_by?
Details
In SQLAlchemy ORM, if I'm querying a class that is part of a polymorphic hierarchy and I want to GROUP BY it's primary key, I must also GROUP BY all primary keys of its parents in the polymorphic hierarchy.
Imagine the following setup inspired by the Mapping Class Inheritance Hierarchies section of the sqlalchemy documentation:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
engineer_name = Column(String(30))
__mapper_args__ = {
'polymorphic_identity':'engineer',
}
class EngineeringTask(Base):
__tablename__ = 'engineering_task'
id = Column(Integer, primary_key=True)
name = Column(String(50))
assigned_engineer_id = Column(ForeignKey(Engineer.id))
assigned_engineer = relationship(Engineer, backref=backref("assigned_tasks", lazy="dynamic"))
If I want to do a query like
session.query(
Engineer,
).join(
Engineer.assigned_tasks,
).add_columns(
func.count(EngineeringTask.id).label('assigned_task_count'),
).group_by(Engineer.id, Employee.id)
Such a query, which is selecting all columns from Engineer and Employee but not aggregating them, is possible in PostgreSQL because (emphasis mine):
When GROUP BY is present (...) it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions or when the ungrouped column is functionally dependent on the grouped columns, since there would otherwise be more than one possible value to return for an ungrouped column. A functional dependency exists if the grouped columns (or a subset thereof) are the primary key of the table containing the ungrouped column.
But it requires that I know / care / remember all the primary key columns of the mapped class I'm selecting and all it's parents in the polymorphic hierarchy (Engineer.id and Employee.id in this case)
How can I obtain the list of all the primary key columns of Engineer and all parents in the polymorphic hierarchy, dynamically?
The best I could come up with so far is this function:
from sqlalchemy import inspect
def polymorphic_primary_keys(cls):
mapper = inspect(cls)
yield from (column for column in mapper.columns if column.primary_key)
if mapper.inherits is not None:
yield from polymorphic_primary_keys(mapper.inherits.entity)
Use it like this:
query = session.query(
Engineer,
).join(
Engineer.assigned_tasks,
).add_columns(
func.count(EngineeringTask.id).label('assigned_task_count'),
).group_by(*polymorphic_primary_keys(Engineer))
Where inspect(Engineer) returns Engineer.__mapper__ which is a Mapper containing:
Mapper.columns, an iterator/dictionary/attribute accessor of the columns of the mapped model
Mapper.inherits, which is the mapper of the parent entity, if any.
It's unsatisfying for a few reasons:
There is Mapper.primary_key which is supposed to be an iterator over the primary keys, but I can't use it since it returns the primary keys of the topmost parent, not the currently mapped entity, so I have to iterate over Mapper.columns and check primary_key attribute.
There is a Mapper.polymorphic_iterator() method and a Mapper.self_and_descendants attribute that both return an iterator containing the current mapper and the mappers of all "downstream" subclasses of the current entity. Why isn't there an equivalent iterator for the "upstream" superclasses?
But it gets the job done...
With this models:
class Ball(Base):
__tablename__ = 'balls'
id = Column(Integer, primary_key=True)
field_id = Column(Integer, ForeignKey('fields.id'))
field = relationship("Field", back_populates="fields")
class Field(Base):
__tablename__ = 'fields'
id = Column(Integer, primary_key=True)
nickname = Column(String)
fields= relationship("Ball", order_by=Ball.id, back_populates="field")
I'm trying to write query to access Field.nickname and Ball.field_id. With use of
result = session.query(Field, Ball).all()
print(result)
I'm able to retrieve
(<Field(id=1, nickname=stadium>, <Ball(id=1, field_id=newest)>), (<Field(id=1, nickname=stadium>, <Ball(id=2, field_id=oldest)>
but when using
result = session.query(Field).join(Ball).all()
print(result)
I'm getting empty list [], doesn't matter if I'm applying any filters or not. According to official docs, join would come in handy if dealing with two tables, so this way I would be able to filter for the same id in both and I guess it would came in handy when displaying query results in jinja template.
You have a typo in your Field class - back_populates='fields' doesn't match up with the attribute name in Ball.
Try changing it to back_populates='field' so that it matches the attribute defined in Ball. With the relationship defined correctly sqlalchemy should be able to do the join automatically.
I'm trying to use SQLAlchemy in a situation where I have a one to many table construct and but I essentially want to create a one to one mapping between tables using a subquery.
For example
class User:
__tablename__='user'
userid = Column(Integer)
username = Column(String)
class Address:
__tablename__='address'
userid = Column(Integer)
address= Column(String)
type= Column(String)
In this case the type column of Address includes strings like "Home", "Work" etc. I would like the output to look something like this
I tried using a subquery where I tried
session.query(Address).filter(Address.type =="Home").subquery("HomeAddress")
and then joining against that but then I lose ORM "entity" mapping.
How can I subquery but retain the ORM attributes in the results object?
You don't need to use a subquery. The join condition is not limited to foreign key against primary key:
home_address = aliased(Address, "home_address")
work_address = aliased(Address, "work_address")
session.query(User) \
.join(home_address, and_(User.userid == home_address.userid,
home_address.type == "Home")) \
.join(work_address, and_(User.userid == work_address.userid,
work_address.type == "Work")) \
.with_entities(User, home_address, work_address)