J'ai une API flask que j'essaie de créer (c'est un nouveau territoire pour moi) et je ne suis pas satisfait de la manière dont les données sont renvoyées.
Cela fonctionne et se connecte à la base de données et renvoie des données (yay) mais c'est une liste de dictionnaires (boo).
Je voudrais le reformater afin que, quand il est appelé, il semble différent.
modèle de flacon:
{ 'id_': 0, 'rank': 0, 'pokemon': None, 'usage_pct': None, #... to save space but you get the idea 'tier': None }
Qui revient ci-dessous lorsqu'il est appelé:
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
mais je voudrais que cela ressemble à ceci:
def __getitem__(self, key): return self.__dict__[key] __tablename__ = "smogon_usage_stats"
J'ai essayé de manipuler le result
dans la classe Stats
, mais cela génère une erreur (je l'ai depuis supprimé, mais c'était une erreur de ne pas être itérable). Je peux toujours modifier les données avec le code de mon application Web, je suppose, mais je préfère l'avoir emballé et prêt à l'emploi.
j'ai donc trouvé des solutions mais rien n'a encore fonctionné.
Pour rendre l'objet indexable, j'ai ajouté ceci au modèle:
{ "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' } }
et a fait l'instruction de retour de la fonction get de la classe Stats
:
return {"data": {x["pokemon"]: x for x in result}}
mais cela ne m'a donné qu'une seule sortie. ce qui est, je suppose, une amélioration technique. (a également obtenu le même résultat en essayant la réponse proposée ci-dessous
J'ai essayé de le simplifier si quelque chose me manquait peut-être et j'ai arrêté d'essayer d'être sophistiqué avec une ligne, et cela me donne toujours une seule sortie. J'ai vérifié que le result
est une liste d'une longueur de 143
. Mais pour une raison ou une autre, je n'obtiens pas les résultats que je souhaite, et je manque d'espace aérien et d'idées.
[ {'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'} ]
et cela renvoie ceci quand je fais une demande:
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)
aussi, juste pour être sûr, j'ai fait une vérification de type()
sur les objets itérés, et ils ont renvoyé <class '__main__.StatsModel'>
.
5 Réponses :
Je ne sais pas si c'est la meilleure approche, mais cela a fonctionné comme vous le souhaitiez.
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))}
Malheureusement, cela n'a pas fonctionné. J'ai eu une erreur TypeError: 'StatsModel' object is not iterable
Essaye ça. Cela lira chaque élément de votre résultat et l'enregistrera dans un dict. Ensuite, enregistrez ce dict dans un autre dict, selon votre format:
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
Merci pour la réponse, mais cela n'a toujours pas fonctionné. Je ne suis pas sûr de ce qui se passe. J'ai testé en imprimant les valeurs de dict [clé] et elles s'impriment, donc cela fonctionne comme un dictionnaire. C'était le résultat qu'il m'a donné {'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}
et n'a donné aucune erreur.
D'accord, pouvez-vous coller le résultat que vous avez reçu lors de l'exécution du code que j'ai mentionné?
Désolé, mon commentaire précédent était déroutant, il a été produit lorsque j'ai essayé d'utiliser votre solution.
D'accord. Pouvez-vous dire quel est le type de données du résultat? Essayez le type(result)
type(result)
m'a donné <class 'list'>
et chaque type
de chaque élément dans result
est <class '__main__.StatsModel'>
J'ai un peu modifié le code. Pouvez-vous essayer ça? J'ai fouillé et j'ai trouvé que pour extraire les données des objets SQLAlchemy, vous devriez les utiliser directement dans la boucle for, au lieu de les enregistrer et de les appeler à nouveau. Voici un exemple de lien
Je l'ai essayé et j'ai obtenu le même résultat que mon premier commentaire. Cependant, j'ai ajouté une instruction d'impression pour déboguer et j'ai obtenu ceci: {'data': {'goldeen': <StatsModel 669508>, 'vullaby': <StatsModel 669411>, 'onix': <StatsModel 669412>, 'timburr': <StatsModel 669413>}}
OK, essayez x .__ dict__. Cela aidera à accéder au dict interne de SQLAlchemy. J'ai édité le code ci-dessus.
Soupir. Cela n'a pas fonctionné non plus. J'ai essayé le codage en dur dans les valeurs de dict
même: dict1[x["pokemon"]] = {'id_': x["id_"], 'rank': x["rank"], 'pokemon': x["pokemon"], 'usage_pct': x["usage_pct"], 'raw_usage': x["raw_usage"], 'raw_pct': x["raw_pct"], 'real': x["real"], 'real_pct': x["real_pct"], 'dex': x["dex"], 'date': x["date"], 'tier': x["tier"]}
et même cela a donné les mêmes résultats.
x.__dict__
imprimé: {'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fa5d616b310>, 'date': '2020-04', 'real_pct': 6.806, 'raw_pct': 6.328, 'raw_usage': 5200, 'pokemon': 'koffing', 'id_': 669434, 'tier': 'gen8lc-1500', 'dex': 109, 'real': 4429, 'usage_pct': 6.79894, 'rank': 26}
Je pense que cette chose x .__ dict__ fonctionne. Il vous suffit de supprimer les clés '_sa_instance_state'. Droite?
On l'aurait pensé. Cependant, pas de chance après avoir essayé avec la compréhension de liste ou avec .pop('_sa_insatnce_state')
ou avec del
. J'ai obtenu le résultat: TypeError: 'InstanceState' object is not subscriptable
. J'ai utilisé à la fois x.pop
et x.__dict__.pop
et le meilleur des cas est le résultat empy dict
dans le commentaire supérieur. c'est comme une de ces choses où le jus ne vaut pas la peine d'être pressé.
Je n'ai pas utilisé Flask-RESTful, mais cela semble être un simple problème de transformation de données.
Dans un shell Python interactif:
>>> 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}}} >>>
À moins que je ne manque quelque chose, il semble que la transformed
soit exactement ce que vous recherchez. (Qu'est-ce que je rate?)
Il pourrait être, bien sûr, ce result
dans votre code n'est pas une list
, tandis que rowList
ci - dessus est. Dans ce cas, envisagez d'abord de la convertir en liste (et de convertir les enregistrements contenus en dictionnaires).
Pour changer simplement entre
new_result = {"data": {}} for item in dictionary: new_result["data"][item["pokemon"]] = item
et
{"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" } }}
Essayez simplement
[ {"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"} ]
où dictionary
est le premier et new_result
le second.
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