API Flask Organiser ses class dans des fichiers

Résolu/Fermé
valenrom97 - 9 juil. 2018 à 23:35
 valenrom97 - 10 juil. 2018 à 20:53
Bonsoir, cela fait quelques temps que je m'intéresse aux applications pour mobile, applications Android en particulier. A plusieurs reprises j'ai tenté d'en réaliser. Voila, je compte me relancer dans la programmation d'une application. Je me suis donc renseigné sur ces histoires d'APIs. Jusqu'à présent je me faisais mes fonctions PHP un peu sales je pense… J'avais donc une API faite maison en PHP. Étant persuadé que ce n'est la bonne manière de procéder j'ai regardé sur internet les alternatives. J'ai trouvé FLASK (Restful) qui permet de mettre au point des APIs en python. Jusqu'à présent j'ai le code suivant:


from flask import Flask
from flaskext.mysql import MySQL
from flask import jsonify
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)
api = Api(app)
mysql = MySQL()

# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = "dbuser"
app.config['MYSQL_DATABASE_PASSWORD'] = "dbpass"
app.config['MYSQL_DATABASE_DB'] = "app_myapp"
app.config['MYSQL_DATABASE_HOST'] = "localhost"

mysql.init_app(app)

# ==================== Import [start] ====================

class User(Resource):
    def get(self, name):

        conn = mysql.connect()
        cursor =conn.cursor()

        parser = reqparse.RequestParser()
        parser.add_argument("key")
        args = parser.parse_args()

        if(args["key"] is None):
            return "No API Key provided", 403

        cursor.callproc('check_api_key',([args["key"]]))
        data = cursor.fetchall()
        if(cursor.rowcount < 1):
            return "Forbidden, bad API Key", 403

        cursor.callproc('get_user_infos',([name]))
        data = cursor.fetchall()
        if(cursor.rowcount < 1):
            return "User not found", 404
        return jsonify(data)

    def post(self, name):
        parser = reqparse.RequestParser()
        parser.add_argument("age")
        parser.add_argument("occupation")
        args = parser.parse_args()

        for user in users:
            if(name == user["name"]):
                return "User with name {} already exists".format(name), 400

        user = {
            "name": name,
            "age": args["age"],
            "occupation": args["occupation"]
        }
        users.append(user)
        return user, 201

    def put(self, name):
        parser = reqparse.RequestParser()
        parser.add_argument("age")
        parser.add_argument("occupation")
        args = parser.parse_args()

        for user in users:
            if(name == user["name"]):
                user["age"] = args["age"]
                user["occupation"] = args["occupation"]
                return user, 200

        user = {
            "name": name,
            "age": args["age"],
            "occupation": args["occupation"]
        }
        users.append(user)
        return user, 201

    def delete(self, name):
        global users
        users = [user for user in users if user["name"] != name]
        return "{} is deleted.".format(name), 200

# ==================== Import [end] ====================

api.add_resource(User, "/user/<string:name>")

#app.run(debug=True)
app.run(host='0.0.0.0')



Je souhaite maintenant ajouter d'autres class, comme une class "message" par exemple, cependant je souhaite séparer ces class dans des fichiers respectifs afin de conserver un code propre. Mais ce n'est pas évident… En PHP je peux faire des "includes" ou des "require_once" afin d'inclure du code "brute". Dans le cas présent, je souhaiterais dégager tout le code qui se trouve entre les deux commentaires "=== Import [start] ===" et "=== Import [end] ===" bref la class User().

Ainsi j'aurais une structure comme suit:
API/
==> main.py
==> Class/
==> Class/class_user.py
==> Class/class_msg.py
==> Class/class_auth.py
==> Class/class_etc.py

Voilà c'est un exemple, cela me paraît simple de procéder ainsi mais je n'y parvient pas pour autant, c'est pourquoi je me tourne vers vous :)
J'ai essayé avec des "import" mais je me retrouve avec des variables non déclarées, je m'y perds ! En théorie je pourrais mettre toutes les class dans le même fichier mais je suis persuadé que c'est une mauvaise pratique.

Si vous avez d'autres idées je suis preneur, par exemple, que pensez-vous de ces fameuses "sql stored procedure" que j'utilise dans mon code ? Certains utilisent "sql sqlalchemy" connaissez-vous la différence avec la méthode de connexion que j'utilise ? Avez-vous une idée sur le sujet ?

Merci beaucoup d'avoir pris le temps de lire ma question jusqu'au bout, et merci d'avance pour vos réponses :)

1 réponse

Pour ceux qui tomberaient sur le même os, j'ai trouvé une solution qui semble bien fonctionner. Voici la structure de mes fichiers:

myapp1/
myapp1/app.py
myapp1/__init__.py
myapp1/resources/
myapp1/resources/cars.py
myapp1/resources/msgs.py
myapp1/resources/users.py
myapp1/resources/db.py
myapp1/resources/__init__.py

Le dossier 'myapp1/resources/' contient mes différents fichiers représentant chacun une class particulière.
Je place également des fichiers '__init__.py' dans chaque dossier, je n'ai pas bien compris le fonctionnement de ces fichiers '__init__.py' mais il semblerait que cela permette à Python de traiter mes dossiers comme des Packages. Pour le moment ces fichiers sont vides, le simple fait qu'ils existent semble permettre cette histoire de Packages (à vérifier).

On a donc 3 class:
-cars
-users
-msgs

Le fichier db.py est un fichier de configuration pour communiquer avec la base de données. Il est inclus dans les class qui utilisent la BDD ainsi que dans le fichier principal 'myapp1/app.py '

Contenu de myapp1/app.py :

from flask import Flask
from flask.ext.mysql import MySQL
from flask import jsonify
from flask_restful import Api, Resource, reqparse
from resources.users import User
from resources.msgs import Msg
from resources.cars import Car
from resources.db import app, api

api.add_resource(User, "/user/<string:name>")
api.add_resource(Msg, "/msg/<string:name>")
api.add_resource(Car, "/car/<string:name>")

#app.run(debug=True)
app.run(host='0.0.0.0')



Contenu de myapp1/resources/cars.py :

from flask_restful import Resource
from resources.db import mysql

class Car(Resource):
    def get(self, name):

        conn = mysql.connect()
        cursor =conn.cursor()

        cursor.callproc('get_car_from_id',(name))
        data = cursor.fetchall()
        if(cursor.rowcount < 1):
            return "no lines... oups.. rien trouve :("
        return data



Contenu de myapp1/resources/db.py :

from flask import Flask
from flask.ext.mysql import MySQL
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)
api = Api(app)
mysql = MySQL()

# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = "dbuser"
app.config['MYSQL_DATABASE_PASSWORD'] = "dbpass"
app.config['MYSQL_DATABASE_DB'] = "app_myapp"
app.config['MYSQL_DATABASE_HOST'] = "localhost"

mysql.init_app(app)



Voilà tout, ce n'est peut-être pas la bonne méthode, sur ma route j'ai également croisé des réponses sur des forums qui préconisaient d'utiliser des " blueprints".

Afin d'ajouter des nouvelles class, il suffit de créer un nouveau fichier dans 'myapp1/resources/' puis de l'importer dans 'myapp1/app.py'. Enfin, il faut ajouter une ligne indiquant comment appeler la ressource depuis l'URL.

api.add_resource(...)
3