My main goal is to make multiple filter options for a Car class. I can easily filter by distance and by the construction year, as they are just attributes of the class. The Car class is related to the Review class by a one-to-many relationship (One car can have many reviews). These reviews have a star rating from 0 - 5. I want to sort the Cars by the average rating of their reviews.
This is where I am at so far: The classes:
class Cars(db.Model):
__tablename__= "Cars"
year = db.Column(db.Integer)
distance = db.Column(db.Integer)
# Some other attributes
reviews = db.relationship('Reviews', backref='car')
@hybrid_property
def score(self):
if len(self.reviews) != 0:
return sum(review.stars for review in self.reviews) / len(self.reviews)
else:
return None
@score.expression
def score(cls):
return (select([func.count(Reviews.id)]).select_from(Reviews).where(Reviews.car_id == cls.id))
class Reviews(db.Model):
__tablename__= "Reviews"
__bind_key__ = 'Reviews'
# Some other attributes
stars = db.Column(db.Integer)
car_id = db.Column(db.Integer, db.ForeignKey('Cars.id'))
This is my view function:
@mainRoutes.route("/cars/<int:page_num>")
def cars(page_num):
cars = getPagination(Cars, page = page_num, sort=Cars.score) #Cars.score can be swapped to other columns
return render_template("cars.html", cars=cars)
I know that the score.expression function doesnt return the average, but instead the count. Thats just a WIP. For now, I just want to get this to work.
This is the getPagination function I wrote:
def getPagination(model, sort, perPage = 16, page = 1, **filter):
return model.query.filter(**filter).order_by(sort).paginate(per_page = perPage, page = page)
When I try to sort the cars by the hybrid_property score, I get the following error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: Reviews
[SQL: SELECT "Cars".id AS "Cars_id", "Cars".model AS "Cars_model", "Cars".year AS "Cars_year", "Cars".distance AS "Cars_distance", "Cars".start_rent_period AS "Cars_start_rent_period", "Cars".end_rent_period AS "Cars_end_rent_period", "Cars".is_available AS "Cars_is_available"
FROM "Cars" ORDER BY (SELECT count("Reviews".id) AS count_1
FROM "Reviews"
WHERE "Reviews".car_id = "Cars".id)
LIMIT ? OFFSET ?]
[parameters: (16, 0)]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
The error originates from line:
return model.query.filter(**filter).order_by(sort).paginate(per_page = perPage, page = page)
If you want the full traceback, I can supply that.
So my question is this: Why does Flask, or SQLalchemy, not recognize the Reviews table. Cause its definetly there. I can display instances of it, I can read and write values into it and everywhere else in the code it works fine.
Other things I have tried:
I took the code for the hybrid_property from Here And also tried it with the provided answer, but return select(Reviews.stars).where(Reviews.car_id==cls.id)
also didnt work
I suspected, that maybe the problem is, that some cars dont have reviews (which some cars definitely dont have) and tried to account for that in this approach: reviews = select(case(exists().where(Reviews.car_id==cls.id), func.sum(Reviews.stars)))
, however I am very new to SQLalchemy and dont even know if my idea here makes sense. It surely didnt fix my issue though.
Edit: The Cars database doesnt have a bind key, because its the "first" database. Here are the settings I use for my Flask project:
SQLALCHEMY_DATABASE_URI = 'sqlite:///../Databases/Cars.db'
SQLALCHEMY_BINDS = {
'Reviews': 'sqlite:///../Databases/Reviews.db',
}