How to group by func.week, year etc in Association proxy - python

I have two databases:
class AcUsers(db.Model):
__tablename__ = "ac_users"
id = Column(Integer, primary_key=True)
b2c_customer_id = Column(Integer, ForeignKey("b2c_customer.id",
ondelete="CASCADE"))
ac_tests = db.relationship(
"AcTests",
back_populates="ac_user",
uselist=False,
lazy="joined",
)
last_test_submit_time = association_proxy("ac_tests",
"last_test_submit_time")
class AcTests(db.Model):
__tablename__ = "ac_tests"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("ac_users.id"))
ac_user = db.relationship(
"AcUsers",
back_populates="ac_tests",
uselist=False,
lazy="joined",
)
first_test_started_time = Column(DateTime)
last_test_submit_time = Column(DateTime)
I have tried to query the database like this:
ac = (
db.session.query(
func.count(AcUsers.id),
func.week(AcUsers.last_test_submit_time),
func.year(AcUsers.last_test_submit_time),
)
.filter(AcUsers.results_sent)
.filter(AcUsers.last_test_submit_time != None)
.all()
)
I got this error:
NotImplementedError: The association proxy can't be used as a plain column expression; it only works inside of a comparison expression
How the I get past this error in order to be able to use the group by week and year in order to know exactly the user for the present dispensation.
I have tried to get the daily, monthly, and yearly data by getting the data in the AcUsers and looping through it:
ac_users = (
db.session.query(AcUsers)
.filter(AcUsers.results_sent)
.filter(AcUsers.last_test_submit_time != None)
.all()
)
daily:
for user in ac_users:
actual_date = user.last_test_submit_time.date()
if actual_date not in daily_data:
daily_data[actual_date] = 1
else:
daily_data[actual_date] += 1
results_sent_daily = []
for x, y in daily_data.items():
results_sent_daily.append({"x": x, "y": y})
I did the same for the monthly and yearly.
How do i make the query use the grouping func into func.year, func.week, func.date etc.
Thank you in advance

As stated in the association proxy documentation, the proxies are not meant for ORM querying aside from filtering.
The most simple thing to do is to craft a query using both models, specifying the join between them and the grouping fields.
ac = (
s.dbs.query(
func.count(AcUsers.id).label("count"),
func.week(AcTests.last_test_submit_time).label("week"),
func.year(AcTests.last_test_submit_time).label("year"),
)
.join(AcUsers.ac_tests)
.group_by(
func.week(AcTests.last_test_submit_time),
func.year(AcTests.last_test_submit_time)
)
.filter(AcTests.last_test_submit_time != None)
.all()
)

Related

AttributeError: 'Query' object has no attribute 'is_clause_element' when joining table with query

AttributeError: 'Query' object has no attribute 'is_clause_element' when joining table with query
I have a query that counts the amount of keywords a company has and then sorts them by the amount of keywords they have.
query_company_ids = Session.query(enjordplatformCompanyToKeywords.company_id.label("company_id"),func.count(enjordplatformCompanyToKeywords.keyword_id)).group_by(enjordplatformCompanyToKeywords.company_id).order_by(desc(func.count(enjordplatformCompanyToKeywords.keyword_id))).limit(20)
I then want to get information about these companies like image, title, info etc and send it to the frontend (this is done later by looping through companies_query).
Though I have trouble in building the connection between the query_company_ids query and enjordplatformCompanies table.
I have tried two ways of doing this:
companies_query = Session.query(enjordplatformCompanies, query_company_ids).filter(enjordplatformCompanies.id == query_company_ids.company_id).all()
companies_query = Session.query(enjordplatformCompanies, query_company_ids).join( query_company_ids, query_company_ids.c.company_id == enjordplatformCompanies.id).all()
But both of them result in the error: AttributeError: 'Query' object has no attribute 'is_clause_element'
Question
How can I join the query_company_ids query and enjordplatformCompanies table?
Thanks
Here are the table definitions
class enjordplatformCompanies(Base):
__tablename__ = "enjordplatform_companies"
id = Column(Integer, primary_key=True, unique=True)
name = Column(String)
about = Column(String)
image = Column(String)
website = Column(String)
week_added = Column(Integer)
year_added = Column(Integer)
datetime_added = Column(DateTime)
created_by_userid = Column(Integer)
company_type = Column(String)
contact_email=Column(String)
adress=Column(String)
city_code=Column(String)
city=Column(String)
class enjordplatformCompanyToKeywords(Base):
__tablename__ = "enjordplatform_company_to_keywords"
id = Column(Integer, primary_key=True, unique=True)
company_id = Column(Integer,ForeignKey("enjordplatform_companies.id"))
keyword_id = Column(Integer,ForeignKey("enjordplatform_keywords.id"))
I copied your example query above and was getting a lot of weird errors until I realized you use Session instead of session. I guess make sure you are using an instance instead of the class or sessionmaker.
Below I create an explicit subquery() to get the company id paired with its keyword count and then I join the companies class against that, applying the order and limit to the final query.
with Session(engine) as session, session.begin():
subq = session.query(
enjordplatformCompanyToKeywords.company_id,
func.count(enjordplatformCompanyToKeywords.keyword_id).label('keyword_count')
).group_by(
enjordplatformCompanyToKeywords.company_id
).subquery()
q = session.query(
enjordplatformCompanies,
subq.c.keyword_count
).join(
subq,
enjordplatformCompanies.id == subq.c.company_id
).order_by(
desc(subq.c.keyword_count)
)
for company, keyword_count in q.limit(20).all():
print (company.name, keyword_count)
This isn't the exact method but explains the intention of calling .subquery() above:
subquery

Exclude something from where clause by using SQLAlchemy core

I have the following model:
class Vote(BaseModel):
__tablename__ 'vote'
id = sa.Column(sa.Integer, autoincrement=True, index=True, primary_key=True)
value = sa.Column(sa.Integer, nullable=False)
rated_user_id = sa.Column(
sa.Integer, sa.ForeignKey('user.id', ondelete='cascade'))
rating_user_id = sa.Column(
sa.Integer, sa.ForeignKey('user.id', ondelete='cascade'))
And I just want to make a query with gives me joined data., nevertheless I don't know how to make this query. This is my approach:
query = sa.select(
[votes, users.alias('u1'), users.alias('u2')],
use_labels=True
).select_from(votes.join(users.alias('u1'),votes.c.rated_user_id == users.alias('u1').c.id).join(users.alias('u2'), votes.c.rating_user_id == users.alias('u2').c.id))
Buy it doesn't work because it includes "user" as "u1" in FROM clause.
Thanks!
Each invocation of alias() produces a unique alias object, even if you give them the same label. Instead give the aliases a name and use the same object in every part of your query:
u1 = users.alias('u1')
u2 = users.alias('u2')
query = sa.select([votes, u1, u2], use_labels=True).\
select_from(votes.
join(u1, votes.c.rated_user_id == u1.c.id).
join(u2, votes.c.rating_user_id == u2.c.id))

SQLAlchemy subquery comparison

I have the following set of tables:
class Job(db.Model):
__tablename__ = 'jobs'
id = db.Column(db.Integer, primary_key=True)
class Informant(db.Model):
__tablename__ = 'informants'
id = db.Column(db.Integer, primary_key=True)
job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'))
max_students = db.Column(db.Integer)
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
queues = db.Table('queues',
db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('job_id', db.Integer, db.ForeignKey('jobs.id')),
PrimaryKeyConstraint('student_id', 'job_id'))
Now I need to obtain something like:
SELECT jobs.id
FROM jobs
WHERE (
SELECT SUM(informants.max_students)
FROM informants
WHERE informants.job_id = jobs.id
) <= (
SELECT COUNT(1)
FROM queues
WHERE queues.job_id = jobs.id
)
So basically I search the jobs with an amount of students that exceed the maximal capacity, the sum of the related informants' capacities. Is there a clean way to do this in SQLAlchemy? I tried the following:
db.session.query(Job.id).filter( \
db.session.query(db.func.sum(Informant.max_students)). \
filter(Informant.job_id == Job.id) <= \
db.session.query(db.func.count(1)).select_from(queues). \
filter(queues.c.job_id == Job.id))
This yields something like SELECT jobs.id FROM jobs WHERE 0 = 1. Is there something I'm missing, because I have successfully used similar queries before. Or am I better of using db.engine.execute to execute the raw SQL?
I was close to having the right answer. The piece that was missing is an as_scalar method on both subqueries. So the final query is:
db.session.query(Job.id).filter( \
db.session.query(db.func.sum(Informant.max_students)). \
filter(Informant.job_id == Job.id).as_scalar() <= \
db.session.query(db.func.count(1)).select_from(queues). \
filter(queues.c.job_id == Job.id).as_scalar())

SQLAlchemy: exclude rows taken from a subquery on a query

Abstraction of my problem, I have 2 tables. A User table, and a Friendship table.
I'm trying to make a query to list all the users available to be added as friend to User 1, Alice, and also excluding herself, using SQLAlchemy.
Considering there could be a lot of friendships, to find Alice's friends:
friend_subquery = db.session.query(Friendship).filter_by(User_id=1).subquery()
Now I want all the users listed, except Alice, and her friends, Bob and Jack.
friends = (db.session.query(User).
filter(User.ID != 1).
outerjoin(friend_subquery,
User.ID != friend_subquery.c.Friend_id))
My expected result would have been to get User 4 and 5, but this query
returns all except Alice herself. The condition of
User.ID != friend_subquery.c.Friend_id
seem NOT to be working as expected.
P.S. I've done my homework of searching, reading docs, but couldn't figure it out. Thanks for your time.
I assumed that your models are defined as below:
class User(db.Model):
__tablename__ = 'User'
ID = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
friendships = db.relationship(
'Friendship',
foreign_keys='Friendship.User_id',
backref='friender',
)
friendships_of = db.relationship(
'Friendship',
foreign_keys='Friendship.Friend_id',
backref='friendee',
)
class Friendship(db.Model):
__tablename__ = 'Friendship'
ID = db.Column(db.Integer, primary_key=True)
User_id = db.Column(db.Integer, db.ForeignKey('User.ID'))
Friend_id = db.Column(db.Integer, db.ForeignKey('User.ID'))
In which case two ways to perform this query is shown in the code below. The first query relies on the relationship User.friendships_of, while the second works with explicit joins:
# Add users
u1, u2, u3, u4, u5 = users = [
User(name="Alice"),
User(name="Bob"),
User(name="Jack"),
User(name="Pluto"),
User(name="Mike"),
]
db.session.add_all(users)
# Add friendhips
u1.friendships.append(Friendship(friendee=u2))
u1.friendships.append(Friendship(friendee=u3))
db.session.commit()
# Find Alice
u_alice = db.session.query(User).filter(User.name == 'Alice').one()
# Query (version 1)
q = (
db.session.query(User)
.filter(~User.friendships_of.any(Friendship.User_id == u_alice.ID))
.filter(User.ID != u_alice.ID)
.all()
)
for x in q:
print(x)
# Query (version 2)
q = (
db.session.query(User)
.outerjoin(
Friendship,
db.and_(
u_alice.ID == Friendship.User_id,
User.ID == Friendship.Friend_id,
)
)
.filter(Friendship.ID == None)
.filter(User.ID != u_alice.ID)
.all()
)
for x in q:
print(x)

How to join 3 tables and perform func.sum

How do I join 3 tables, Clients, Orders and Deposits and perform func.sum on Orders.total and Deposits.total for each Client.id that exists in the database ? The query result should include columns Clients.email, func.sum(Orders.total) and func.sum(Deposits.total).
So far, I've tried different queries along the lines of :
listeclients = db.session.query(Clients,func.sum(Clients.orders.total).\
label("ctotal"),func.sum((Clients.deposits.total).\
label("dtotal"))).group_by(Client.id).all()
giving me different errors such as:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Clients.orders has an attribute 'total'
I would like to see how one does this in sqlalchemy, but I'd also settle for hints behind the logic of such a query ...
Are my mappings correct? What is the syntax of such a join? Should I use eagerload somewhere? I've had success with simpler queries, but one like this is over my head for now ! Any help is welcome, even just the logic of it in raw SQL. I'm stuck w/ this ...
class Clients(db.Model):
__tablename__ = 'clients'
id = db.Column(db.Integer, primary_key = True)
email = db.Column(db.String(60), index = True, unique = True)
adresse = db.Column(db.String(64), index = True)
telephone = db.Column(db.String(10), index = True)
confirmed = db.Column(db.Boolean, default = False)
orders = db.relationship('Orders')
deposits = db.relationship('Deposits')
class Orders(db.Model):
__tablename__ = 'orders'
id = db.Column(db.Integer, primary_key = True)
client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
total = db.Column(db.Float)
date = db.Column(db.DateTime, index = True, default=datetime.now)
client = db.relationship('Clients')
class Deposits(db.Model):
__tablename__='deposits'
id = db.Column(db.Integer, primary_key = True)
date = db.Column(db.DateTime, index = True, default=datetime.now)
client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
total = db.Column(db.Float)
cheque = db.Column(db.Boolean)
client = db.relationship('Clients')
update: updated the query below to handle properly the sum:
sq1 = (db.session.query(Orders.client_id, func.sum(Orders.total).label("ctotal"))
.group_by(Orders.client_id)).subquery("sub1")
sq2 = (db.session.query(Deposits.client_id, func.sum(Deposits.total).label("dtotal"))
.group_by(Deposits.client_id)).subquery("sub2")
q = (db.session.query(Clients, sq1.c.ctotal, sq2.c.dtotal)
.outerjoin(sq1, sq1.c.client_id == Clients.id)
.outerjoin(sq2, sq2.c.client_id == Clients.id)
)
Also, instead of defining relationships twice (which might actually fail on some versions of sqlalchemy), you can simply use backref:
class Clients(db.Model):
orders = db.relationship('Orders', backref='client')
deposits = db.relationship('Deposits', backref='client')
class Orders(db.Model):
# client = db.relationship('Clients')
class Deposits(db.Model):
# client = db.relationship('Clients')
In sql it is straightforward:
select c.email, sum(o.total), sum(d.total)
from Clients c
left join Orders o
on ...
left join Deposits d
on ...
group by c.email

Categories

Resources