Usar ORM ao invés de SQL bruto
This commit is contained in:
parent
ad5aa0d69e
commit
bbfddcae56
|
@ -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
|
|
@ -0,0 +1,2 @@
|
||||||
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///database.db'
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
@ -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,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,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)
|
|
@ -1,7 +0,0 @@
|
||||||
from flask import Flask
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
app.debug = True
|
|
||||||
|
|
||||||
from .routes import routes
|
|
|
@ -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")
|
|
|
@ -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))
|
|
|
@ -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
|
|
|
@ -1,5 +1,3 @@
|
||||||
#!/bin/python3
|
#!/bin/python3
|
||||||
from app.database import db
|
from api.app import db
|
||||||
from app.trainer import Trainer
|
db.create_all()
|
||||||
|
|
||||||
db.create_trainers_table()
|
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
|
flask_marshmallow==0.14.0
|
||||||
|
Flask_SQLAlchemy==2.5.1
|
||||||
|
Werkzeug==2.0.2
|
||||||
Flask==2.0.2
|
Flask==2.0.2
|
||||||
|
SQLAlchemy==1.4.25
|
||||||
|
|
Loading…
Reference in New Issue