簡體   English   中英

Flask + SQLalchemy計算字段過濾器

[英]Flask +SQLalchemy calculated field filter

目前,我有一個名為calc_totalPrice的混合屬性的類。 在此混合屬性中,我嘗試根據與另一個表(通過中間表)相關的多個項目來計算某事物的總價。 這是課程:

class Recipe(db.Model):

    recipeID = Column(Integer, primary_key=True)
    userID = Column(ForeignKey('user.userID'), nullable=False)
    name = Column(String(35), nullable=False)
    description = Column(String(140), nullable=False)

    User = db.relationship('User')

    @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()
        print(calculatedPrice)
        return calculatedPrice

我嘗試運行的方法如下所示:

@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 -

我今天才開始使用Flask,雖然我可以大致了解我要去哪里的問題,但我不知道如何解決。 有什么想法或選擇嗎?

更新:我取得了一些成功。 我將混合屬性更改為:

@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

問題是,這會將所有價格列都設置為第一個值。 使用.first到.all返回一個列表,這很不方便。 有任何提示或想法嗎?

您首次嘗試混合屬性時,有點太混合了。 它執行查詢,但返回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實例的總價。 在給定合適的索引的情況下,即使對於較大的查詢,相關的標recipeID查詢也可能表現良好,例如,應使按recipeID查找IngredientsToRecipe變得容易。 但這不是您唯一的選擇。 如果您需要更具體的查詢,請務必使用此類查詢。 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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM