SQL Alchemy order_by not ordering on a join? - flask

This has to be super simple but I'm stuck. I used order_by a lot for simple queries but for some reason it won't work on a join as seen below.
Here is my query:
g.items = db.session.query(Executor).join("listed").\
order_by(Executor.executor_order.desc()).\
filter_by(Executor.will_id == g.profile.id).all()
Here is the sql result from the above query - totally ignores the order_by part for some reason:
SELECT executor.id AS executor_id, executor.will_id AS
executor_will_id, executor.listed_people_id AS
executor_listed_people_id, executor.executor_order AS
executor_executor_order FROM executor WHERE executor.will_id = :will_id_1
What am I doing wrong here? Why is it completely ignoring the .order_by, even if I put it before the filter_by?
Here is the executor Model - the join appears to work (I need to bang around it some more to make sure):
class Executor(db.Model):
id = db.Column(db.Integer, primary_key = True)
will_id = db.Column(db.Integer(), db.ForeignKey('will.id', ondelete='CASCADE'))
listed_people_id = db.Column(db.Integer(), db.ForeignKey('listed_people.id', ondelete='CASCADE'))
executor_order = db.Column(db.Integer(), nullable=True)
#relationship - so I can call Executor.listed_people for a list.
listed_people = db.relationship('ListedPeople', backref='executor')

Related

Count related and filter in SQLAlchemy

So I have an object Services, object Services has a foreign key to object Waiters. Object waiter has also got a time field "exit" which must be smaller than the current time. The idea is to return all objects Waiter with a count for how many objects service they are related to that have a field exit larger than the current time. Then I have to do the same with Tables.
Finally I need to filter out the Waiters which currently hold four or more current services, and I have to filter out the tables with one or more.
Here are my models:
class Waiter(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
service = db.relationship('Service', backref='waiter', lazy=True)
class Table(db.Model):
id = db.Column(db.Integer, primary_key=True)
table_no = db.Column(db.String(128))
service = db.relationship('Service', backref='table', lazy=True)
class Service(db.Model):
id = db.Column(db.Integer, primary_key=True)
table = db.Column(db.Integer, db.ForeignKey('table.id'))
waiter = db.Column(db.Integer, db.ForeignKey('waiter.id'))
arrival = db.Column(db.DateTime, default=datetime.datetime.now(mx))
exit = db.Column(db.DateTime)
tip = db.Column(db.Numeric(10,2))
So far I've got something like:
waiters = Waiter.query(Waiter.id, \
func.count(*"I don't know what to put here"*).\
label("service_count")).groupby(Waiter.id).subquery()
EDIT:
After further research I have come to the following query. I still don't know if it's right:
waiters = Waiter.query(Waiter.id, \
func.count("*").filter(Service.exit>=datetime.datetime.now()) .\
label("service_count")).groupby(Waiter.id).subquery()
Any help is much appreciated. Thank you very much.
I got an answer that does what I want but instead of of an annotation I get a tupple with the values. It's this:
waiters = db.session.query(Waiter, func.count(Service.id).\
filter(Service.exit>time)).\
outerjoin(Service).group_by(Waiter).all()
It returns something like this:
[(<Waiter 1>, 0)]

flask sqlalchemy one-to-many filter, count & grouping

I am currently trying to build a query which
give me for a one-to-many sqlalchemy query in flask both my result filters grouped and then says how many individual entries there are for it
Following is my database model to illustrate the question:
class cellphone(db.Model):
__tablename__ = 'cellphone'
id = db.Column(db.Integer, primary_key = True)
number = db.Column(db.String(30), unique=True)
sms = db.relationship('sms_accounting', backref="cellphone", lazy='dynamic')
class sms_accounting(db.Model):
__tablename__ = 'sms_accounting'
id = db.Column(db.Integer, primary_key = True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
cellphone_id = db.Column(db.Integer, db.ForeignKey('cellphone.id'))
What I want to do now is find out how many SMS were sent within X days per number.
Filtering and grouping I managed to do, but to calculate the sum per device correctly is not possible.
def sms_count():
search_peroid='90' #time to fetch events in days
period_start = datetime.utcnow() - timedelta(hours=int(search_peroid))
phone_count = sms_accounting.query.filter(sms_accounting.timestamp.between(period_start, \
datetime.utcnow() )).group_by(sms_accounting.cellphone_id).all()
I found some examples for func.count, but unfortunately none of them works. This already starts with the usage,
AttributeError: BaseQuery object has no attribute 'func'
even though it was imported especially.
from sqlalchemy.sql.functions import func
Forgive me if I am wrong.
As an option, you could try executing an SQL Query through Flask.
db.session.execute('select number, count(sms_accounting.id) from cellphone join sms_accounting on sms_accounting.cellphone_id = cellphone.id');
You can easily add the time based filter using where.
Regarding the AttributeError, are you sure you are using the 'func' method properly? The correct usage can be found on this unrelated answer at https://stackoverflow.com/a/4086229/4854064. It might be that you accidentally called func as a method of the query object.

sqlalchemy flask ignoring an and_ and func.DATE in filter query

I have this query that is trying to find a record given the same day and a status:
ld=LunchDay.query.filter(and_(func.DATE(LunchDay.timestamp == datetime.date.today()), LunchDay.status==1))
The model:
class LunchDay(db.Model):
__tablename__ = 'lunch_day'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = relationship("User", foreign_keys=[user_id])
timestamp = db.Column(db.DateTime, index=True, default=datetime.today())
status = db.Column(db.Integer)
It runs and doesn't throw an error, yet it seems to not regard date. It will find entries with dates like yesterdays in the DateTimeField of the database:
2018-11-13 00:00:00.000000
Which is yesterdays date, but it is picking it up based just on the status almost just like it is looking at it as an OR. The imports I use are:
from sqlalchemy import func, and_
Print out the actual sql that is being generated by your query to see what is happening. E.g.:
ld=LunchDay.query.filter(and_(func.DATE(LunchDay.timestamp == datetime.date.today()), LunchDay.status==1))
print(ld)
Prints:
SELECT lunch_day.id AS lunch_day_id, lunch_day.timestamp AS lunch_day_timestamp, lunch_day.status AS lunch_day_status
FROM lunch_day
WHERE DATE(lunch_day.timestamp = %(timestamp_1)s) AND lunch_day.status = %(status_1)s
There you can see that the equality of lunch_day.timestamp and the param timestamp_1 is being passed to the DATE function.
Which is actually pretty easy to see in your sqlalchemy query: func.DATE(LunchDay.timestamp == datetime.date.today()). I assume you want to convert LunchDay.timestamp to a date and then compare it to datetime.date.today() which should be db.func.DATE(LunchDay.timestamp) == date.today().
print(LunchDay.query.filter(and_(func.DATE(LunchDay.timestamp) == datetime.date.today(), LunchDay.status == 1)))
prints:
SELECT lunch_day.id AS lunch_day_id, lunch_day.timestamp AS lunch_day_timestamp, lunch_day.status AS lunch_day_status
FROM lunch_day
WHERE DATE(lunch_day.timestamp) = %(DATE_1)s AND lunch_day.status = %(status_1)s
One other thing to note is that multiple statements passed to filter() are automatically treated as an and_(), so you can simplify your query a little by removing that:
LunchDay.query.filter(func.DATE(LunchDay.timestamp) == datetime.date.today(), LunchDay.status == 1)

Sqlalchemy many to many relationship

I have two tables in many to many relationship:
class Association(db.Model):
__tablename__ = 'association'
club_id = db.Column(db.Integer, db.ForeignKey('clubs.id'), primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), primary_key=True)
joined_date = db.Column(db.String)
assoc_student = db.relationship("Student")
class Club(db.Model):
__tablename__ = 'clubs'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
location = db.Column(db.String)
club_assoc = db.relationship("Association")
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
age = db.Column(db.String)
gender = db.Column(db.String)
Questions:
1) What is the difference between these two queries?
students = db.session.query(Association).filter_by(club_id='1')
students = Association.query.filter_by(club_id='1')
They seem to give the same result!
2) I'm trying to get a list of students with certain age but this following query doesn't work:
db.session.query(Association).filter_by(Association.club_id=='1', Association.assoc_student.age=='15')
But I get this error:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Association.assoc_student has an attribute 'age'
That's why I'm using this one:
db.session.query(Student).join(Association).filter(Association.club_id=='1', Student.age=='15')
Is there a better way to do this without "join"? Maybe with using "backref"!?
1) What is the difference between these two queries?
They do almost the same thing. Former is the way to query objects provided with SQLAlchemy (library Flask uses to access database).
Latter is the convenient way to query models added by Flask-SQLAlchemy library. It makes your queries more readable + extends query with few useful methods. Take a look at source of the flask_sqlalchemy.BaseQuery class to see them: get_or_404(), first_or_404() and paginate().
Usually you want to use latter method to query objects.
2) I'm trying to get a list of students with certain age but this following query doesn't work.
There are two things here:
Be aware about the difference between filter() and filter_by() methods. In your example you try to use filter_by() with SQL expressions instead of kwargs, which is incorrect.
When you're using filter() you can't specify columns over a relationships (like Association.assoc_student.age). The only allowed format is ModelName.column_name. That's why it fails.
Is there a better way?
Your second approach is absolutely correct and fine to use. I don't think there is a better way to do it. Alternatively you can use code below to avoid importing db (if you define query in another file):
Student.query.join(Association).filter(Association.club_id == '1', Student.age == '15')

OperationalError: (OperationalError) no such column

I have a problem. I have to do this query:
#app.route('/api/subscriptions/<string:id>', methods=('DELETE',))
#decorators.login_required
def delete_subscription(id):
dbsession = DBSession()
session = Session()
favorit = (dbsession.query(StudentsFavorites)
.filter(Exams.number == str(id))
.filter(StudentsFavorites.exam_id)
.filter(Students.id == StudentsFavorites.student_id)
.filter(Students.id == str(session.get_user_id()))
.delete() )
dbsession.flush()
return jsonify(error=False)
But when I do this query I get this exception:
OperationalError: (OperationalError) no such column: exams.number u'DELETE FROM students_favorites WHERE exams.number = ? AND students_favorites.exam_id AND students.id = students_favorites.student_id AND students.id = ?' ('123123123', 'a24213')
The tables are very big and got lots of information, so i can't post all of it. But this query works:
#app.route('/api/subscriptions/<string:id>', methods=('PUT',))
#decorators.login_required
def add_subscription(id):
dbsession = DBSession()
session = Session()
examID = (dbsession.query(Exams.id)
.filter(Exams.number == id).first()
)
favorit=StudentsFavorites(student_id=session.get_user_id(), exam_id=examID.id)
dbsession.add(favorit)
dbsession.flush()
return jsonify(error=False)
Short view to the table:
table: Exams
rows: id, number (number is the id i put into the function)
table: StudentsFavorites
rows: student_id, exams_id
table: Students
rows: id
I really didn't understand, why he didn't find the number row in the exception.
EDIT:
Database StudentsFavorites:
class StudentsFavorites(Base):
"""N:M resolve model for the exams to the semester.
"""
__tablename__ = "students_favorites"
student_id = Column(Unicode(255), ForeignKey("students.id"), primary_key=True, autoincrement=False)
exam_id = Column(Integer, ForeignKey("exams.id"), primary_key=True, autoincrement=False)
created_at = Column(DateTime, nullable = False, default = datetime.now)
student = relationship("Students", uselist = False, lazy="joined")
exam = relationship("Exams", uselist=False, lazy="joined")
Something like this? I tried this:
(dbsession.query(StudentsFavorites)
.filter(StudentsFavorites.exam.id == str(id))
.filter(StudentsFavorites.student.id == str(session.get_user_id()))
.delete()
)
But got the error, that id didn't exist in exams / student
You have two cases of the same problem. Your query has information for StudentFavorites which means it knows about StudentFavorites.student_id and StudentFaovrites.exams_id. It doesn't know anything about Students.id, Exames.id and Exames.number. In order for you to query a StudentFavorites object and have it know about those other values you're going to have to perform a sql join.
Join's can be a bit of a pain in the ass to get working in sqlalchemy (well... in regular sql as well). Since I don't know what your table schema is I can't talk about that but the view should look something like this.
#app.route('/api/subscriptions/<string:id>', methods=('DELETE',))
#decorators.login_required
def delete_subscription(id):
dbsession = DBSession()
session = Session()
favorit = (dbsession.query(StudentsFavorites)
.join(Exames)
.join(students)
.filter(Exams.number == str(id))
.filter(StudentsFavorites.exam_id)
.filter(Students.id == StudentsFavorites.student_id)
.filter(Students.id == str(session.get_user_id()))
.delete() )
dbsession.flush()
return jsonify(error=False)
Alternatively, you can look into setting up Foreign key relationships in your table statements if you use the ORM to create your tables
The reason your second example works is because you're specifying a query over an exam table and only using values found in that table.
Response to Edit:
Right now your table relationships aren't set up correctly. Specifically the sections: Many To Many and Deleting Rows from the Many to Many Table
This example code is explained in much more (and better) detail in the posted link but the basic idea is that you have a associate_table (in your case StudentFavorites) contains foreign keys which have a relationship which is specified in one or more of your other tables. I personally advise that you go with the table example and not the object example.
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)