I have a flask api I'm trying to make (it's new territory for me) and I'm unsatisfied with how the data is returned.
It works and connects to the database and returns data (yay) but it's a list of dictionaries (boo).
I would like reformat it so when it is called it appears different.
flask model:
from flask import Flask, jsonify
from flask_restful import Api, Resource, reqparse, abort, fields, marshal_with
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
api = Api(app)
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgres://postgres:[password]@127.0.0.1:5432/usagestats'
db = SQLAlchemy(app)
class StatsModel(db.Model):
#added in from edit1
def __getitem__(self, key):
return self.__dict__[key]
__tablename__ = "smogon_usage_stats"
id_ = db.Column(db.Integer, primary_key=True)
rank = db.Column(db.Integer, nullable=False)
pokemon = db.Column(db.String(50), nullable=False)
usage_pct = db.Column(db.Float, nullable=False)
raw_usage = db.Column(db.Integer, nullable=False)
raw_pct = db.Column(db.Float, nullable=False)
real = db.Column(db.Integer, nullable=False)
real_pct = db.Column(db.Float, nullable=False)
dex = db.Column(db.Integer, nullable=False)
date = db.Column(db.String(10), nullable=False)
tier = db.Column(db.String(50), nullable=False)
def __repr__(self):
return f"Stats(id = {id_}, rank = {rank}, pokemon = {pokemon}, usage_pct = {usage_pct}, raw_usage = {raw_usage}, raw_pct = {raw_pct}, real = {real}, real_pct = {real_pct})"
resource_fields = {
'id_': fields.Integer,
'rank': fields.Integer,
'pokemon': fields.String,
'usage_pct': fields.Float,
'raw_usage': fields.Integer,
'raw_pct': fields.Float,
'real': fields.Integer,
'real_pct': fields.Float,
'dex': fields.Integer,
'date': fields.String,
'tier': fields.String
}
class Stats(Resource):
@marshal_with(resource_fields)
def get(self, date, tier):
result = StatsModel.query.filter_by(date=date, tier=tier + "-1500").all()
return result
api.add_resource(Stats, "/stats/<string:date>/<string:tier>-1500")
if __name__ == "__main__":
app.run(host='127.0.0.1', port=3000, debug=True)
Which returns below when called:
[
{'id_': 669551, 'rank': 153, 'pokemon': 'snorunt', 'usage_pct': 0.07347, 'raw_usage': 104, 'raw_pct': 0.127, 'real': 96, 'real_pct': 0.148, 'dex': 361, 'date': '2020-04', 'tier': 'gen8lc-1500'},
{'id_': 669552, 'rank': 154, 'pokemon': 'milcery', 'usage_pct': 0.0672, 'raw_usage': 108, 'raw_pct': 0.131, 'real': 87, 'real_pct': 0.134, 'dex': 868, 'date': '2020-04', 'tier': 'gen8lc-1500'},
{'id_': 669553, 'rank': 156, 'pokemon': 'cosmog', 'usage_pct': 0.0199, 'raw_usage': 26, 'raw_pct': 0.032, 'real': 19, 'real_pct': 0.029, 'dex': 789, 'date': '2020-04', 'tier': 'gen8lc-1500'}
]
but I would like it to look like this:
{
"data":
'snorunt': {
'id_': 669551, 'rank': 153, 'pokemon': 'snorunt', 'usage_pct': 0.07347, 'raw_usage': 104, 'raw_pct': 0.127, 'real': 96, 'real_pct': 0.148, 'dex': 361, 'date': '2020-04', 'tier': 'gen8lc-1500'},
'milcery': {
'id_': 669552, 'rank': 154, 'pokemon': 'milcery', 'usage_pct': 0.0672, 'raw_usage': 108, 'raw_pct': 0.131, 'real': 87, 'real_pct': 0.134, 'dex': 868, 'date': '2020-04', 'tier': 'gen8lc-1500'},
'cosmog': {
'id_': 669553, 'rank': 156, 'pokemon': 'cosmog', 'usage_pct': 0.0199, 'raw_usage': 26, 'raw_pct': 0.032, 'real': 19, 'real_pct': 0.029, 'dex': 789, 'date': '2020-04', 'tier': 'gen8lc-1500'
}
}
I've tried manipulating result
in the Stats
class, but it throws an error (I've since taken it out, but it was an error of not being iterable). I can always change the data with my webapp code, I guess, but I'd rather have it packaged and ready to go.
edit 1
so I found some solutions but nothing has quite worked yet.
To make the object subscriptable, I added this to the model:
def __getitem__(self, key):
return self.__dict__[key]
__tablename__ = "smogon_usage_stats"
and made the Stats
class' get function return statement:
return {"data": {x["pokemon"]: x for x in result}}
but that only gave me a single output. which is I guess technically improvement. (also got the same result when trying the proposed answer below
edit 2
I tried simplifying it if maybe I was missing something and stopped trying to be fancy with one line, and it's still giving me a single output. I've verified that result
is a list with length 143
. But for some reason or other, I'm not getting the results I want, and I'm running out of airspace and ideas.
class Stats(Resource):
@marshal_with(resource_fields)
def get(self, date, tier):
result = StatsModel.query.filter_by(date=date, tier=tier + "-1500").all()
resp = {"data":{}}
for i in range(len(result)):
t = {result[i]["pokemon"]: result[i]}
resp["data"].update(t)
return resp
and that returns this when I do a request:
{
'id_': 0,
'rank': 0,
'pokemon': None,
'usage_pct': None,
#... to save space but you get the idea
'tier': None
}
also, just to be sure, I did a type()
check on the iterated objects, and they returned <class '__main__.StatsModel'>
.
from flask -- change output from response
No comments:
Post a Comment