#python #json #flask #request #flask-sqlalchemy
#python #json #flask #запрос #flask-sqlalchemy
Вопрос:
У меня есть API flask, который я пытаюсь создать (это новая территория для меня), и я недоволен тем, как возвращаются данные.
Он работает и подключается к базе данных и возвращает данные (yay), но это список словарей (boo).
Я хотел бы переформатировать его так, чтобы при его вызове он выглядел иначе.
модель flask:
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)
Который возвращается ниже при вызове:
[
{'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'}
]
но я бы хотел, чтобы это выглядело так:
{
"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'
}
}
Я пытался манипулировать result
в Stats
классе, но он выдает ошибку (с тех пор я ее удалил, но это была ошибка, связанная с невозможностью повторения). Я думаю, я всегда могу изменить данные с помощью своего кода веб-приложения, но я бы предпочел, чтобы он был упакован и готов к работе.
редактировать 1
итак, я нашел несколько решений, но пока ничего не сработало.
Чтобы сделать объект подписываемым, я добавил это в модель:
def __getitem__(self, key):
return self.__dict__[key]
__tablename__ = "smogon_usage_stats"
и сделал Stats
оператор возврата функции get класса:
return {"data": {x["pokemon"]: x for x in result}}
но это дало мне только один вывод. я думаю, это техническое улучшение. (также получил тот же результат при попытке предложить ответ ниже
редактировать 2
Я попытался упростить его, если, возможно, я что-то упустил, и перестал пытаться быть причудливым с одной строкой, и это все еще дает мне один вывод. Я проверил, что result
это список с длиной 143
. Но по той или иной причине я не получаю желаемых результатов, и у меня заканчивается пространство и идеи.
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
и это возвращает это, когда я выполняю запрос:
{
'id_': 0,
'rank': 0,
'pokemon': None,
'usage_pct': None,
#... to save space but you get the idea
'tier': None
}
кроме того, на всякий случай я type()
проверил повторяющиеся объекты, и они вернулись <class '__main__.StatsModel'>
.
Ответ №1:
Я не уверен, что это лучший подход, но он работал так, как вы хотели.
class Stats(Resource):
@marshal_with(resource_fields)
def get(self, date, tier):
result = StatsModel.query.filter_by(date=date, tier=tier "-1500").all()
return {"data": list(map(lambda x: {x['pokemon']: x}, result))}
Комментарии:
1. К сожалению, это не сработало. Я получил сообщение об ошибке
TypeError: 'StatsModel' object is not iterable
Ответ №2:
Попробуйте это. Это позволит считывать каждый элемент из вашего результата и сохранять его в dict. Затем сохраните этот dict в другой dict в соответствии с вашим форматом:
class Stats(Resource):
@marshal_with(resource_fields)
def get(self, date, tier):
dict1, dict2 = {}, {}
#result = StatsModel.query.filter_by(date=date, tier=tier "-1500").all()
for x in StatsModel.query.filter_by(date=date, tier=tier "-1500").all():
dict1[x["pokemon"]] = x.__dict__
dict2["data"] = dict1
return dict2
Комментарии:
1. Спасибо за ответ, но он все равно не сработал. Я не уверен, что происходит. Я протестировал, напечатав значения dict [key], и они печатаются, поэтому он функционирует как словарь. Это был результат, который он дал мне
{'id_': 0, 'rank': 0, 'pokemon': None, 'usage_pct': None, 'raw_usage': 0, 'raw_pct': None, 'real': 0, 'real_pct': None, 'dex': 0, 'date': None, 'tier': None}
, и не дал ошибок.2. Хорошо, можете ли вы вставить вывод, полученный при выполнении кода, о котором я упоминал?
3. Извините, мой предыдущий комментарий был сбивающим с толку, это было создано, когда я пытался использовать ваше решение.
4. Хорошо. Можете ли вы сказать, каков тип данных результата? Попробуйте
type(result)
5.
type(result)
дал мне<class 'list'>
и каждомуtype
из каждого элемента вresult
<class '__main__.StatsModel'>
Ответ №3:
Я не использовал Flask-RESTful, но это похоже на простую проблему преобразования данных.
В интерактивной оболочке Python:
>>> rowList = [
... {'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'}
... ]
>>>
>>> transformed = {"data": {row['pokemon']: row for row in rowList}}
>>>
>>> import pprint
>>> pprint.pprint(transformed)
{'data': {'cosmog': {'date': '2020-04',
'dex': 789,
'id_': 669553,
'pokemon': 'cosmog',
'rank': 156,
'raw_pct': 0.032,
'raw_usage': 26,
'real': 19,
'real_pct': 0.029,
'tier': 'gen8lc-1500',
'usage_pct': 0.0199},
'milcery': {'date': '2020-04',
'dex': 868,
'id_': 669552,
'pokemon': 'milcery',
'rank': 154,
'raw_pct': 0.131,
'raw_usage': 108,
'real': 87,
'real_pct': 0.134,
'tier': 'gen8lc-1500',
'usage_pct': 0.0672},
'snorunt': {'date': '2020-04',
'dex': 361,
'id_': 669551,
'pokemon': 'snorunt',
'rank': 153,
'raw_pct': 0.127,
'raw_usage': 104,
'real': 96,
'real_pct': 0.148,
'tier': 'gen8lc-1500',
'usage_pct': 0.07347}}}
>>>
Если я чего-то не упустил, похоже, transformed
это именно то, что вы ищете. (Чего мне не хватает?)
Конечно, может быть, что result
в вашем коде нет a list
, в то время rowList
как выше есть. В этом случае сначала рассмотрите возможность преобразования его в список (и преобразования содержащихся записей в dicts).
Ответ №4:
Для простого переключения между
[
{"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"}
]
и
{"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"
}
}}
Попробуйте просто
new_result = {"data": {}}
for item in dictionary:
new_result["data"][item["pokemon"]] = item
где dictionary
первый и new_result
второй.
Ответ №5:
class Stats(Resource):
@marshal_with(resource_fields)
def get(self, date, tier):
data_dict, result = {}, {}
for item in StatsModel.query.filter_by(date=date, tier=tier "-1500").all():
data_dict[item["pokemon"]] = item
result["data"] = data_dict
return result