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)
Related
I have the classes Shift and Staff that are related in three ways. A Shift object can have a staff1, staff2, and a list of standby staff that is managed with a secondary table. Here are the models.
class Shift(db.Model):
__tablename__ = 'shifts'
shift_id = db.Column(db.Integer, primary_key=True)
staff1_id = db.Column(db.Integer, db.ForeignKey('staff.staff_id'))
staff1 = db.relationship('Staff', foreign_keys=[staff1_id])
staff2_id = db.Column(db.Integer, db.ForeignKey('staff.staff_id'))
staff2 = db.relationship('Staff', foreign_keys=[staff2_id])
standby = db.relationship('Staff', secondary='standby_staff')
class Staff(db.Model):
__tablename__ = 'staff'
staff_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
standby_staff = db.Table('standby_staff', db.metadata,
db.Column('shift_id', db.Integer, db.ForeignKey('shifts.shift_id')),
db.Column('staff_id', db.Integer, db.ForeignKey('staff.staff_id')))
I want to be able to query for all the shifts that a staff member is associated with, either as staff1, staff2, or on standby. I wrote the following SQLAlchemy query:
shifts = Shift.query.filter((Shift.staff1 == staff) |
(Shift.staff2 == staff) |
(Shift.standby.contains(staff))).all()
The SQL query produced is:
SELECT shifts.shift_id AS shifts_shift_id, shifts.staff1_id AS shifts_staff1_id, shifts.staff2_id AS shifts_staff2_id
FROM shifts, standby_staff AS standby_staff_1
WHERE ? = shifts.staff1_id OR ? = shifts.staff2_id OR shifts.shift_id = standby_staff_1.shift_id AND ? = standby_staff_1.staff_id
Here's the problem. If the staff member is not present in the secondary table (never scheduled as standby staff), then the query returns zero results. As soon as the staff member is in the secondary table, all the expected results are returned.
What's going on here? Shouldn't the fact that the conditions are being linked with OR not necessitate the staff member being in standby? What am I doing wrong?
---EDIT---
To be sure this problem wasn't being caused by logical operators being evaluated in an unexpected order, I added self_group() to the end of the SQLAlchemy query so that the SQL query would contain parentheses around the last two conditions:
def query(staff_id):
staff = Staff.query.get(staff_id)
shifts = Shift.query.filter((Shift.staff1 == staff) |
(Shift.staff2 == staff) |
(Shift.standby.contains(staff)).self_group())
SELECT shifts.shift_id AS shifts_shift_id, shifts.staff1_id AS shifts_staff1_id, shifts.staff2_id AS shifts_staff2_id
FROM shifts, standby_staff AS standby_staff_1
WHERE ? = shifts.staff1_id OR ? = shifts.staff2_id OR (shifts.shift_id = standby_staff_1.shift_id AND ? = standby_staff_1.staff_id)
In my application, a 'set' can have a number of 'products' associated with it. Products listed against a set must have quantities defined. For this many-to-many relationship I have followed the SQLAlchemy documentation to use an association table with an additional column (quantity).
I am trying to create a form where the user can assign products and quantities against a given set. Both the sets and products already exist in the database. The data from the form are:
set.id
product.id
quantity
This works to create a new association (e.g. set 1 is 'linked' to product 3 with quantity=XYZ) but I get an integrity error when I try to update an existing record.
I can manually add a relationship/record (dummy data) or within the Flask view function as follows:
s = Set.query.get(2)
p = Product.query.get(3)
a = Set_Product_Association(set=s, product=p, quantity=23)
db.session.add(a)
db.session.commit()
Updating the record (different quantity) manually as follows works:
s.products[0].quantity = 43
db.session.add(s)
db.session.commit()
However when I use the code from the first block instead (with the aim to update the quantity field for a given, existing set and product ID), i.e.:
a = Set_Product_Association(set=s, product=p, quantity=43)
I get an integrity error
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: set_product_association.set_id, set_product_association.product_id [SQL: 'INSERT INTO set_product_association (set_id, product_id, quantity) VALUES (?, ?, ?)'] [parameters: (2, 3, 43)]
I assume this is to tell me that I'm trying to append a new record rather than updating the existing one.
How should I approach this? The 'manual' method works but relies on working out the correct index in the list (i.e. for the correct product.id).
Curiously, if I use form.popluate_obj(set) in my Flask view function to process the form data as described in my question here, I can update fields but not create new 'associations'. Unfortunately, I don't know what goes on behind the scenes there....
My models are defined like so:
class Set_Product_Association(db.Model):
__tablename__ = 'set_product_association'
set_id = db.Column(db.Integer, db.ForeignKey('sets.id'), primary_key=True)
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), primary_key=True)
quantity = db.Column(db.Integer)
product = db.relationship("Product", back_populates="sets")
set = db.relationship("Set", back_populates="products")
class Set(db.Model):
__tablename__ = 'sets'
id = db.Column(db.Integer, primary_key=True)
products = db.relationship("Set_Product_Association",
back_populates="set")
class Product(db.Model):
__tablename__= 'products'
id = db.Column(db.Integer, primary_key=True)
part_number = db.Column(db.String(100), unique=True, nullable=False)
sets = db.relationship("Set_Product_Association",
back_populates="product")
Edit:
I've also tried reversing the operation as suggested here:
s = Set.query.get(2)
a = Set_Product_Association()
a.quantity = 43
a.product = Product.query.get(3)
a.set = s
db.session.commit()
But I still get an error:
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: set_product_association.set_id, set_product_association.product_id [SQL: 'INSERT INTO set_product_association (set_id, product_id, quantity) VALUES (?, ?, ?)'] [parameters: (2, 3, 43)]
You get an integrity error because you are trying to create a new object with the same primary keys.
This:
a = Set_Product_Association(set=s, product=p, quantity=43)
Does not update, but create.
If you want to update the actual row in the table, you need to update the existing one:
assoc = Set_Product_Association.query.filter_by(set=s, product=p).first()
assoc.quantity = 43
db.session.commit()
Also, from the documentation it is advised to not use a model but an actual table.
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)
I try to do query in sqlalchemy to get with self-referential relationship which is filtered on parent and also child level.
category_country = Table('category_country', Base.metadata,
Column('category_id', Integer, ForeignKey('category.id'), primary_key=True),
Column('country_id', Integer, ForeignKey('country.id'), primary_key=True)
)
class Category(Base):
__tablename__ = "category"
id = Column(Integer, primary_key=True, autoincrement=True)
parent_id = Column(Integer, ForeignKey('category.id'))
subcategories = relationship("Category", backref=backref('parent', remote_side=id))
countries = relationship(Country, secondary = category_country, backref='categories')
class Country(Base):
__tablename__ = "country"
id = Column(Integer, primary_key=True)
query
category = s.query(Category).join(Category.countries).options(contains_eager(Category.countries)).filter(Country.id == 1).filter(Category.id == category_id).join(Category.countries, aliased=True).join(Category.subcategories, aliased=True).options(contains_eager(Category.countries)).filter(Country.id == 1).first()
but it doesn't work. I need to find children which are from country 1 and its parent is category_id and country is also 1
I don't completely get your model/code on my first read, but the way I would tackle this is by splitting the self-referential join into a subquery() statement like this:
filter_by_country = (db.session.query(...)
.filter(...)
.subquery())
final_results = (db.session.query(...)
.join(filter_by_country,
db.and_(Category.id == filter_by_country.c.id, ..., ...))
.options(...)
.filter(...)
.etc(...).first())
I've found this sort of a pattern can help simplify these type of queries. Hope this helps.
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')