目前我有一个名为calc_totalPrice的混合属性的类。在这个混合属性中,我尝试根据与另一个表(通过中间表)相关的多个项来计算某物的总价。这是班级:
@app.route('/api/tasks/getrecipetypeinbudget/<float:budget>', methods=['GET'])
def GetRecipeTypeInBudget(budget):
recipeIngredients = Recipe.query.filter(Recipe.calc_totalPrice <= budget)
serialiser = RecipeSerializer(many = True)
result = serialiser.dump(recipeIngredients)
return jsonify({'Recipe' : result})
我尝试运行的方法如下:
* Running on http://0.0.0.0:53827/ (Press CTRL+C to quit)
127.0.0.1 - - [10/Apr/2018 00:27:11] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:11] "GET /static/content/bootstrap.min.css HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:12] "GET /static/scripts/modernizr-2.6.2.js HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:12] "GET /static/scripts/respond.js HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:12] "GET /static/content/site.css HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:12] "GET /static/scripts/bootstrap.js HTTP/1.1" 200 -
127.0.0.1 - - [10/Apr/2018 00:27:12] "GET /static/scripts/jquery-1.10.2.js HTTP/1.1" 200 -
sum(ingredient.price)
[2018-04-10 00:27:27,382] ERROR in app: Exception on /api/tasks/getrecipetypeinbudget/5.0 [GET]
Traceback (most recent call last):
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 1193, in _execute_context
context)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\default.py", line 507, in do_execute
cursor.execute(statement, parameters)
psycopg2.ProgrammingError: aggregate functions are not allowed in WHERE
LINE 3: WHERE sum(ingredient.price) <= 5.0
^
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\app\views.py", line 46, in GetRecipeTypeInBudget
result = serialiser.dump(recipeIngredients)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\marshmallow\schema.py", line 481, in dump
obj = list(obj)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\orm\query.py", line 2889, in __iter__
return self._execute_and_instances(context)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\orm\query.py", line 2912, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 948, in execute
return meth(self, multiparams, params)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\sql\elements.py", line 269, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 1060, in _execute_clauseelement
compiled_sql, distilled_params
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 1200, in _execute_context
context)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 1413, in _handle_dbapi_exception
exc_info
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\util\compat.py", line 203, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\util\compat.py", line 186, in reraise
raise value.with_traceback(tb)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\base.py", line 1193, in _execute_context
context)
File "C:\Users\Samuel.endeva\Google Drive\Projects\Sal\SalApp\SalApp\FlaskWebAPI\env\lib\site-packages\sqlalchemy\engine\default.py", line 507, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) aggregate functions are not allowed in WHERE
LINE 3: WHERE sum(ingredient.price) <= 5.0
^
[SQL: 'SELECT recipe."recipeID" AS "recipe_recipeID", recipe."userID" AS "recipe_userID", recipe.name AS recipe_name, recipe.description AS recipe_description \nFROM recipe, ingredient \nWHERE sum(ingredient.price) <= %(param_1)s'] [parameters: {'param_1': 5.0}] (Background on this error at: http://sqlalche.me/e/f405)
127.0.0.1 - - [10/Apr/2018 00:27:27] "GET /api/tasks/getrecipetypeinbudget/5.0 HTTP/1.1" 500 -
不幸的是,我收到以下错误:
@hybrid_property
def calc_totalPrice(self):
calculatedPrice = func.sum(Ingredient.price).label('price')
recipeIngredientJoin = Recipe.query.join(IngredientsToRecipe,Recipe.recipeID == IngredientsToRecipe.recipeID).join(Ingredient,IngredientsToRecipe.siin == Ingredient.siin).add_columns(calculatedPrice).group_by(Recipe.recipeID).filter(Recipe.recipeID == self.recipeID).first()
return recipeIngredientJoin.price
我今天刚刚开始使用Flask,虽然我可以理解我哪里出错,但我不知道如何解决它。有什么想法或替代方案吗?
更新: 我取得了一些成功。我将混合属性更改为:
var balls = [];
var obstacle;
function setup() {
createCanvas(windowWidth, windowHeight);
obstacle = new Obstacle();
}
function draw() {
background(75);
obstacle.display();
for(var i = 0; i < balls.length; i++) {
balls[i].display();
balls[i].update();
balls[i].edges();
}
}
function mousePressed() {
balls.push(new Ball(mouseX, mouseY));
}
function Ball(x, y) {
this.x = x;
this.y = y;
this.r = 15;
this.gravity = 0.5;
this.velocity = 0;
this.display = function() {
fill(255, 0 , 100);
stroke(255);
ellipse(this.x, this.y, this.r*2);
}
this.update = function() {
this.velocity += this.gravity;
this.y += this.velocity;
}
this.edges = function() {
if (this.y >= windowHeight - this.r*2) {
this.y = windowHeight - this.r*2;
this.velocity = this.velocity* -1;
this.gravity = this.gravity * 1.1;
}
}
}
function Obstacle() {
this.x = windowWidth - windowWidth;
this.y = windowHeight / 2;
this.w = 200;
this.h = 25;
this.display = function() {
fill(0);
stroke(255);
rect(this.x, this.y, this.w, this.h);
}
}
function RectCircleColliding(Ball, Obstacle) {
var distX = Math.abs(Ball.x - Obstacle.x - Obstacle.w / 2);
var distY = Math.abs(Ball.y - Obstacle.y - Obstacle.h / 2);
if (distX > (Obstacle.w / 2 + Ball.r)) {
return false;
console.log("no hit");
}
if (distY > (Obstacle.h / 2 + Ball.r)) {
return false;
console.log("no hit");
}
if (distX <= (Obstacle.w / 2)) {
return true;
console.log("hit");
}
if (distY <= (Obstacle.h / 2)) {
return true;
console.log("hit");
}
var dx = distX - Obstacle.w / 2;
var dy = distY - Obstacle.h / 2;
return (dx * dx + dy * dy <= (Ball.r * Ball.r));
}
问题是,这是将所有价格列设置为第一个值。使用.first到.all返回一个列表,这很不方便。任何提示或想法?
答案 0 :(得分:2)
你对混合财产的第一次尝试有点过于混乱;它执行查询,但返回一个SQL表达式对象,即聚合函数表达式,它在WHERE子句中不可用。你应该将两者分开:
class Recipe(db.Model):
# The relationship makes defining the hybrid property easier on the
# Python side.
ingredients = db.relationship('Ingredient', secondary='ingredients_to_recipe')
@hybrid_property
def calc_totalPrice(self):
# Take a simple sum over ingredient prices, using the
# relationship. This is meant to take place in Python.
return sum(i.price for i in self.ingredients)
@calc_totalPrice.expression
def calc_totalPrice(cls):
# Create a correlated subquery, usable in WHERE clauses, SELECT lists etc.
# In other words, this is for SQL.
return db.session.query(func.sum(Ingredient.price)).\
join(IngredientsToRecipe).\
filter_by(recipeID=cls.recipeID).\
label('price')
这样的混合属性便于检查单个或几个Recipe实例的总价。在给定合适的索引的情况下,即使对于较大的查询,相关的标量子查询也可能表现得很好 - 例如,IngredientsToRecipe
按recipeID
查找应该变得容易。但这不是你唯一的选择。如果您需要更具体的查询,请务必使用此类查询。 SQLAlchemy的优点在于它允许将关系数据库的功能与ORM并排使用:
price = func.sum(Ingredient.price).label('price')
# Note that using `User` as the name of an attribute is a bit confusing.
# Doing `group_by(Recipe.recipeID)` instead of `group_by(Recipe)` is ok,
# at least in databases that recognize that the other non-aggregate columns
# have a functional dependency to the grouping column, or in other words
# `Recipe.recipeID` defines the rest. Be wary about databases that allow
# non-aggregate columns in a group even without such a dependency. You
# will probably get indeterminate results.
cheap_recipes = db.session.query(Recipe, price).\
filter_by(User=some_user).\
join(Recipe.ingredients).\
group_by(Recipe.recipeID).\
having(price <= 5.0).\
all()