SQLAlchemy: get average of greatest

I'm trying to create a Flask app (Postgresql database) that shows the latest score from individual players, including the average scores and goals for the team. So a player can have multiple scores, but on the leaderboard I only want to show her most recent score, and show an average number of goals for the whole team, based on the latest score per player.

My Models.py

class Player(db.Model):
    __tablename__ = 'player'
    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String, nullable=False)
    score = db.relationship('Score', backref='player', lazy='dynamic')

    def __init__(self, firstname):
        self.firstname = firstname

    def __repr__(self):
        return '<id {}>'.format(self.id)


class Score(db.Model):
    __tablename__ = 'score'
    id = db.Column(db.Integer, primary_key=True)
    timestamp = db.Column(db.DateTime, nullable=False)
    score = db.Column(db.Integer, nullable=True)
    goals = db.Column(db.Integer, nullable=True)
    player_id = db.Column(db.Integer, db.ForeignKey('player.id'))

    def __init__(self, score, player_id):
        self.timestamp = datetime.now()
        self.score = score
        self.player_id = player_id

    def __repr__(self):
        return '<id {}>'.format(self.id)

My app.py:

@app.route('/', methods=['GET', 'POST'])
@login_required
def home():

    #Latest score per player
    latest_scores_per_player = db.session.query(Player, Score).
        join(Score).
        distinct(Player.id).
        filter(Player.user_id == current_user.id).
        order_by(Player.id, Score.timestamp.desc())

    #Average goals per player
    avarage_goals = db.session.query(Player, Score).
        join(Score).
        distinct(Player.id).
        filter(Player.user_id == current_user.id).
        order_by(Player.id, Score.timestamp.desc()).
        func.avg(Score.goals).label('average').scalar()

    return render_template(
        'home.html',
        latest_scores_per_player=latest_scores_per_player,
        avarage_goals=avarage_goals)

'Latest score per player' is properly showing the latest entered score per player. But 'Average goals per player' returns an error:

AttributeError: 'BaseQuery' object has no attribute 'func'

How can I query for the average goals across all players for only the latest entered score per player?

Update: I feel I'm closer with:

#Average goals per player
avarage_goals = db.session.query(Player, Score, func.avg(Score.goals)).
  join(Score).
  distinct(Player.id).
  filter(Player.user_id == current_user.id).
  order_by(Player.id, Score.timestamp.desc()).scalar()

But not really there yet. Struggling to get the func.avg to work together with the distinct and order_by.


If I understood you correctly, then you simply need to use latest scores per player as a subquery and average the results. Since you store the Query object in latest scores per player, you can use that:

@app.route('/', methods=['GET', 'POST'])
@login_required
def home():

    # Latest score per player
    latest_scores_per_player = db.session.query(Player, Score).
        join(Score).
        distinct(Player.id).
        filter(Player.user_id == current_user.id).
        order_by(Player.id, Score.timestamp.desc())

    # Create a subquery that queries only the goals from the groups
    subq = latest_scores_per_player.
        with_entities(Score.goals).
        subquery()

    #Average goals *across all players*
    average_goals = db.session.query(func.avg(subq.c.goals)).scalar()

    return render_template(
        'home.html',
        latest_scores_per_player=latest_scores_per_player,
        avarage_goals=average_goals)

This will do 2 trips to the DB. You could also just query the results of latest scores per player and calculate the average in Python. With a recent enough version of Python you can use the statistics module:

import statistics

@app.route('/', methods=['GET', 'POST'])
@login_required
def home():

    # Latest score per player
    # Materialize the results to a list here for calculating average
    latest_scores_per_player = db.session.query(Player, Score).
        join(Score).
        distinct(Player.id).
        filter(Player.user_id == current_user.id).
        order_by(Player.id, Score.timestamp.desc()).
        all()

    average_goals = None
    if latest_scores_per_player:
        average_goals = statistics.mean(
            score.goals for player, score in latest_scores_per_player)

    return render_template(
        'home.html',
        latest_scores_per_player=latest_scores_per_player,
        avarage_goals=average_goals)

Or do it yourself (add necessary conversions to float, if using Python 2):

    average_goals = None
    if latest_scores_per_player:
        average_goals = sum(
            score.goals for player, score in latest_scores_per_player)
        average_goals /= len(latest_scores_per_player)
链接地址: http://www.djcxy.com/p/38720.html

上一篇: Lucene,Sphinx,Postgresql,MySQL?

下一篇: SQLAlchemy:获得最大的平均值