Usar ORM ao invés de SQL bruto

This commit is contained in:
Augusto Gunsch 2021-10-17 18:59:27 -03:00
parent ad5aa0d69e
commit bbfddcae56
No known key found for this signature in database
GPG Key ID: F7EEFE29825C72DC
16 changed files with 157 additions and 129 deletions

View File

@ -1,2 +1,2 @@
requirements:
pipreqs
pipreqs --force

12
api/app.py Executable file
View File

@ -0,0 +1,12 @@
#!/bin/python3
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
app = Flask(__name__)
app.config.from_object('api.config')
db = SQLAlchemy(app)
ma = Marshmallow(app)
import api.models.trainer
import api.routes.routes

2
api/config.py Normal file
View File

@ -0,0 +1,2 @@
SQLALCHEMY_DATABASE_URI = 'sqlite:///database.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

44
api/models/trainer.py Normal file
View File

@ -0,0 +1,44 @@
from api.app import db, ma
from werkzeug.security import generate_password_hash
from enum import Enum
teams = (
"Team Valor",
"Team Instinct",
"Team Mystic"
)
class InvalidTeam(Exception):
pass
# modelo do Trainer para o SQLAlchemy
class Trainer(db.Model):
__tablename__ = "trainers"
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
nickname = db.Column(db.String(20), nullable=False, index=True)
first_name = db.Column(db.String(30), nullable=False)
last_name = db.Column(db.String(30), nullable=False)
email = db.Column(db.String(60), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
team = db.Column(db.String(10), nullable=False)
pokemons_owned = db.Column(db.Integer, default=0)
def __init__(self, nickname, first_name, last_name, email, password, team):
if team not in teams:
raise InvalidTeam()
self.nickname = nickname
self.first_name = first_name
self.last_name = last_name
self.email = email
self.password = generate_password_hash(password)
self.team = team
# schema do Marshmallow
class TrainerSchema(ma.Schema):
class Meta:
fields = ('id', 'nickname', 'first_name', 'last_name', 'email', 'team', 'pokemons_owned')
trainer_schema = TrainerSchema()
trainer_schemas = TrainerSchema(many=True)

0
api/routes/__init__.py Normal file
View File

12
api/routes/routes.py Normal file
View File

@ -0,0 +1,12 @@
from flask import request
from sqlite3 import ProgrammingError, IntegrityError
from api.app import app, db
from api.views import trainer
@app.route('/trainer', methods=['GET'])
def route_get_trainers():
return trainer.get_trainers()
@app.route('/trainer', methods=['POST'])
def route_create_trainer():
return trainer.post_trainer()

0
api/views/__init__.py Normal file
View File

80
api/views/trainer.py Normal file
View File

@ -0,0 +1,80 @@
from sqlalchemy.exc import NoResultFound, IntegrityError
from api.models.trainer import Trainer, trainer_schema, trainer_schemas, InvalidTeam
from api.app import db
from flask import request, jsonify
# função auxiliar
def error(code, type, message, http_code=400):
return ({
"code": code,
"type": type,
"message": message
}, http_code)
def get_trainers():
args = request.args
try:
limit = int(args.get("limit", -1))
offset = int(args.get("offset", 0))
except ValueError:
return error(0, "ParsingError", "Couldn't parse parameter as integer")
if limit < -1 or offset < 0:
return error(1, "ParsingError", "Expected positive integer as parameter")
nickname = args.get("nickname", "")
nickname_contains = args.get("nickname_contains", "")
try:
if nickname:
if nickname_contains:
return error(2, "ConflictingParameters", "nickname and nickname_contains are mutually exclusive")
query = Trainer.query.filter_by(nickname=nickname)
return trainer_schemas.dumps(query.all())
else: # se nickname_contains também está vazio, retornará todos trainers
pattern = '%'+nickname_contains+'%'
query = Trainer.query.filter(Trainer.nickname.like(pattern))
return trainer_schemas.dumps(query.all())
except NoResultFound:
return jsonify([])
def post_trainer():
try:
json = request.get_json()
except:
return error(3, "ParsingError", "Failed to parse JSON content")
if type(json) is not dict:
return error(4, "ParsingError", "Expected JSON object as content")
try:
nickname = json["nickname"]
first_name = json["first_name"]
last_name = json["last_name"]
email = json["email"]
password = json["password"]
team = json["team"]
trainer = Trainer(
nickname=nickname,
first_name=first_name,
last_name=last_name,
email=email,
password=password,
team=team
)
db.session.add(trainer)
db.session.commit()
return trainer_schema.dump(trainer)
except InvalidTeam:
return error(5, "ParsingError", "Field team is invalid")
except KeyError:
return error(6, "ParsingError", "Missing JSON object fields")
except IntegrityError:
return error(7, "ConflictingResource", "Trainer with the same nickname or email already exists", http_code=500)

View File

@ -1,7 +0,0 @@
from flask import Flask
app = Flask(__name__)
app.debug = True
from .routes import routes

View File

@ -1,55 +0,0 @@
from .trainer import Trainer
import sqlite3
class Database:
def __init__(self, db_file):
self.db_file = db_file
def create_trainers_table(self):
con = sqlite3.connect(self.db_file)
con.execute("""
CREATE TABLE IF NOT EXISTS Trainers
(
id INT UNSIGNED UNIQUE,
nickname TINYTEXT PRIMARY KEY,
first_name TINYTEXT,
last_name TINYTEXT,
email TINYTEXT,
password TINYTEXT,
team TINYTEXT,
pokemons_owned INT UNSIGNED
)
""")
con.commit()
con.close()
def __get_trainers(self, sql):
con = sqlite3.connect(self.db_file)
trainers = []
for row in con.execute(*sql):
trainers.append(Trainer(*row).__dict__)
con.close()
return trainers
def get_trainer_by_nickname(self, nickname, limit, offset):
return self.__get_trainers(("SELECT * FROM Trainers WHERE nickname = ? LIMIT ? OFFSET ?", (nickname, limit, offset)))
def get_trainers_by_nickname_contains(self, contains, limit, offset):
return self.__get_trainers(("SELECT * FROM Trainers WHERE nickname LIKE ? LIMIT ? OFFSET ?", ("%" + contains + "%", limit, offset)))
def insert_trainer(self, trainer):
con = sqlite3.connect(self.db_file)
con.execute("INSERT INTO Trainers VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
(trainer.id, trainer.nickname,
trainer.first_name, trainer.last_name,
trainer.email, trainer.password,
trainer.team, trainer.pokemons_owned)
)
con.commit()
con.close()
db = Database("database.db")

View File

@ -1,39 +0,0 @@
from flask import request, jsonify
from app import app
from app.database import db
@app.route('/trainer', methods=['GET'])
def route_get_trainers():
args = request.args
try:
limit = int(args.get("limit", -1))
offset = int(args.get("offset", 0))
except ValueError:
return {
"code": 1,
"type": "Integer parsing error",
"message": "Couldn't parse parameter as integer"
}, 500
if limit < -1 or offset < 0:
return {
"code": 2,
"type": "Integer parsing error",
"message": "Expected positive integer as parameter"
}, 500
nickname = args.get("nickname", "")
nickname_contains = args.get("nickname_contains", "")
if nickname and nickname_contains:
return {
"code": 3,
"type": "Invalid parameter",
"message": "Parameters \"nickname\" and \"nickname_contains\" are mutually exclusive"
}, 500
if nickname:
return jsonify(db.get_trainer_by_nickname(nickname, limit, offset))
else:
return jsonify(db.get_trainers_by_nickname_contains(nickname_contains, limit, offset))

View File

@ -1,18 +0,0 @@
class Trainer:
def __init__(self,
id,
nickname,
first_name,
last_name,
email,
password,
team,
pokemons_owned):
self.id = id
self.nickname = nickname
self.first_name = first_name
self.last_name = last_name
self.email = email
self.password = password
self.team = team
self.pokemons_owned = pokemons_owned

View File

@ -1,5 +1,3 @@
#!/bin/python3
from app.database import db
from app.trainer import Trainer
db.create_trainers_table()
from api.app import db
db.create_all()

View File

@ -1 +1,5 @@
flask_marshmallow==0.14.0
Flask_SQLAlchemy==2.5.1
Werkzeug==2.0.2
Flask==2.0.2
SQLAlchemy==1.4.25

5
run.py
View File

@ -1,5 +0,0 @@
#!/bin/python3
from app import app
if __name__ == "__main__":
app.run()