├── .gitignore ├── README.md ├── preview1.JPG ├── preview2.png ├── preview3.PNG ├── requirements.txt └── src ├── app.py ├── config.py ├── database └── db.py ├── models ├── MovieModel.py ├── __init__.py └── entities │ ├── Movie.py │ └── __init__.py ├── routes ├── Movie.py └── __init__.py └── utils └── DateFormat.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .env 3 | 4 | __pycache__/ 5 | *.py[cod] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # REST API con Python, Flask y PostgreSQL 2 | 3 | REST API con Python, Flask y PostgreSQL. Usaremos el protocolo HTTP junto con los métodos GET, POST, PUT y DELETE y el formato JSON para enviar y recibir datos; además, aprende a probarla utilizando un cliente REST como Insomnia. 4 | 5 |
6 | 7 | Primero, crear un entorno virtual: 8 | ### `python -m virtualenv env` 9 | 10 | Para instalar los paquetes necesarios: 11 | ### `pip install -r requirements.txt` 12 | 13 | Crear un archivo .env (en la raíz del proyecto) para las variables de entorno: 14 | 15 | ### `SECRET_KEY=SECRET_KEY` 16 | ### `PGSQL_HOST=host` 17 | ### `PGSQL_USER=user` 18 | ### `PGSQL_PASSWORD=password` 19 | ### `PGSQL_DB=database` 20 | 21 | Descarga de PostgreSQL: https://www.postgresql.org/download/ 22 |
23 | Descarga de Insomnia: https://insomnia.rest/download 24 | 25 |
26 | 27 | ![](./preview1.JPG) 28 |

29 | ![](./preview2.png) 30 |

31 | ![](./preview3.PNG) 32 | 33 | # 🌍 Por si deseas contactarme 👨‍💻 : 34 | 35 | [![LinkedIn](https://img.shields.io/badge/LinkedIn-Oscar_Garcia-0077B5?style=for-the-badge&logo=linkedin&logoColor=white&labelColor=101010)](https://pe.linkedin.com/in/uskokrum2010) 36 | [![YouTube](https://img.shields.io/badge/YouTube-UskoKruM2010-FF0000?style=for-the-badge&logo=youtube&logoColor=white&labelColor=101010)](https://youtube.com/uskokrum2010) 37 | [![Twitter](https://img.shields.io/badge/Twitter-@uskokrum2010-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white&labelColor=101010)](https://twitter.com/uskokrum2010) 38 | [![Instagram](https://img.shields.io/badge/Instagram-@uskokrum2010-E4405F?style=for-the-badge&logo=instagram&logoColor=white&labelColor=101010)](https://instagram.com/uskokrum2010) 39 | [![Facebook](https://img.shields.io/badge/Facebook-@uskokrum2010-1877F2?style=for-the-badge&logo=facebook&logoColor=white&labelColor=101010)](https://facebook.com/uskokrum2010) 40 | [![Udemy](https://img.shields.io/badge/Udemy-Oscar_Garcia-EC5252?style=for-the-badge&logo=udemy&logoColor=white&labelColor=101010)](https://www.udemy.com/course/sql-para-administracion-de-bases-de-datos-con-mysql/) 41 | [![Web](https://img.shields.io/badge/My_Website-uskokrum2010.com-14a1f0?style=for-the-badge&logo=dev.to&logoColor=white&labelColor=101010)](https://uskokrum2010.com) 42 | [![Email](https://img.shields.io/badge/uskokrum2010@gmail.com-mi_email_personal-D14836?style=for-the-badge&logo=gmail&logoColor=white&labelColor=101010)](mailto:uskokrum2010@gmail.com) -------------------------------------------------------------------------------- /preview1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/preview1.JPG -------------------------------------------------------------------------------- /preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/preview2.png -------------------------------------------------------------------------------- /preview3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/preview3.PNG -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/requirements.txt -------------------------------------------------------------------------------- /src/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_cors import CORS 3 | 4 | from config import config 5 | 6 | # Routes 7 | from routes import Movie 8 | 9 | app = Flask(__name__) 10 | 11 | CORS(app, resources={"*": {"origins": "http://localhost:9300"}}) 12 | 13 | 14 | def page_not_found(error): 15 | return "

Not found page

", 404 16 | 17 | 18 | if __name__ == '__main__': 19 | app.config.from_object(config['development']) 20 | 21 | # Blueprints 22 | app.register_blueprint(Movie.main, url_prefix='/api/movies') 23 | 24 | # Error handlers 25 | app.register_error_handler(404, page_not_found) 26 | app.run() 27 | -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | from decouple import config 2 | 3 | 4 | class Config: 5 | SECRET_KEY = config('SECRET_KEY') 6 | 7 | 8 | class DevelopmentConfig(Config): 9 | DEBUG = True 10 | 11 | 12 | config = { 13 | 'development': DevelopmentConfig 14 | } 15 | -------------------------------------------------------------------------------- /src/database/db.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | from psycopg2 import DatabaseError 3 | from decouple import config 4 | 5 | 6 | def get_connection(): 7 | try: 8 | return psycopg2.connect( 9 | host=config('PGSQL_HOST'), 10 | user=config('PGSQL_USER'), 11 | password=config('PGSQL_PASSWORD'), 12 | database=config('PGSQL_DATABASE') 13 | ) 14 | except DatabaseError as ex: 15 | raise ex 16 | -------------------------------------------------------------------------------- /src/models/MovieModel.py: -------------------------------------------------------------------------------- 1 | from database.db import get_connection 2 | from .entities.Movie import Movie 3 | 4 | 5 | class MovieModel(): 6 | 7 | @classmethod 8 | def get_movies(self): 9 | try: 10 | connection = get_connection() 11 | movies = [] 12 | 13 | with connection.cursor() as cursor: 14 | cursor.execute("SELECT id, title, duration, released FROM movie ORDER BY title ASC") 15 | resultset = cursor.fetchall() 16 | 17 | for row in resultset: 18 | movie = Movie(row[0], row[1], row[2], row[3]) 19 | movies.append(movie.to_JSON()) 20 | 21 | connection.close() 22 | return movies 23 | except Exception as ex: 24 | raise Exception(ex) 25 | 26 | @classmethod 27 | def get_movie(self, id): 28 | try: 29 | connection = get_connection() 30 | 31 | with connection.cursor() as cursor: 32 | cursor.execute("SELECT id, title, duration, released FROM movie WHERE id = %s", (id,)) 33 | row = cursor.fetchone() 34 | 35 | movie = None 36 | if row != None: 37 | movie = Movie(row[0], row[1], row[2], row[3]) 38 | movie = movie.to_JSON() 39 | 40 | connection.close() 41 | return movie 42 | except Exception as ex: 43 | raise Exception(ex) 44 | 45 | @classmethod 46 | def add_movie(self, movie): 47 | try: 48 | connection = get_connection() 49 | 50 | with connection.cursor() as cursor: 51 | cursor.execute("""INSERT INTO movie (id, title, duration, released) 52 | VALUES (%s, %s, %s, %s)""", (movie.id, movie.title, movie.duration, movie.released)) 53 | affected_rows = cursor.rowcount 54 | connection.commit() 55 | 56 | connection.close() 57 | return affected_rows 58 | except Exception as ex: 59 | raise Exception(ex) 60 | 61 | @classmethod 62 | def update_movie(self, movie): 63 | try: 64 | connection = get_connection() 65 | 66 | with connection.cursor() as cursor: 67 | cursor.execute("""UPDATE movie SET title = %s, duration = %s, released = %s 68 | WHERE id = %s""", (movie.title, movie.duration, movie.released, movie.id)) 69 | affected_rows = cursor.rowcount 70 | connection.commit() 71 | 72 | connection.close() 73 | return affected_rows 74 | except Exception as ex: 75 | raise Exception(ex) 76 | 77 | @classmethod 78 | def delete_movie(self, movie): 79 | try: 80 | connection = get_connection() 81 | 82 | with connection.cursor() as cursor: 83 | cursor.execute("DELETE FROM movie WHERE id = %s", (movie.id,)) 84 | affected_rows = cursor.rowcount 85 | connection.commit() 86 | 87 | connection.close() 88 | return affected_rows 89 | except Exception as ex: 90 | raise Exception(ex) 91 | -------------------------------------------------------------------------------- /src/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/src/models/__init__.py -------------------------------------------------------------------------------- /src/models/entities/Movie.py: -------------------------------------------------------------------------------- 1 | from utils.DateFormat import DateFormat 2 | 3 | 4 | class Movie(): 5 | 6 | def __init__(self, id, title=None, duration=None, released=None) -> None: 7 | self.id = id 8 | self.title = title 9 | self.duration = duration 10 | self.released = released 11 | 12 | def to_JSON(self): 13 | return { 14 | 'id': self.id, 15 | 'title': self.title, 16 | 'duration': self.duration, 17 | 'released': DateFormat.convert_date(self.released) 18 | } 19 | -------------------------------------------------------------------------------- /src/models/entities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/src/models/entities/__init__.py -------------------------------------------------------------------------------- /src/routes/Movie.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | import uuid 3 | 4 | # Entities 5 | from models.entities.Movie import Movie 6 | # Models 7 | from models.MovieModel import MovieModel 8 | 9 | main = Blueprint('movie_blueprint', __name__) 10 | 11 | 12 | @main.route('/') 13 | def get_movies(): 14 | try: 15 | movies = MovieModel.get_movies() 16 | return jsonify(movies) 17 | except Exception as ex: 18 | return jsonify({'message': str(ex)}), 500 19 | 20 | 21 | @main.route('/') 22 | def get_movie(id): 23 | try: 24 | movie = MovieModel.get_movie(id) 25 | if movie != None: 26 | return jsonify(movie) 27 | else: 28 | return jsonify({}), 404 29 | except Exception as ex: 30 | return jsonify({'message': str(ex)}), 500 31 | 32 | 33 | @main.route('/add', methods=['POST']) 34 | def add_movie(): 35 | try: 36 | title = request.json['title'] 37 | duration = int(request.json['duration']) 38 | released = request.json['released'] 39 | id = uuid.uuid4() 40 | movie = Movie(str(id), title, duration, released) 41 | 42 | affected_rows = MovieModel.add_movie(movie) 43 | 44 | if affected_rows == 1: 45 | return jsonify(movie.id) 46 | else: 47 | return jsonify({'message': "Error on insert"}), 500 48 | 49 | except Exception as ex: 50 | return jsonify({'message': str(ex)}), 500 51 | 52 | 53 | @main.route('/update/', methods=['PUT']) 54 | def update_movie(id): 55 | try: 56 | title = request.json['title'] 57 | duration = int(request.json['duration']) 58 | released = request.json['released'] 59 | movie = Movie(id, title, duration, released) 60 | 61 | affected_rows = MovieModel.update_movie(movie) 62 | 63 | if affected_rows == 1: 64 | return jsonify(movie.id) 65 | else: 66 | return jsonify({'message': "No movie updated"}), 404 67 | 68 | except Exception as ex: 69 | return jsonify({'message': str(ex)}), 500 70 | 71 | 72 | @main.route('/delete/', methods=['DELETE']) 73 | def delete_movie(id): 74 | try: 75 | movie = Movie(id) 76 | 77 | affected_rows = MovieModel.delete_movie(movie) 78 | 79 | if affected_rows == 1: 80 | return jsonify(movie.id) 81 | else: 82 | return jsonify({'message': "No movie deleted"}), 404 83 | 84 | except Exception as ex: 85 | return jsonify({'message': str(ex)}), 500 86 | -------------------------------------------------------------------------------- /src/routes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UskoKruM/python-flask-postgresql-rest-api/a6215cd4d2717b8cee66bba67ee35a598ac23ea4/src/routes/__init__.py -------------------------------------------------------------------------------- /src/utils/DateFormat.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | class DateFormat(): 5 | 6 | @classmethod 7 | def convert_date(self, date): 8 | return datetime.datetime.strftime(date, '%d/%m/%Y') 9 | --------------------------------------------------------------------------------