Que tan "micro" framework es...?
https://slides.com/superdiana/esflask
$whoami
Diana Rodríguez
Google Developer Expert:
- Web Technologies - Google Maps Platform
- Google Cloud Platform - Firebase
Auth0 Ambassador
Microsoft MVP: Developer Technologies
Python Developer Advocate @ Vonage
🚀 https://superdi.dev
🐦 @cotufa82
Una persona que todos los días aprende algo nuevo!!
$whoami-NOT
-
Framework Agnostic
-
Weapon of Choice
-
Zero Wars/Mobs/Cults
-
Ejemplos "obvios"
🛁🧼
Flask
"Flask es un microframework de Python que está basado en Werkzeug, Jinja 2 y buenas intenciones. Mediante Flask podemos construir aplicaciones Web y Restful con Python de una forma extraordinariamente sencilla."
"Microframework"
Pros!
- Curva de aprendizaje ✅
- "production ready" ✅
- Buen manejo de rutas
- Se pueden usar sesiones
- Extensible ✅
- Excelente documentacion ✅
Cons
- Ninguno
- Complejo para apps muy grandes
- Comunidad
- No tiene admin
- Sin login o auth
- Sin ORM
- Las migraciones pueden ser complicadas
Full
Stack
Flask!?
A vuelo de pájaro!
Contexto...
### layout.hmtl
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='css/materialize.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block head %}{% endblock %}
</head>
<body>
<header class="container-fluid">
<nav class="teal">
<div class="container">
<div class="row">
<div class="col">
<a href="#" class="brand-logo"><i class="material-icons logo">record_voice_over</i> Scout = Nexmo + Nightscout</a>
</div>
</div>
</div>
</nav>
</header>
<main class="container">
{% block content %}{% endblock %}
</main>
<footer class="page-footer teal">
<div class="footer-copyright">
<div class="container">
Made By <a class="brown-text text-lighten-3">CodeON</a>
</div>
</div>
</footer>
<script language="javascript" src="{{ url_for('static', filename='js/materialize.min.js') }}"></script>
{% block script %}{% endblock %}
</body>
</html>
### login.html
{% extends "layout.html" %}
{% block head %}
<script src="https://apis.google.com/js/platform.js" async defer></script>
<meta name="google-signin-client_id" content="{{ client_id }}">
{% endblock %}
{% block content %}
<div id="user" class="guest">Welcome guest, You need to authenticate</div>
<div class="row">
<div class="col s6 offset-s3">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">Login To Enter Scout</span>
<p>This application is going to allow you configure some alerts to your Phone, a Favorite Phone or 5 contact Phones. If you use a nightscout device and you have your api available for external queries. You can use this server and when the glucose level is to low and to high you will receive a call to indicates the glucose level. If you not Answer the call then an sms is sent to your favorite number and other 5 contact numbers.</p>
<div class="g-signin2" data-onsuccess="onSignIn"></div>
</div>
</div>
</div>
</div>
<script language="javascript">
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
if(profile.getId()!==null && profile.getId()!==undefined){
var xhr = new XMLHttpRequest();
xhr.open('POST', '{{ site_url|safe }}/login');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
//Authenticated so redirect to index
window.location = "{{ site_url|safe }}/";
};
xhr.send('idtoken=' + googleUser.getAuthResponse().id_token + "&username=" + profile.getName() + "&email=" + profile.getEmail());
}
}
</script>
### app.py
@app.route('/login',methods=["POST"])
def login():
try:
token = request.form.get("idtoken")
client_id = os.getenv("GOOGLE_CLIENT_ID")
infoid = id_token.verify_oauth2_token(token, google.auth.transport.requests.Request(), client_id)
if infoid['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')
userid = infoid['sub']
#Here is a good place to create the session
session["user"] = {"userid": userid, "username": request.form.get("username"), "email": request.form.get("email")}
return userid
except ValueError:
return "Error"
pass
from opentok import OpenTok, Roles
from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_socketio import SocketIO, join_room, emit, send
from dotenv import load_dotenv
from os import environ
from os.path import join, dirname
import requests, json
# Get env vars from file
envpath = join(dirname(__file__), "./.env")
load_dotenv(envpath)
app = Flask(__name__)
CORS(app)
socketio = SocketIO(app, cors_allowed_origins="*")
opentok = OpenTok(environ.get("OPENTOK_API_KEY"), environ.get("OPENTOK_API_SECRET"))
rooms = {}
playlist_videos = {}
queue_count = {}
@app.route("/api/get-token", methods=["POST"])
def get_token():
global rooms
global queue_count
# global playlist_room
params = request.get_json() or request.form or request.args
# This route its going to receive username and a user level
if "username" and "userlevel" and "room" in params:
connection_metadata = "username={username},userLevel={userlevel}".format(
username=params["username"], userlevel=params["userlevel"]
)
# Verify if room exists, if not then create room and session id for room
# If room exists create a token for user using the session id of the room
if params["room"] not in rooms:
# This method is going to return a session_id and a token
session = opentok.create_session("192.168.1.14")
token = opentok.generate_token(
session.session_id, Roles.publisher, None, connection_metadata
)
rooms[params["room"]] = session.session_id
queue_count[session.session_id] = 0
# playlist_room[session.session_id] = []
return (
jsonify(
{
"status": "success",
"token": token,
"session_id": session.session_id,
}
),
200,
)
else:
token = opentok.generate_token(
rooms[params["room"]], Roles.publisher, None, connection_metadata
)
return (
jsonify(
{
"status": "success",
"token": token,
"session_id": rooms[params["room"]],
}
),
200,
)
else:
return (
jsonify({"status": "error", "message": "Required params not provided"}),
200,
)
# We decided get the playlist information from here. And put the data available for the room passing the sameone with request or sockets
@app.route("/api/get-videos-from-playlist", methods=["GET"])
def get_videos():
global playlist_videos
global rooms
params = request.get_json() or request.form or request.args
if "playlist_id" and "room" in params:
if params["room"] in rooms:
if params["playlist_id"] in playlist_videos:
# The playlist exist already. then just retieve it
data = playlist_videos[params["playlist_id"]]
# create a litte wrapper
data = {"status": "success", "playlist": data}
else:
# The playlist not exist. Then retrieve from youtube API, save it in playlist_videos and then return it to client
try:
print(params["playlist_id"] + " " + environ.get("YOUTUBE_API_KEY"))
data = json.loads(
requests.get(
"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId={}&key={}".format(
params["playlist_id"], environ.get("YOUTUBE_API_KEY")
)
).text
)
playlist_videos[params["playlist_id"]] = data
# create a litte wrapper
data = {"status": "success", "playlist": data}
except:
print("Error when trying to return videos")
data = {
"status": "error",
"message": "Error when trying to return videos",
}
return jsonify(data), 200
else:
return jsonify({"status": "error", "message": "Room not found"}), 200
else:
return (
jsonify({"status": "error", "message": "Required params not provided"}),
200,
)
@app.route("/api/get-queue-count", methods=["GET"])
def get_queue_count():
global queue_count
global rooms
params = request.get_json() or request.form or request.args
if "room" in params:
return (
jsonify(
{"status": "success", "queue_count": queue_count[rooms[params["room"]]]}
),
200,
)
else:
return (
jsonify({"status": "error", "message": "Required params not provided"}),
200,
)
@socketio.on("join.room")
def on_create(data):
global rooms
# This method add user to room - room is the session_id from opentok
if data["room"] in rooms:
join_room(rooms[data["room"]])
emit(
"join.room",
{
"message": "Conneted to room: {room} / {room_id}".format(
room=data["room"], room_id=rooms[data["room"]]
)
},
)
else:
emit("join.room", {"message": "Not connected because room not found"})
@socketio.on("share.video.stream")
def on_share_video_stream(data):
global rooms
# sending to all clients in room except sender
# print(data)
emit("share.video.stream", data["selector"], room=rooms[data["room"]])
# A queue count for room is necesary to start functionality
@socketio.on("add.to.queue")
def on_add_queue(data):
global rooms
global queue_count
# sending to all clients in room except sender
# print(data)
queue_count[rooms[data["room"]]] += 1
emit(
"add.to.queue",
{
"vid": data["vid"],
"vname": data["vname"],
"vimage": data["vimage"],
"username": data["username"],
"useremail": data["useremail"],
"videoselector": data["videoselector"],
"queue_count": queue_count[rooms[data["room"]]],
},
room=rooms[data["room"]],
)
@socketio.on("sub.queue")
def on_sub_queue(data):
global queue_count
queue_count[rooms[data["room"]]] -= 1
# Case to send message to specific client
# the next two lines are needed if you start your app with python
if __name__ == "__main__":
socketio.run(app, debug=True, host="0.0.0.0", port=80)
# socketio.run(app, debug=True, host='localhost', port=5000)
Entonces... 🤔
- Flask es solo para proyectos pequeños?
- Por que le llaman micro framework?
- Puede hacer todo lo que hace Django?
- En que casos deberia usar Flask en vez de Django?
En conclusion...
- Excelente opción si estás comenzando en desarrollo con Python, y si no... también 😜
- No se limita a desarrollo de aplicaciones simples.
- Pon y quita a voluntad y según necesidades especificas!
- Una vez domines Flask, moverse a Django será muy sencillo...
- ... O tal vez, no haga falta!
Micro framework?
PRAISE YOURSELF
Recursos
Gracias Totales!!
@cotufa82
https://superdi.dev
Flask: Qué tan "micro" framework es?
By Super Diana
Flask: Qué tan "micro" framework es?
- 1,081