From 7adf7f82db7700a5a5e8b5fb0272df961901e5e6 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Wed, 30 Apr 2025 18:21:26 +0000 Subject: [PATCH 01/16] base --- src/models.py | 118 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/src/models.py b/src/models.py index 7f535f618..6fda2be8c 100644 --- a/src/models.py +++ b/src/models.py @@ -1,19 +1,113 @@ from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean -from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String, Boolean, Integer, ForeignKey, Float, Text, DateTime, func +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime db = SQLAlchemy() -class User(db.Model): + +class Usuario(db.Model): + __tablename__ = "usuarios" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + apellido: Mapped[str] = mapped_column(String(100), nullable=False) + email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) + password: Mapped[str] = mapped_column(String(255), nullable=False) + fecha_registro: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + ultima_conexion: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + activo: Mapped[bool] = mapped_column(Boolean(), default=True) + + # Relaciones + planetas_favoritos = relationship("PlanetaFavorito", back_populates="usuario") + personajes_favoritos = relationship("PersonajeFavorito", back_populates="usuario") + vehiculos_favoritos = relationship("VehiculoFavorito", back_populates="usuario") + + +class Planeta(db.Model): + __tablename__ = "planetas" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + diametro: Mapped[int] = mapped_column(Integer, nullable=True) + clima: Mapped[str] = mapped_column(String(50), nullable=True) + poblacion: Mapped[int] = mapped_column(Integer, nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + + personajes = relationship("Personaje", back_populates="planeta_natal") + favoritos = relationship("PlanetaFavorito", back_populates="planeta") + + +class Personaje(db.Model): + __tablename__ = "personajes" + id: Mapped[int] = mapped_column(primary_key=True) - email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False) - password: Mapped[str] = mapped_column(nullable=False) - is_active: Mapped[bool] = mapped_column(Boolean(), nullable=False) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + especie: Mapped[str] = mapped_column(String(50), nullable=True) + altura: Mapped[int] = mapped_column(Integer, nullable=True) + peso: Mapped[int] = mapped_column(Integer, nullable=True) + genero: Mapped[str] = mapped_column(String(20), nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + planeta_natal_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=True) + vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"),unique = True, nullable=True) + vehiculo = relationship("Vehiculo", back_populates="personaje", uselist=False) + planeta_natal = relationship("Planeta", back_populates="personajes") + favoritos = relationship("PersonajeFavorito", back_populates="personaje") + + +class Vehiculo(db.Model): + __tablename__ = "vehiculos" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + modelo: Mapped[str] = mapped_column(String(100), nullable=True) + longitud: Mapped[float] = mapped_column(Float, nullable=True) + velocidad_maxima: Mapped[int] = mapped_column(Integer, nullable=True) + tripulacion: Mapped[int] = mapped_column(Integer, nullable=True) + pasajeros: Mapped[int] = mapped_column(Integer, nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) + + personaje = relationship("Personaje", back_populates="vehiculo", uselist=False) + favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") + + + +class PlanetaFavorito(db.Model): + __tablename__ = "planetas_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + planeta_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + usuario = relationship("Usuario", back_populates="planetas_favoritos") + planeta = relationship("Planeta", back_populates="favoritos") + + +class PersonajeFavorito(db.Model): + __tablename__ = "personajes_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + usuario = relationship("Usuario", back_populates="personajes_favoritos") + personaje = relationship("Personaje", back_populates="favoritos") + + +class VehiculoFavorito(db.Model): + __tablename__ = "vehiculos_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - def serialize(self): - return { - "id": self.id, - "email": self.email, - # do not serialize the password, its a security breach - } + usuario = relationship("Usuario", back_populates="vehiculos_favoritos") + vehiculo = relationship("Vehiculo", back_populates="favoritos") From e9c78159e551169a1f4e9ef6052d523c3cf3d1ec Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Wed, 30 Apr 2025 19:06:48 +0000 Subject: [PATCH 02/16] prueba 2 --- src/app.py | 16 +++++++++------- src/models.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/app.py b/src/app.py index 016c5bff9..e2884d78d 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, User +from models import db, Personaje, Usuario, Planeta, Vehiculo #from models import Person app = Flask(__name__) @@ -36,14 +36,16 @@ def handle_invalid_usage(error): def sitemap(): return generate_sitemap(app) -@app.route('/user', methods=['GET']) -def handle_hello(): - response_body = { - "msg": "Hello, this is your GET /user response " - } - return jsonify(response_body), 200 +@app.route('/people', methods=['GET']) +def get_people(): + people = Personaje.query.all() + return jsonify() + + + + # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': diff --git a/src/models.py b/src/models.py index 6fda2be8c..e403f0a9e 100644 --- a/src/models.py +++ b/src/models.py @@ -35,6 +35,17 @@ class Planeta(db.Model): imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) descripcion: Mapped[str] = mapped_column(Text, nullable=True) + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "diametro": self.diametro, + "clima": self.clima, + "poblacion": self.poblacion, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + personajes = relationship("Personaje", back_populates="planeta_natal") favoritos = relationship("PlanetaFavorito", back_populates="planeta") @@ -57,6 +68,18 @@ class Personaje(db.Model): planeta_natal = relationship("Planeta", back_populates="personajes") favoritos = relationship("PersonajeFavorito", back_populates="personaje") + def serialize(self): + return{ + "id": self.id, + "nombre": self.nombre, + "especie": self.especie, + "altura": self.altura, + "peso": self.peso, + "genero": self.genero, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + class Vehiculo(db.Model): __tablename__ = "vehiculos" @@ -72,6 +95,19 @@ class Vehiculo(db.Model): descripcion: Mapped[str] = mapped_column(Text, nullable=True) personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "modelo": self.modelo, + "longitud": self.longitud, + "velocidad_maxima": self.velocidad_maxima, + "tripulacion": self.tripulacion, + "pasajeros": self.pasajeros, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + personaje = relationship("Personaje", back_populates="vehiculo", uselist=False) favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") From dfab664c2973e8784f4ef36c8d4664f69c000828 Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Wed, 30 Apr 2025 19:10:05 +0000 Subject: [PATCH 03/16] GET users and favorites --- src/app.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/app.py b/src/app.py index 016c5bff9..c6aff9356 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, User +from models import db, Usuario #from models import Person app = Flask(__name__) @@ -36,15 +36,27 @@ def handle_invalid_usage(error): def sitemap(): return generate_sitemap(app) -@app.route('/user', methods=['GET']) -def handle_hello(): +@app.route('/users', methods=['GET']) +def get_users(): + response = Usuario.query.all() + return jsonify(response), 200 - response_body = { - "msg": "Hello, this is your GET /user response " +@app.route('/users/favorites', method=['GET']) +def get_favorites(): + user = Usuario.query.get(Usuario.id) + + if not user: + return jsonify({"error": "User not found"}), 404 + + favorites = { + "planetas": [p.serialize() for p in Usuario.planetas_favoritos], + "personajes": [p.serialize() for p in Usuario.personajes_favoritos], + "vehiculos": [v.serialize() for v in Usuario.vehiculos_favoritos], } - return jsonify(response_body), 200 + return jsonify(favorites), 200 +@app.route('/') # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': PORT = int(os.environ.get('PORT', 3000)) From 1c0d7ed7b7225a473c6f13d930e83e0066cb0c49 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Wed, 30 Apr 2025 19:11:56 +0000 Subject: [PATCH 04/16] prueba esto funciona asi? --- src/app.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/app.py b/src/app.py index 016c5bff9..c6c465775 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, User +from models import db, Personaje #from models import Person app = Flask(__name__) @@ -36,14 +36,10 @@ def handle_invalid_usage(error): def sitemap(): return generate_sitemap(app) -@app.route('/user', methods=['GET']) -def handle_hello(): - - response_body = { - "msg": "Hello, this is your GET /user response " - } - - return jsonify(response_body), 200 +@app.route('/people', methods=['GET']) +def get_people(): + people = Personaje.query.all() + return jsonify([personaje.serialize() for personaje in people]), 200 # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': From 8a30ccda2750ebffef4a3ef983cd1aff71dd430e Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Fri, 2 May 2025 18:25:22 +0000 Subject: [PATCH 05/16] Delete added --- src/app.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/app.py b/src/app.py index c6aff9356..e3af7a539 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, Usuario +from models import db, Usuario, PlanetaFavorito, VehiculoFavorito, PersonajeFavorito #from models import Person app = Flask(__name__) @@ -43,9 +43,9 @@ def get_users(): @app.route('/users/favorites', method=['GET']) def get_favorites(): - user = Usuario.query.get(Usuario.id) + user_id = Usuario.query.get(Usuario.id) - if not user: + if not user_id: return jsonify({"error": "User not found"}), 404 favorites = { @@ -56,6 +56,34 @@ def get_favorites(): return jsonify(favorites), 200 +@app.route('/favorite/planet/', method=['DELETE']) +def delete_fav_planet(planet_id): + user_id = Usuario.query.get(Usuario.id) + favorite = PlanetaFavorito.query.filter_by(usuario_id=user_id, planeta_id=planet_id).first() + + if not favorite: + return jsonify({"message": "Favorite not found"}), 404 + + db.session.delete(favorite) + db.session.commit() + + updated_favorites = PlanetaFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([fav.serialize() for fav in updated_favorites]), 200 + +@app.route('/favorite/people/', method=['DELETE']) +def delete_fav_person(people_id): + user_id = Usuario.query.get(Usuario.id) + person = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() + + if not person: + return jsonify({"message": "Person not found"}), 404 + + db.session.delete(person) + db.session.commit() + + updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_people]), 200 + @app.route('/') # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': From 5d4a219abcfba0bfd38f37af471e24d6ee6a5e99 Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Fri, 2 May 2025 18:47:30 +0000 Subject: [PATCH 06/16] Updates --- src/app.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/app.py b/src/app.py index e3af7a539..0f7e16140 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, Usuario, PlanetaFavorito, VehiculoFavorito, PersonajeFavorito +from models import db, Usuario, Planeta, Personaje, Vehiculo, PlanetaFavorito, PersonajeFavorito #from models import Person app = Flask(__name__) @@ -84,6 +84,33 @@ def delete_fav_person(people_id): updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() return jsonify([p.serialize() for p in updated_people]), 200 +@app.route('/planets/', method=['DELETE']) +def delete_planet(planet_id): + planet = Planeta.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() + + if not person: + return jsonify({"message": "Person not found"}), 404 + + db.session.delete(person) + db.session.commit() + + updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_people]), 200 + +@app.route('/people', method=['DELETE']) +def delete_fav_person(people_id): + user_id = Usuario.query.get(Usuario.id) + person = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() + + if not person: + return jsonify({"message": "Person not found"}), 404 + + db.session.delete(person) + db.session.commit() + + updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_people]), 200 + @app.route('/') # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': From 6f0f5296037170f36eed1ed5e9d31659145cb875 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Fri, 2 May 2025 21:25:57 +0000 Subject: [PATCH 07/16] =?UTF-8?q?a=C3=B1adidos=20varios=20endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.py | 107 +++++++++++++++++++++++++++++++++++--------------- src/models.py | 45 +++++++++++++++++---- 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/app.py b/src/app.py index 7a917c5c1..02f8a5f78 100644 --- a/src/app.py +++ b/src/app.py @@ -8,13 +8,13 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, Personaje, Usuario, Planeta, Vehiculo, Usuario, Planeta, Vehiculo, PlanetaFavorito, PersonajeFavorito, VehiculoFavorito +from models import db, Personaje, Usuario, Planeta, Vehiculo, PlanetaFavorito, PersonajeFavorito, VehiculoFavorito -#from models import Person app = Flask(__name__) app.url_map.strict_slashes = False +# Configuración de la base de datos db_url = os.getenv("DATABASE_URL") if db_url is not None: app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace("postgres://", "postgresql://") @@ -22,21 +22,23 @@ app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +# Inicialización de extensiones MIGRATE = Migrate(app, db) db.init_app(app) CORS(app) setup_admin(app) -# Handle/serialize errors like a JSON object +# Manejo global de errores personalizados @app.errorhandler(APIException) def handle_invalid_usage(error): return jsonify(error.to_dict()), error.status_code -# generate sitemap with all your endpoints +# Genera un mapa visual de todas las rutas @app.route('/') def sitemap(): return generate_sitemap(app) +# ENDPOINTS DE PERSONAJES @app.route('/people', methods=['GET']) def get_people(): people = Personaje.query.all() @@ -47,17 +49,29 @@ def get_personaje(people_id): personaje = Personaje.query.get_or_404(people_id) return jsonify(personaje.serialize()), 200 +# ENDPOINTS DE PLANETAS @app.route('/planets', methods=['GET']) def get_planetas(): planetas = Planeta.query.all() return jsonify([planeta.serialize() for planeta in planetas]), 200 - @app.route('/planets/', methods=['GET']) def get_planeta(planet_id): planeta = Planeta.query.get_or_404(planet_id) return jsonify(planeta.serialize()), 200 +# ENDPOINTS DE VEHICULOS +@app.route('/vehicles', methods=['GET']) +def get_vehiculos(): + vehiculos = Vehiculo.query.all() + return jsonify([vehiculo.serialize() for vehiculo in vehiculos]), 200 + +@app.route('/vehicles/', methods=['GET']) +def get_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + return jsonify(vehiculo.serialize()), 200 + +# ENDPOINTS DE USUARIOS Y FAVORITOS @app.route('/users', methods=['GET']) def get_users(): response = Usuario.query.all() @@ -75,12 +89,9 @@ def get_favorites(): "personajes": [p.serialize() for p in Usuario.personajes_favoritos], "vehiculos": [v.serialize() for v in Usuario.vehiculos_favoritos], } - return jsonify(favorites), 200 -@app.route('/people', methods=['GET']) -def get_people(): - people = Personaje.query.all() - return jsonify([personaje.serialize() for personaje in people]), 200 + +# ENDPOINTS DE FAVORITOS - ELIMINAR @app.route('/favorite/planet/', method=['DELETE']) def delete_fav_planet(planet_id): @@ -99,44 +110,58 @@ def delete_fav_planet(planet_id): @app.route('/favorite/people/', method=['DELETE']) def delete_fav_person(people_id): user_id = Usuario.query.get(Usuario.id) - person = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() + favorite = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() - if not person: + if not favorite: return jsonify({"message": "Person not found"}), 404 - db.session.delete(person) + db.session.delete(favorite) db.session.commit() - updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() - return jsonify([p.serialize() for p in updated_people]), 200 + updated_favorites = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_favorites]), 200 -@app.route('/planets/', method=['DELETE']) -def delete_planet(planet_id): - planet = Planeta.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() +@app.route('/favorite/vehicle/', methods=['DELETE']) +def delete_fav_vehiculo(vehicle_id): + user_id = Usuario.query.get(Usuario.id) + favorite = VehiculoFavorito.query.filter_by(usuario_id=user_id, vehiculo_id=vehicle_id).first() - if not person: - return jsonify({"message": "Person not found"}), 404 + if not favorite: + return jsonify({"message": "Vehículo favorito no encontrado"}), 404 - db.session.delete(person) + db.session.delete(favorite) db.session.commit() - updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() - return jsonify([p.serialize() for p in updated_people]), 200 + updated_favorites = VehiculoFavorito.query.filter_by(usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_favorites]), 200 -@app.route('/people', method=['DELETE']) -def delete_fav_person(people_id): - user_id = Usuario.query.get(Usuario.id) - person = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() +# ENDPOINTS DE ELIMINAR - if not person: - return jsonify({"message": "Person not found"}), 404 +@app.route('/planets/', methods=['DELETE']) +def delete_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) - db.session.delete(person) + db.session.delete(planeta) db.session.commit() + return jsonify({"msg": f"Planeta {planet_id} eliminado"}), 200 - updated_people = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() - return jsonify([p.serialize() for p in updated_people]), 200 +@app.route('/people/', methods=['DELETE']) +def delete_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + + db.session.delete(personaje) + db.session.commit() + return jsonify({"msg": f"Personaje {people_id} eliminado"}), 200 +@app.route('/vehicles/', methods=['DELETE']) +def delete_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + + db.session.delete(vehiculo) + db.session.commit() + return jsonify({"msg": f"Vehículo {vehicle_id} eliminado"}), 200 + +# ENDPOINTS DE FAVORITOS - AÑADIR @app.route('/favorite/planet/', method=['POST']) def add_favorite_planet(planet_id): user = Usuario.query.get(Usuario.id) @@ -172,6 +197,24 @@ def add_favorite_personaje(people_id): return jsonify({'msg': 'Personaje añadido a favoritos', 'personaje': personaje.nombre}), 201 +@app.route('/favorite/vehicle/', methods=['POST']) +def add_favorite_vehiculo(vehicle_id): + user = Usuario.query.get(Usuario.id) + vehiculo = Vehiculo.query.get(vehicle_id) + + if not vehiculo: + return jsonify({'error': 'Vehículo no encontrado'}), 404 + + favorito_existente = next((fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) + if favorito_existente: + return jsonify({'msg': 'El vehículo ya está en favoritos'}), 400 + + nuevo_favorito = VehiculoFavorito(usuario_id=user.id, vehiculo_id=vehicle_id) + db.session.add(nuevo_favorito) + db.session.commit() + + return jsonify({'msg': 'Vehículo añadido a favoritos','vehiculo': vehiculo.nombre}), 201 + # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': diff --git a/src/models.py b/src/models.py index 10f778d4c..654d3e695 100644 --- a/src/models.py +++ b/src/models.py @@ -36,8 +36,9 @@ def serialize(self): def serialize_with_relations(self): data = self.serialize() - data['made_by_teacher'] = self.made_by_teacher.serialize() - data['students'] = [student.serialize() for student in self.students] + data['planetas_favoritos'] = [pf.serialize() for pf in self.planetas_favoritos] + data['personajes_favoritos'] = [pef.serialize() for pef in self.personajes_favoritos] + data['vehiculos_favoritos'] = [vf.serialize() for vf in self.vehiculos_favoritos] return data @@ -67,8 +68,8 @@ def serialize(self): } def serialize_with_relations(self): data = self.serialize() - data['made_by_teacher'] = self.made_by_teacher.serialize() - data['students'] = [student.serialize() for student in self.students] + data['personajes'] = [p.serialize() for p in self.personajes] + data['favoritos'] = [f.serialize() for f in self.favoritos] return data @@ -103,8 +104,11 @@ def serialize(self): } def serialize_with_relations(self): data = self.serialize() - data['made_by_teacher'] = self.made_by_teacher.serialize() - data['students'] = [student.serialize() for student in self.students] + if self.planeta_natal: + data['planeta_natal'] = self.planeta_natal.serialize() + if self.vehiculo: + data['vehiculo'] = self.vehiculo.serialize() + data['favoritos'] = [f.serialize() for f in self.favoritos] return data @@ -140,8 +144,9 @@ def serialize(self): } def serialize_with_relations(self): data = self.serialize() - data['made_by_teacher'] = self.made_by_teacher.serialize() - data['students'] = [student.serialize() for student in self.students] + if self.personaje: + data['personaje'] = self.personaje.serialize() + data['favoritos'] = [f.serialize() for f in self.favoritos] return data @@ -153,6 +158,14 @@ class PlanetaFavorito(db.Model): planeta_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=False) fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "planeta_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } + usuario = relationship("Usuario", back_populates="planetas_favoritos") planeta = relationship("Planeta", back_populates="favoritos") @@ -164,6 +177,14 @@ class PersonajeFavorito(db.Model): usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=False) fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "personaje_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } usuario = relationship("Usuario", back_populates="personajes_favoritos") personaje = relationship("Personaje", back_populates="favoritos") @@ -177,5 +198,13 @@ class VehiculoFavorito(db.Model): vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"), nullable=False) fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "vehiculo_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } + usuario = relationship("Usuario", back_populates="vehiculos_favoritos") vehiculo = relationship("Vehiculo", back_populates="favoritos") From 40dc7dd86b447690bced4d70c24a622c3f5a63ed Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Mon, 5 May 2025 18:01:02 +0000 Subject: [PATCH 08/16] POST PUT tests --- src/app.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/app.py b/src/app.py index 02f8a5f78..df31ba10d 100644 --- a/src/app.py +++ b/src/app.py @@ -92,7 +92,6 @@ def get_favorites(): return jsonify(favorites), 200 # ENDPOINTS DE FAVORITOS - ELIMINAR - @app.route('/favorite/planet/', method=['DELETE']) def delete_fav_planet(planet_id): user_id = Usuario.query.get(Usuario.id) @@ -136,7 +135,6 @@ def delete_fav_vehiculo(vehicle_id): return jsonify([p.serialize() for p in updated_favorites]), 200 # ENDPOINTS DE ELIMINAR - @app.route('/planets/', methods=['DELETE']) def delete_planeta(planet_id): planeta = Planeta.query.get_or_404(planet_id) @@ -215,6 +213,82 @@ def add_favorite_vehiculo(vehicle_id): return jsonify({'msg': 'Vehículo añadido a favoritos','vehiculo': vehiculo.nombre}), 201 +### POSTs + +#POST Personajes +@app.route('/people', methods=['POST']) +def post_people(): + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("especie"), str): + return jsonify({"error": "'especie' must be a string"}), 400 + if not isinstance(request_body.get("altura"), (int, float)): + return jsonify({"error": "'altura' must be a number"}), 400 + if not isinstance(request_body.get("peso"), (int, float)): + return jsonify({"error": "'peso' must be a number"}), 400 + if not isinstance(request_body.get("genero"), str): + return jsonify({"error": "'genero' must be a string"}), 400 + if not isinstance(request_body.get("imagen_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + + # At this point, request_body is validated + # You can now create and save a new Personaje object, for example + + return jsonify({"message": "Personaje created successfully"}), 201 + + +### PUTs +# PUT Personajes +@app.route('/people/', methods=['PUT']) +def put_people(people_id): + data = Personaje.query.get_or_404(people_id) + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + else: + data.nombre = request_body.nombre + if not isinstance(request_body.get("especie"), str): + return jsonify({"error": "'especie' must be a string"}), 400 + else: + data.especie = request_body.especie + if not isinstance(request_body.get("altura"), (int, float)): + return jsonify({"error": "'altura' must be a number"}), 400 + else: + data.altura = request_body.altura + if not isinstance(request_body.get("peso"), (int, float)): + return jsonify({"error": "'peso' must be a number"}), 400 + else: + data.peso = request_body.peso + if not isinstance(request_body.get("genero"), str): + return jsonify({"error": "'genero' must be a string"}), 400 + else: + data.genero = request_body.genero + if not isinstance(request_body.get("imagen_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + else: + data.image_url = request_body.image_url + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + else: + data.descripcion = request_body.descripcion + + # At this point, request_body is validated + # You can now create and save a new Personaje object, for example + + return jsonify({"message": "Personaje updated successfully"}), 201 # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': From 8bafdd1a86fc9c573c39cd99f19f61a623896e2c Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Mon, 5 May 2025 19:31:17 +0000 Subject: [PATCH 09/16] Avances 5-5 --- migrations/versions/7f3fdc0ecd62_.py | 35 +++++ src/admin.py | 4 +- src/app.py | 30 ++-- src/models.py | 210 --------------------------- src/models/__init__.py | 12 ++ src/models/associations.py | 63 ++++++++ src/models/personaje.py | 41 ++++++ src/models/planeta.py | 33 +++++ src/models/usuario.py | 39 +++++ src/models/vehiculo.py | 40 +++++ 10 files changed, 283 insertions(+), 224 deletions(-) create mode 100644 migrations/versions/7f3fdc0ecd62_.py delete mode 100644 src/models.py diff --git a/migrations/versions/7f3fdc0ecd62_.py b/migrations/versions/7f3fdc0ecd62_.py new file mode 100644 index 000000000..7d9cd826d --- /dev/null +++ b/migrations/versions/7f3fdc0ecd62_.py @@ -0,0 +1,35 @@ +"""empty message + +Revision ID: 7f3fdc0ecd62 +Revises: a5cffa318ac2 +Create Date: 2025-05-05 18:45:14.298755 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '7f3fdc0ecd62' +down_revision = 'a5cffa318ac2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('user') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('email', sa.VARCHAR(length=120), autoincrement=False, nullable=False), + sa.Column('password', sa.VARCHAR(length=80), autoincrement=False, nullable=False), + sa.Column('is_active', sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='user_pkey'), + sa.UniqueConstraint('email', name='user_email_key') + ) + # ### end Alembic commands ### diff --git a/src/admin.py b/src/admin.py index bb934027c..550d83536 100644 --- a/src/admin.py +++ b/src/admin.py @@ -1,6 +1,6 @@ import os from flask_admin import Admin -from models import db, User +from models import db from flask_admin.contrib.sqla import ModelView def setup_admin(app): @@ -10,7 +10,7 @@ def setup_admin(app): # Add your models here, for example this is how we add a the User model to the admin - admin.add_view(ModelView(User, db.session)) + #admin.add_view(ModelView(User, db.session)) # You can duplicate that line to add mew models # admin.add_view(ModelView(YourModelName, db.session)) \ No newline at end of file diff --git a/src/app.py b/src/app.py index df31ba10d..01f296528 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, Personaje, Usuario, Planeta, Vehiculo, PlanetaFavorito, PersonajeFavorito, VehiculoFavorito +from models import * app = Flask(__name__) @@ -77,7 +77,7 @@ def get_users(): response = Usuario.query.all() return jsonify(response), 200 -@app.route('/users/favorites', method=['GET']) +@app.route('/users/favorites', methods=['GET']) def get_favorites(): user_id = Usuario.query.get(Usuario.id) @@ -92,7 +92,7 @@ def get_favorites(): return jsonify(favorites), 200 # ENDPOINTS DE FAVORITOS - ELIMINAR -@app.route('/favorite/planet/', method=['DELETE']) +@app.route('/favorite/planet/', methods=['DELETE']) def delete_fav_planet(planet_id): user_id = Usuario.query.get(Usuario.id) favorite = PlanetaFavorito.query.filter_by(usuario_id=user_id, planeta_id=planet_id).first() @@ -106,7 +106,7 @@ def delete_fav_planet(planet_id): updated_favorites = PlanetaFavorito.query.filter_by(usuario_id=user_id).all() return jsonify([fav.serialize() for fav in updated_favorites]), 200 -@app.route('/favorite/people/', method=['DELETE']) +@app.route('/favorite/people/', methods=['DELETE']) def delete_fav_person(people_id): user_id = Usuario.query.get(Usuario.id) favorite = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() @@ -160,7 +160,7 @@ def delete_vehiculo(vehicle_id): return jsonify({"msg": f"Vehículo {vehicle_id} eliminado"}), 200 # ENDPOINTS DE FAVORITOS - AÑADIR -@app.route('/favorite/planet/', method=['POST']) +@app.route('/favorite/planet/', methods=['POST']) def add_favorite_planet(planet_id): user = Usuario.query.get(Usuario.id) planeta = Planeta.query.get(planet_id) @@ -177,7 +177,7 @@ def add_favorite_planet(planet_id): return jsonify({'msg': 'Planeta añadido a favoritos', 'planeta': planeta.nombre}), 201 -@app.route('/favorite/people/', method=['POST']) +@app.route('/favorite/people/', methods=['POST']) def add_favorite_personaje(people_id): user = Usuario.query.get(Usuario.id) personaje = Personaje.query.get(people_id) @@ -239,8 +239,18 @@ def post_people(): if not isinstance(request_body.get("descripcion"), str): return jsonify({"error": "'descripcion' must be a string"}), 400 - # At this point, request_body is validated - # You can now create and save a new Personaje object, for example + new_personaje = Personaje( + nombre=request_body['nombre'], + especie=request_body['especie'], + altura=request_body['altura'], + peso=request_body['peso'], + genero=request_body['genero'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_personaje) + db.session.commit() return jsonify({"message": "Personaje created successfully"}), 201 @@ -255,7 +265,6 @@ def put_people(people_id): if not request_body: return jsonify({"error": "Empty request body"}), 400 - # Validate required fields and their types if not isinstance(request_body.get("nombre"), str): return jsonify({"error": "'nombre' must be a string"}), 400 else: @@ -285,9 +294,6 @@ def put_people(people_id): else: data.descripcion = request_body.descripcion - # At this point, request_body is validated - # You can now create and save a new Personaje object, for example - return jsonify({"message": "Personaje updated successfully"}), 201 # this only runs if `$ python src/app.py` is executed diff --git a/src/models.py b/src/models.py deleted file mode 100644 index 654d3e695..000000000 --- a/src/models.py +++ /dev/null @@ -1,210 +0,0 @@ -from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean, Integer, ForeignKey, Float, Text, DateTime, func -from sqlalchemy.orm import Mapped, mapped_column, relationship -from datetime import datetime - -db = SQLAlchemy() - - -class Usuario(db.Model): - __tablename__ = "usuarios" - - id: Mapped[int] = mapped_column(primary_key=True) - nombre: Mapped[str] = mapped_column(String(100), nullable=False) - apellido: Mapped[str] = mapped_column(String(100), nullable=False) - email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) - password: Mapped[str] = mapped_column(String(255), nullable=False) - fecha_registro: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - ultima_conexion: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - activo: Mapped[bool] = mapped_column(Boolean(), default=True) - - # Relaciones - planetas_favoritos = relationship("PlanetaFavorito", back_populates="usuario") - personajes_favoritos = relationship("PersonajeFavorito", back_populates="usuario") - vehiculos_favoritos = relationship("VehiculoFavorito", back_populates="usuario") - - def serialize(self): - return { - "id": self.id, - "nombre": self.nombre, - "apellido": self.apellido, - "email": self.email, - "fecha_registro": self.fecha_registro, - "ultima_conexion": self.ultima_conexion, - "activo": self.activo - } - - def serialize_with_relations(self): - data = self.serialize() - data['planetas_favoritos'] = [pf.serialize() for pf in self.planetas_favoritos] - data['personajes_favoritos'] = [pef.serialize() for pef in self.personajes_favoritos] - data['vehiculos_favoritos'] = [vf.serialize() for vf in self.vehiculos_favoritos] - return data - - -class Planeta(db.Model): - __tablename__ = "planetas" - - id: Mapped[int] = mapped_column(primary_key=True) - nombre: Mapped[str] = mapped_column(String(100), nullable=False) - diametro: Mapped[int] = mapped_column(Integer, nullable=True) - clima: Mapped[str] = mapped_column(String(50), nullable=True) - poblacion: Mapped[int] = mapped_column(Integer, nullable=True) - imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) - descripcion: Mapped[str] = mapped_column(Text, nullable=True) - - personajes = relationship("Personaje", back_populates="planeta_natal") - favoritos = relationship("PlanetaFavorito", back_populates="planeta") - - def serialize(self): - return { - "id": self.id, - "nombre": self.nombre, - "diametro": self.diametro, - "clima": self.clima, - "poblacion": self.poblacion, - "imagen_url": self.imagen_url, - "descripcion": self.descripcion - } - def serialize_with_relations(self): - data = self.serialize() - data['personajes'] = [p.serialize() for p in self.personajes] - data['favoritos'] = [f.serialize() for f in self.favoritos] - return data - - -class Personaje(db.Model): - __tablename__ = "personajes" - - id: Mapped[int] = mapped_column(primary_key=True) - nombre: Mapped[str] = mapped_column(String(100), nullable=False) - especie: Mapped[str] = mapped_column(String(50), nullable=True) - altura: Mapped[int] = mapped_column(Integer, nullable=True) - peso: Mapped[int] = mapped_column(Integer, nullable=True) - genero: Mapped[str] = mapped_column(String(20), nullable=True) - imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) - descripcion: Mapped[str] = mapped_column(Text, nullable=True) - planeta_natal_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=True) - vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"),unique = True, nullable=True) - - vehiculo = relationship("Vehiculo", back_populates="personaje", uselist=False) - planeta_natal = relationship("Planeta", back_populates="personajes") - favoritos = relationship("PersonajeFavorito", back_populates="personaje") - - def serialize(self): - return { - "id": self.id, - "nombre": self.nombre, - "especie": self.especie, - "altura": self.altura, - "peso": self.peso, - "genero": self.genero, - "imagen_url": self.imagen_url, - "descripcion": self.descripcion - } - def serialize_with_relations(self): - data = self.serialize() - if self.planeta_natal: - data['planeta_natal'] = self.planeta_natal.serialize() - if self.vehiculo: - data['vehiculo'] = self.vehiculo.serialize() - data['favoritos'] = [f.serialize() for f in self.favoritos] - return data - - -class Vehiculo(db.Model): - __tablename__ = "vehiculos" - - id: Mapped[int] = mapped_column(primary_key=True) - nombre: Mapped[str] = mapped_column(String(100), nullable=False) - modelo: Mapped[str] = mapped_column(String(100), nullable=True) - longitud: Mapped[float] = mapped_column(Float, nullable=True) - velocidad_maxima: Mapped[int] = mapped_column(Integer, nullable=True) - tripulacion: Mapped[int] = mapped_column(Integer, nullable=True) - pasajeros: Mapped[int] = mapped_column(Integer, nullable=True) - imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) - descripcion: Mapped[str] = mapped_column(Text, nullable=True) - personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) - - - personaje = relationship("Personaje", back_populates="vehiculo", uselist=False) - favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") - - def serialize(self): - return { - "id": self.id, - "nombre": self.nombre, - "modelo": self.modelo, - "longitud": self.longitud, - "velocidad_maxima": self.velocidad_maxima, - "tripulacion": self.tripulacion, - "pasajeros": self.pasajeros, - "imagen_url": self.imagen_url, - "descripcion": self.descripcion - } - def serialize_with_relations(self): - data = self.serialize() - if self.personaje: - data['personaje'] = self.personaje.serialize() - data['favoritos'] = [f.serialize() for f in self.favoritos] - return data - - -class PlanetaFavorito(db.Model): - __tablename__ = "planetas_favoritos" - - id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - planeta_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - - def serialize(self): - return { - "id": self.id, - "usuario_id": self.usuario_id, - "planeta_id": self.planeta_id, - "fecha_agregado": self.fecha_agregado - } - - usuario = relationship("Usuario", back_populates="planetas_favoritos") - planeta = relationship("Planeta", back_populates="favoritos") - - -class PersonajeFavorito(db.Model): - __tablename__ = "personajes_favoritos" - - id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - - def serialize(self): - return { - "id": self.id, - "usuario_id": self.usuario_id, - "personaje_id": self.planeta_id, - "fecha_agregado": self.fecha_agregado - } - - usuario = relationship("Usuario", back_populates="personajes_favoritos") - personaje = relationship("Personaje", back_populates="favoritos") - - -class VehiculoFavorito(db.Model): - __tablename__ = "vehiculos_favoritos" - - id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - - def serialize(self): - return { - "id": self.id, - "usuario_id": self.usuario_id, - "vehiculo_id": self.planeta_id, - "fecha_agregado": self.fecha_agregado - } - - usuario = relationship("Usuario", back_populates="vehiculos_favoritos") - vehiculo = relationship("Vehiculo", back_populates="favoritos") diff --git a/src/models/__init__.py b/src/models/__init__.py index e69de29bb..84a05f296 100644 --- a/src/models/__init__.py +++ b/src/models/__init__.py @@ -0,0 +1,12 @@ +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import String, Boolean, Integer, ForeignKey, Float, Text, DateTime, func +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime + +db = SQLAlchemy() + +from .usuario import Usuario +from .personaje import Personaje +from .vehiculo import Vehiculo +from .planeta import Planeta +from .associations import PersonajeFavorito, PlanetaFavorito, VehiculoFavorito \ No newline at end of file diff --git a/src/models/associations.py b/src/models/associations.py index e69de29bb..7cc1c712e 100644 --- a/src/models/associations.py +++ b/src/models/associations.py @@ -0,0 +1,63 @@ +from .database import db +from sqlalchemy import ForeignKey, DateTime, func +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime + +class PlanetaFavorito(db.Model): + __tablename__ = "planetas_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + planeta_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "planeta_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } + + usuario = relationship("Usuario", back_populates="planetas_favoritos") + planeta = relationship("Planeta", back_populates="favoritos") + + +class PersonajeFavorito(db.Model): + __tablename__ = "personajes_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "personaje_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } + + usuario = relationship("Usuario", back_populates="personajes_favoritos") + personaje = relationship("Personaje", back_populates="favoritos") + + +class VehiculoFavorito(db.Model): + __tablename__ = "vehiculos_favoritos" + + id: Mapped[int] = mapped_column(primary_key=True) + usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) + vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + + def serialize(self): + return { + "id": self.id, + "usuario_id": self.usuario_id, + "vehiculo_id": self.planeta_id, + "fecha_agregado": self.fecha_agregado + } + + usuario = relationship("Usuario", back_populates="vehiculos_favoritos") + vehiculo = relationship("Vehiculo", back_populates="favoritos") \ No newline at end of file diff --git a/src/models/personaje.py b/src/models/personaje.py index e69de29bb..efa87d836 100644 --- a/src/models/personaje.py +++ b/src/models/personaje.py @@ -0,0 +1,41 @@ +from .database import db +from sqlalchemy import String, Integer, ForeignKey, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship + +class Personaje(db.Model): + __tablename__ = "personajes" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + especie: Mapped[str] = mapped_column(String(50), nullable=True) + altura: Mapped[int] = mapped_column(Integer, nullable=True) + peso: Mapped[int] = mapped_column(Integer, nullable=True) + genero: Mapped[str] = mapped_column(String(20), nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + planeta_natal_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=True) + vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"),unique = True, nullable=True) + + vehiculo = relationship("Vehiculo", foreign_keys=[vehiculo_id], back_populates="personaje", uselist=False) + planeta_natal = relationship("Planeta", back_populates="personajes") + favoritos = relationship("PersonajeFavorito", back_populates="personaje") + + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "especie": self.especie, + "altura": self.altura, + "peso": self.peso, + "genero": self.genero, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + def serialize_with_relations(self): + data = self.serialize() + if self.planeta_natal: + data['planeta_natal'] = self.planeta_natal.serialize() + if self.vehiculo: + data['vehiculo'] = self.vehiculo.serialize() + data['favoritos'] = [f.serialize() for f in self.favoritos] + return data \ No newline at end of file diff --git a/src/models/planeta.py b/src/models/planeta.py index e69de29bb..c2f47b37f 100644 --- a/src/models/planeta.py +++ b/src/models/planeta.py @@ -0,0 +1,33 @@ +from .database import db +from sqlalchemy import String, Integer, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship + +class Planeta(db.Model): + __tablename__ = "planetas" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + diametro: Mapped[int] = mapped_column(Integer, nullable=True) + clima: Mapped[str] = mapped_column(String(50), nullable=True) + poblacion: Mapped[int] = mapped_column(Integer, nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + + personajes = relationship("Personaje", back_populates="planeta_natal") + favoritos = relationship("PlanetaFavorito", back_populates="planeta") + + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "diametro": self.diametro, + "clima": self.clima, + "poblacion": self.poblacion, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + def serialize_with_relations(self): + data = self.serialize() + data['personajes'] = [p.serialize() for p in self.personajes] + data['favoritos'] = [f.serialize() for f in self.favoritos] + return data \ No newline at end of file diff --git a/src/models/usuario.py b/src/models/usuario.py index e69de29bb..66b2b3335 100644 --- a/src/models/usuario.py +++ b/src/models/usuario.py @@ -0,0 +1,39 @@ +from .database import db +from sqlalchemy import String, Boolean, DateTime, func +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime + +class Usuario(db.Model): + __tablename__ = "usuarios" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + apellido: Mapped[str] = mapped_column(String(100), nullable=False) + email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) + password: Mapped[str] = mapped_column(String(255), nullable=False) + fecha_registro: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + ultima_conexion: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + activo: Mapped[bool] = mapped_column(Boolean(), default=True) + + # Relaciones + planetas_favoritos = relationship("PlanetaFavorito", back_populates="usuario") + personajes_favoritos = relationship("PersonajeFavorito", back_populates="usuario") + vehiculos_favoritos = relationship("VehiculoFavorito", back_populates="usuario") + + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "apellido": self.apellido, + "email": self.email, + "fecha_registro": self.fecha_registro, + "ultima_conexion": self.ultima_conexion, + "activo": self.activo + } + + def serialize_with_relations(self): + data = self.serialize() + data['planetas_favoritos'] = [pf.serialize() for pf in self.planetas_favoritos] + data['personajes_favoritos'] = [pef.serialize() for pef in self.personajes_favoritos] + data['vehiculos_favoritos'] = [vf.serialize() for vf in self.vehiculos_favoritos] + return data \ No newline at end of file diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index e69de29bb..39a0b65f6 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -0,0 +1,40 @@ +from .database import db +from sqlalchemy import String, Integer, ForeignKey, Float, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship + +class Vehiculo(db.Model): + __tablename__ = "vehiculos" + + id: Mapped[int] = mapped_column(primary_key=True) + nombre: Mapped[str] = mapped_column(String(100), nullable=False) + modelo: Mapped[str] = mapped_column(String(100), nullable=True) + longitud: Mapped[float] = mapped_column(Float, nullable=True) + velocidad_maxima: Mapped[int] = mapped_column(Integer, nullable=True) + tripulacion: Mapped[int] = mapped_column(Integer, nullable=True) + pasajeros: Mapped[int] = mapped_column(Integer, nullable=True) + imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) + descripcion: Mapped[str] = mapped_column(Text, nullable=True) + personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) + + + personaje = relationship("Personaje", foreign_keys=[personaje_id], back_populates="vehiculo", uselist=False) + favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") + + def serialize(self): + return { + "id": self.id, + "nombre": self.nombre, + "modelo": self.modelo, + "longitud": self.longitud, + "velocidad_maxima": self.velocidad_maxima, + "tripulacion": self.tripulacion, + "pasajeros": self.pasajeros, + "imagen_url": self.imagen_url, + "descripcion": self.descripcion + } + def serialize_with_relations(self): + data = self.serialize() + if self.personaje: + data['personaje'] = self.personaje.serialize() + data['favoritos'] = [f.serialize() for f in self.favoritos] + return data \ No newline at end of file From fb763211b7784ce10cf802a8ed9c77c661d7cc2d Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Wed, 7 May 2025 16:11:50 +0000 Subject: [PATCH 10/16] Avances --- src/admin.py | 8 ++-- src/app.py | 84 +++++++++++++++++++++++++++++++---------- src/models/__init__.py | 14 +++---- src/models/personaje.py | 20 +++++++--- src/models/vehiculo.py | 15 ++++++-- 5 files changed, 98 insertions(+), 43 deletions(-) diff --git a/src/admin.py b/src/admin.py index 550d83536..1e294a09a 100644 --- a/src/admin.py +++ b/src/admin.py @@ -1,16 +1,14 @@ import os from flask_admin import Admin -from models import db -from flask_admin.contrib.sqla import ModelView + def setup_admin(app): app.secret_key = os.environ.get('FLASK_APP_KEY', 'sample key') app.config['FLASK_ADMIN_SWATCH'] = 'cerulean' admin = Admin(app, name='4Geeks Admin', template_mode='bootstrap3') - # Add your models here, for example this is how we add a the User model to the admin - #admin.add_view(ModelView(User, db.session)) + # admin.add_view(ModelView(User, db.session)) # You can duplicate that line to add mew models - # admin.add_view(ModelView(YourModelName, db.session)) \ No newline at end of file + # admin.add_view(ModelView(YourModelName, db.session)) diff --git a/src/app.py b/src/app.py index 01f296528..c4cb27255 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,8 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import * +from models import Personaje, Vehiculo, Planeta, Usuario, PersonajeFavorito, VehiculoFavorito, PlanetaFavorito +from models import db app = Flask(__name__) @@ -17,7 +18,8 @@ # Configuración de la base de datos db_url = os.getenv("DATABASE_URL") if db_url is not None: - app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace("postgres://", "postgresql://") + app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace( + "postgres://", "postgresql://") else: app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False @@ -29,54 +31,70 @@ setup_admin(app) # Manejo global de errores personalizados + + @app.errorhandler(APIException) def handle_invalid_usage(error): return jsonify(error.to_dict()), error.status_code # Genera un mapa visual de todas las rutas + + @app.route('/') def sitemap(): return generate_sitemap(app) # ENDPOINTS DE PERSONAJES + + @app.route('/people', methods=['GET']) def get_people(): people = Personaje.query.all() return jsonify([personaje.serialize() for personaje in people]), 200 + @app.route('/people/', methods=['GET']) def get_personaje(people_id): personaje = Personaje.query.get_or_404(people_id) return jsonify(personaje.serialize()), 200 # ENDPOINTS DE PLANETAS + + @app.route('/planets', methods=['GET']) def get_planetas(): planetas = Planeta.query.all() return jsonify([planeta.serialize() for planeta in planetas]), 200 + @app.route('/planets/', methods=['GET']) def get_planeta(planet_id): planeta = Planeta.query.get_or_404(planet_id) return jsonify(planeta.serialize()), 200 # ENDPOINTS DE VEHICULOS + + @app.route('/vehicles', methods=['GET']) def get_vehiculos(): vehiculos = Vehiculo.query.all() return jsonify([vehiculo.serialize() for vehiculo in vehiculos]), 200 + @app.route('/vehicles/', methods=['GET']) def get_vehiculo(vehicle_id): vehiculo = Vehiculo.query.get_or_404(vehicle_id) return jsonify(vehiculo.serialize()), 200 # ENDPOINTS DE USUARIOS Y FAVORITOS + + @app.route('/users', methods=['GET']) def get_users(): response = Usuario.query.all() return jsonify(response), 200 + @app.route('/users/favorites', methods=['GET']) def get_favorites(): user_id = Usuario.query.get(Usuario.id) @@ -92,10 +110,13 @@ def get_favorites(): return jsonify(favorites), 200 # ENDPOINTS DE FAVORITOS - ELIMINAR + + @app.route('/favorite/planet/', methods=['DELETE']) def delete_fav_planet(planet_id): user_id = Usuario.query.get(Usuario.id) - favorite = PlanetaFavorito.query.filter_by(usuario_id=user_id, planeta_id=planet_id).first() + favorite = PlanetaFavorito.query.filter_by( + usuario_id=user_id, planeta_id=planet_id).first() if not favorite: return jsonify({"message": "Favorite not found"}), 404 @@ -103,13 +124,16 @@ def delete_fav_planet(planet_id): db.session.delete(favorite) db.session.commit() - updated_favorites = PlanetaFavorito.query.filter_by(usuario_id=user_id).all() + updated_favorites = PlanetaFavorito.query.filter_by( + usuario_id=user_id).all() return jsonify([fav.serialize() for fav in updated_favorites]), 200 + @app.route('/favorite/people/', methods=['DELETE']) def delete_fav_person(people_id): user_id = Usuario.query.get(Usuario.id) - favorite = PersonajeFavorito.query.filter_by(usuario_id=user_id, personaje_id=people_id).first() + favorite = PersonajeFavorito.query.filter_by( + usuario_id=user_id, personaje_id=people_id).first() if not favorite: return jsonify({"message": "Person not found"}), 404 @@ -117,13 +141,16 @@ def delete_fav_person(people_id): db.session.delete(favorite) db.session.commit() - updated_favorites = PersonajeFavorito.query.filter_by(usuario_id=user_id).all() + updated_favorites = PersonajeFavorito.query.filter_by( + usuario_id=user_id).all() return jsonify([p.serialize() for p in updated_favorites]), 200 + @app.route('/favorite/vehicle/', methods=['DELETE']) def delete_fav_vehiculo(vehicle_id): user_id = Usuario.query.get(Usuario.id) - favorite = VehiculoFavorito.query.filter_by(usuario_id=user_id, vehiculo_id=vehicle_id).first() + favorite = VehiculoFavorito.query.filter_by( + usuario_id=user_id, vehiculo_id=vehicle_id).first() if not favorite: return jsonify({"message": "Vehículo favorito no encontrado"}), 404 @@ -131,10 +158,13 @@ def delete_fav_vehiculo(vehicle_id): db.session.delete(favorite) db.session.commit() - updated_favorites = VehiculoFavorito.query.filter_by(usuario_id=user_id).all() + updated_favorites = VehiculoFavorito.query.filter_by( + usuario_id=user_id).all() return jsonify([p.serialize() for p in updated_favorites]), 200 # ENDPOINTS DE ELIMINAR + + @app.route('/planets/', methods=['DELETE']) def delete_planeta(planet_id): planeta = Planeta.query.get_or_404(planet_id) @@ -143,6 +173,7 @@ def delete_planeta(planet_id): db.session.commit() return jsonify({"msg": f"Planeta {planet_id} eliminado"}), 200 + @app.route('/people/', methods=['DELETE']) def delete_personaje(people_id): personaje = Personaje.query.get_or_404(people_id) @@ -151,6 +182,7 @@ def delete_personaje(people_id): db.session.commit() return jsonify({"msg": f"Personaje {people_id} eliminado"}), 200 + @app.route('/vehicles/', methods=['DELETE']) def delete_vehiculo(vehicle_id): vehiculo = Vehiculo.query.get_or_404(vehicle_id) @@ -160,6 +192,8 @@ def delete_vehiculo(vehicle_id): return jsonify({"msg": f"Vehículo {vehicle_id} eliminado"}), 200 # ENDPOINTS DE FAVORITOS - AÑADIR + + @app.route('/favorite/planet/', methods=['POST']) def add_favorite_planet(planet_id): user = Usuario.query.get(Usuario.id) @@ -167,8 +201,9 @@ def add_favorite_planet(planet_id): if not planeta: return jsonify({'error': 'Planeta no encontrado'}), 404 - - favorito_existente= next((fav for fav in user.planetas_favoritos if fav.planeta_id == planet_id), None) + + favorito_existente = next( + (fav for fav in user.planetas_favoritos if fav.planeta_id == planet_id), None) if favorito_existente: return jsonify({'msg': 'El planeta ya está en favoritos'}), 400 nuevo_favorito = PlanetaFavorito(usuario_id=user.id, planeta_id=planet_id) @@ -177,6 +212,7 @@ def add_favorite_planet(planet_id): return jsonify({'msg': 'Planeta añadido a favoritos', 'planeta': planeta.nombre}), 201 + @app.route('/favorite/people/', methods=['POST']) def add_favorite_personaje(people_id): user = Usuario.query.get(Usuario.id) @@ -184,17 +220,20 @@ def add_favorite_personaje(people_id): if not personaje: return jsonify({'error': 'Personaje no encontrado'}), 404 - - favorito_existente= next((fav for fav in user.persoanjes_favoritos if fav.personaje_id == people_id), None) + + favorito_existente = next( + (fav for fav in user.persoanjes_favoritos if fav.personaje_id == people_id), None) if favorito_existente: return jsonify({'msg': 'El personaje ya está en favoritos'}), 400 - - nuevo_favorito = PersonajeFavorito(usuario_id=user.id, personaje_id=people_id) + + nuevo_favorito = PersonajeFavorito( + usuario_id=user.id, personaje_id=people_id) db.session.add(nuevo_favorito) db.session.commit() return jsonify({'msg': 'Personaje añadido a favoritos', 'personaje': personaje.nombre}), 201 + @app.route('/favorite/vehicle/', methods=['POST']) def add_favorite_vehiculo(vehicle_id): user = Usuario.query.get(Usuario.id) @@ -203,19 +242,23 @@ def add_favorite_vehiculo(vehicle_id): if not vehiculo: return jsonify({'error': 'Vehículo no encontrado'}), 404 - favorito_existente = next((fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) + favorito_existente = next( + (fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) if favorito_existente: return jsonify({'msg': 'El vehículo ya está en favoritos'}), 400 - nuevo_favorito = VehiculoFavorito(usuario_id=user.id, vehiculo_id=vehicle_id) + nuevo_favorito = VehiculoFavorito( + usuario_id=user.id, vehiculo_id=vehicle_id) db.session.add(nuevo_favorito) db.session.commit() - return jsonify({'msg': 'Vehículo añadido a favoritos','vehiculo': vehiculo.nombre}), 201 + return jsonify({'msg': 'Vehículo añadido a favoritos', 'vehiculo': vehiculo.nombre}), 201 + +# POSTs + +# POST Personajes -### POSTs -#POST Personajes @app.route('/people', methods=['POST']) def post_people(): request_body = request.get_json() @@ -255,7 +298,7 @@ def post_people(): return jsonify({"message": "Personaje created successfully"}), 201 -### PUTs +# PUTs # PUT Personajes @app.route('/people/', methods=['PUT']) def put_people(people_id): @@ -296,6 +339,7 @@ def put_people(people_id): return jsonify({"message": "Personaje updated successfully"}), 201 + # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': PORT = int(os.environ.get('PORT', 3000)) diff --git a/src/models/__init__.py b/src/models/__init__.py index 84a05f296..0b5e605a4 100644 --- a/src/models/__init__.py +++ b/src/models/__init__.py @@ -1,12 +1,8 @@ +from .favoritos import PersonajeFavorito, VehiculoFavorito, PlanetaFavorito +from .usuario import Usuario +from .planeta import Planeta +from .vehiculo import Vehiculo +from .personaje import Personaje from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean, Integer, ForeignKey, Float, Text, DateTime, func -from sqlalchemy.orm import Mapped, mapped_column, relationship -from datetime import datetime db = SQLAlchemy() - -from .usuario import Usuario -from .personaje import Personaje -from .vehiculo import Vehiculo -from .planeta import Planeta -from .associations import PersonajeFavorito, PlanetaFavorito, VehiculoFavorito \ No newline at end of file diff --git a/src/models/personaje.py b/src/models/personaje.py index efa87d836..a7d7e6eb6 100644 --- a/src/models/personaje.py +++ b/src/models/personaje.py @@ -1,6 +1,8 @@ from .database import db from sqlalchemy import String, Integer, ForeignKey, Text from sqlalchemy.orm import Mapped, mapped_column, relationship +from .vehiculo import Vehiculo + class Personaje(db.Model): __tablename__ = "personajes" @@ -13,10 +15,17 @@ class Personaje(db.Model): genero: Mapped[str] = mapped_column(String(20), nullable=True) imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) descripcion: Mapped[str] = mapped_column(Text, nullable=True) - planeta_natal_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=True) - vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"),unique = True, nullable=True) + planeta_natal_id: Mapped[int] = mapped_column( + ForeignKey("planetas.id"), nullable=True) + vehiculo_id: Mapped[int] = mapped_column( + ForeignKey("vehiculos.id"), unique=True, nullable=True) - vehiculo = relationship("Vehiculo", foreign_keys=[vehiculo_id], back_populates="personaje", uselist=False) + vehiculo = relationship( + "Vehiculo", + back_populates="personaje", + uselist=False, + # foreign_keys=[Vehiculo.personaje_id] + ) planeta_natal = relationship("Planeta", back_populates="personajes") favoritos = relationship("PersonajeFavorito", back_populates="personaje") @@ -30,7 +39,8 @@ def serialize(self): "genero": self.genero, "imagen_url": self.imagen_url, "descripcion": self.descripcion - } + } + def serialize_with_relations(self): data = self.serialize() if self.planeta_natal: @@ -38,4 +48,4 @@ def serialize_with_relations(self): if self.vehiculo: data['vehiculo'] = self.vehiculo.serialize() data['favoritos'] = [f.serialize() for f in self.favoritos] - return data \ No newline at end of file + return data diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index 39a0b65f6..d95c0ea6e 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -2,6 +2,7 @@ from sqlalchemy import String, Integer, ForeignKey, Float, Text from sqlalchemy.orm import Mapped, mapped_column, relationship + class Vehiculo(db.Model): __tablename__ = "vehiculos" @@ -14,10 +15,15 @@ class Vehiculo(db.Model): pasajeros: Mapped[int] = mapped_column(Integer, nullable=True) imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) descripcion: Mapped[str] = mapped_column(Text, nullable=True) - personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) - + personaje_id: Mapped[int] = mapped_column( + ForeignKey("personajes.id"), unique=True, nullable=True) - personaje = relationship("Personaje", foreign_keys=[personaje_id], back_populates="vehiculo", uselist=False) + personaje = relationship( + "Personaje", + back_populates="vehiculo", + uselist=False, + # foreign_keys=[personaje_id] + ) favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") def serialize(self): @@ -32,9 +38,10 @@ def serialize(self): "imagen_url": self.imagen_url, "descripcion": self.descripcion } + def serialize_with_relations(self): data = self.serialize() if self.personaje: data['personaje'] = self.personaje.serialize() data['favoritos'] = [f.serialize() for f in self.favoritos] - return data \ No newline at end of file + return data From f3e51884f50e616aee83ed37aa34e0138e95a551 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Wed, 7 May 2025 16:45:33 +0000 Subject: [PATCH 11/16] funciones PUT --- src/app.py | 144 ++++++++++++++++++++++++++++++---------- src/models/__init__.py | 8 +-- src/models/personaje.py | 13 +++- src/models/vehiculo.py | 12 +++- 4 files changed, 130 insertions(+), 47 deletions(-) diff --git a/src/app.py b/src/app.py index 01f296528..6b7c504b2 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import * +from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito app = Flask(__name__) @@ -28,6 +28,10 @@ CORS(app) setup_admin(app) +with app.app_context(): + print("Tablas registradas:", db.metadata.tables.keys()) + db.create_all() + # Manejo global de errores personalizados @app.errorhandler(APIException) def handle_invalid_usage(error): @@ -256,45 +260,115 @@ def post_people(): ### PUTs + +def validar_tipo(valor, tipo_esperado, nombre_campo): + if valor is not None and not isinstance(valor, tipo_esperado): + raise ValueError(f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") + # PUT Personajes @app.route('/people/', methods=['PUT']) -def put_people(people_id): - data = Personaje.query.get_or_404(people_id) - request_body = request.get_json() +def update_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + personaje.nombre = data["nombre"] + if "especie" in data: + validar_tipo(data["especie"], str, "especie") + personaje.especie = data["especie"] + if "altura" in data: + validar_tipo(data["altura"], int, "altura") + personaje.altura = data["altura"] + if "peso" in data: + validar_tipo(data["peso"], int, "peso") + personaje.peso = data["peso"] + if "genero" in data: + validar_tipo(data["genero"], str, "genero") + personaje.genero = data["genero"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + personaje.descripcion = data["descripcion"] + if "planeta_natal_id" in data: + validar_tipo(data["planeta_natal_id"], int, "planeta_natal_id") + personaje.planeta_natal_id = data["planeta_natal_id"] + if "vehiculo_id" in data: + validar_tipo(data["vehiculo_id"], int, "vehiculo_id") + personaje.vehiculo_id = data["vehiculo_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 - if not request_body: - return jsonify({"error": "Empty request body"}), 400 + db.session.commit() + return jsonify(personaje.serialize()), 200 - if not isinstance(request_body.get("nombre"), str): - return jsonify({"error": "'nombre' must be a string"}), 400 - else: - data.nombre = request_body.nombre - if not isinstance(request_body.get("especie"), str): - return jsonify({"error": "'especie' must be a string"}), 400 - else: - data.especie = request_body.especie - if not isinstance(request_body.get("altura"), (int, float)): - return jsonify({"error": "'altura' must be a number"}), 400 - else: - data.altura = request_body.altura - if not isinstance(request_body.get("peso"), (int, float)): - return jsonify({"error": "'peso' must be a number"}), 400 - else: - data.peso = request_body.peso - if not isinstance(request_body.get("genero"), str): - return jsonify({"error": "'genero' must be a string"}), 400 - else: - data.genero = request_body.genero - if not isinstance(request_body.get("imagen_url"), str): - return jsonify({"error": "'imagen_url' must be a string"}), 400 - else: - data.image_url = request_body.image_url - if not isinstance(request_body.get("descripcion"), str): - return jsonify({"error": "'descripcion' must be a string"}), 400 - else: - data.descripcion = request_body.descripcion +# PUT Planetas +@app.route('/planets/', methods=['PUT']) +def update_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + planeta.nombre = data["nombre"] + if "diametro" in data: + validar_tipo(data["diametro"], int, "diametro") + planeta.diametro = data["diametro"] + if "clima" in data: + validar_tipo(data["clima"], str, "clima") + planeta.clima = data["clima"] + if "poblacion" in data: + validar_tipo(data["poblacion"], int, "poblacion") + planeta.poblacion = data["poblacion"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + planeta.descripcion = data["descripcion"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 - return jsonify({"message": "Personaje updated successfully"}), 201 + db.session.commit() + return jsonify(planeta.serialize()), 200 + +# PUT Vehiculos +@app.route('/vehicles/', methods=['PUT']) +def update_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + vehiculo.nombre = data["nombre"] + if "modelo" in data: + validar_tipo(data["modelo"], str, "modelo") + vehiculo.modelo = data["modelo"] + if "longitud" in data: + validar_tipo(data["longitud"], float, "longitud") + vehiculo.longitud = data["longitud"] + if "velocidad_maxima" in data: + validar_tipo(data["velocidad_maxima"], int, "velocidad_maxima") + vehiculo.velocidad_maxima = data["velocidad_maxima"] + if "tripulacion" in data: + validar_tipo(data["tripulacion"], int, "tripulacion") + vehiculo.tripulacion = data["tripulacion"] + if "pasajeros" in data: + validar_tipo(data["pasajeros"], int, "pasajeros") + vehiculo.pasajeros = data["pasajeros"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + vehiculo.descripcion = data["descripcion"] + if "personaje_id" in data: + validar_tipo(data["personaje_id"], int, "personaje_id") + vehiculo.personaje_id = data["personaje_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(vehiculo.serialize()), 200 # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': diff --git a/src/models/__init__.py b/src/models/__init__.py index 84a05f296..ab00871fb 100644 --- a/src/models/__init__.py +++ b/src/models/__init__.py @@ -1,10 +1,4 @@ -from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean, Integer, ForeignKey, Float, Text, DateTime, func -from sqlalchemy.orm import Mapped, mapped_column, relationship -from datetime import datetime - -db = SQLAlchemy() - +from .database import db from .usuario import Usuario from .personaje import Personaje from .vehiculo import Vehiculo diff --git a/src/models/personaje.py b/src/models/personaje.py index efa87d836..f6451001f 100644 --- a/src/models/personaje.py +++ b/src/models/personaje.py @@ -16,7 +16,12 @@ class Personaje(db.Model): planeta_natal_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=True) vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"),unique = True, nullable=True) - vehiculo = relationship("Vehiculo", foreign_keys=[vehiculo_id], back_populates="personaje", uselist=False) + vehiculo = relationship( + "Vehiculo", + foreign_keys=[vehiculo_id], + back_populates="personaje", + uselist=False + ) planeta_natal = relationship("Planeta", back_populates="personajes") favoritos = relationship("PersonajeFavorito", back_populates="personaje") @@ -29,8 +34,10 @@ def serialize(self): "peso": self.peso, "genero": self.genero, "imagen_url": self.imagen_url, - "descripcion": self.descripcion - } + "descripcion": self.descripcion, + "planeta_natal_id": self.planeta_natal_id, + "vehiculo_id": self.vehiculo_id + } def serialize_with_relations(self): data = self.serialize() if self.planeta_natal: diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index 39a0b65f6..a1482acdd 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -17,7 +17,14 @@ class Vehiculo(db.Model): personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=True) - personaje = relationship("Personaje", foreign_keys=[personaje_id], back_populates="vehiculo", uselist=False) + personaje = relationship( + "Personaje", + back_populates="vehiculo", + uselist=False, + foreign_keys="Personaje.vehiculo_id", # Indica quién posee la relación + remote_side="Personaje.vehiculo_id", + primaryjoin="Vehiculo.id == Personaje.vehiculo_id" # Condición explícita + ) favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") def serialize(self): @@ -30,7 +37,8 @@ def serialize(self): "tripulacion": self.tripulacion, "pasajeros": self.pasajeros, "imagen_url": self.imagen_url, - "descripcion": self.descripcion + "descripcion": self.descripcion, + "personaje_id": self.personaje_id } def serialize_with_relations(self): data = self.serialize() From db0cdc74b5d3070961c85f7a09f4a0a361780b75 Mon Sep 17 00:00:00 2001 From: Luis Oballos Mancini Date: Wed, 7 May 2025 17:58:49 +0000 Subject: [PATCH 12/16] Requests done --- src/admin.py | 2 + src/app.py | 222 ++++++++++++++++++++++++++++++++++------ src/models/__init__.py | 10 +- src/models/personaje.py | 18 ++-- src/models/planeta.py | 12 ++- src/models/usuario.py | 33 ++++-- src/models/vehiculo.py | 16 ++- 7 files changed, 252 insertions(+), 61 deletions(-) diff --git a/src/admin.py b/src/admin.py index 1e294a09a..6c168b8be 100644 --- a/src/admin.py +++ b/src/admin.py @@ -1,5 +1,7 @@ import os from flask_admin import Admin +from models import db +from flask_admin.contrib.sqla import ModelView def setup_admin(app): diff --git a/src/app.py b/src/app.py index c4cb27255..418852a20 100644 --- a/src/app.py +++ b/src/app.py @@ -8,8 +8,7 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import Personaje, Vehiculo, Planeta, Usuario, PersonajeFavorito, VehiculoFavorito, PlanetaFavorito -from models import db +from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito app = Flask(__name__) @@ -30,6 +29,10 @@ CORS(app) setup_admin(app) +with app.app_context(): + print("Tablas registradas:", db.metadata.tables.keys()) + db.create_all() + # Manejo global de errores personalizados @@ -297,47 +300,206 @@ def post_people(): return jsonify({"message": "Personaje created successfully"}), 201 +# POST Planetas -# PUTs -# PUT Personajes -@app.route('/people/', methods=['PUT']) -def put_people(people_id): - data = Personaje.query.get_or_404(people_id) + +@app.route('/planeta', methods=['POST']) +def post_planeta(): request_body = request.get_json() if not request_body: return jsonify({"error": "Empty request body"}), 400 + # Validate required fields and their types if not isinstance(request_body.get("nombre"), str): return jsonify({"error": "'nombre' must be a string"}), 400 - else: - data.nombre = request_body.nombre - if not isinstance(request_body.get("especie"), str): - return jsonify({"error": "'especie' must be a string"}), 400 - else: - data.especie = request_body.especie - if not isinstance(request_body.get("altura"), (int, float)): - return jsonify({"error": "'altura' must be a number"}), 400 - else: - data.altura = request_body.altura - if not isinstance(request_body.get("peso"), (int, float)): - return jsonify({"error": "'peso' must be a number"}), 400 - else: - data.peso = request_body.peso - if not isinstance(request_body.get("genero"), str): - return jsonify({"error": "'genero' must be a string"}), 400 - else: - data.genero = request_body.genero + if not isinstance(request_body.get("diametro"), int): + return jsonify({"error": "'diametro' must be a string"}), 400 + if not isinstance(request_body.get("clima"), str): + return jsonify({"error": "'clima' must be a number"}), 400 + if not isinstance(request_body.get("poblacion"), int): + return jsonify({"error": "'poblacion' must be a number"}), 400 if not isinstance(request_body.get("imagen_url"), str): return jsonify({"error": "'imagen_url' must be a string"}), 400 - else: - data.image_url = request_body.image_url if not isinstance(request_body.get("descripcion"), str): return jsonify({"error": "'descripcion' must be a string"}), 400 - else: - data.descripcion = request_body.descripcion - return jsonify({"message": "Personaje updated successfully"}), 201 + new_planeta = Planeta( + nombre=request_body['nombre'], + diametro=request_body['diametro'], + clima=request_body['clima'], + poblacion=request_body['poblacion'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_planeta) + db.session.commit() + + return jsonify({"message": "Planet created successfully"}), 201 + +# POST Vehiculo + + +@app.route('/vehiculo', methods=['POST']) +def post_vehiculo(): + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("modelo"), str): + return jsonify({"error": "'modelo' must be a string"}), 400 + if not isinstance(request_body.get("longitud"), (int, float)): + return jsonify({"error": "'longitud' must be a number"}), 400 + if not isinstance(request_body.get("velocidad_maxima"), (int, float)): + return jsonify({"error": "'velocidad_maxima' must be a number"}), 400 + if not isinstance(request_body.get("tripulacion"), int): + return jsonify({"error": "'tripulacion' must be a string"}), 400 + if not isinstance(request_body.get("pasajeros"), int): + return jsonify({"error": "'pasajeros' must be a string"}), 400 + if not isinstance(request_body.get("image_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + + new_vehiculo = Planeta( + nombre=request_body['nombre'], + modelo=request_body['modelo'], + longitud=request_body['longitud'], + velocidad_maxima=request_body['velocidad_maxima'], + tripulacion=request_body['tripulacion'], + pasajeros=request_body['pasajeros'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_vehiculo) + db.session.commit() + + return jsonify({"message": "Vehiculo created successfully"}), 201 + + +# PUTs + +def validar_tipo(valor, tipo_esperado, nombre_campo): + if valor is not None and not isinstance(valor, tipo_esperado): + raise ValueError( + f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") + +# PUT Personajes + + +@app.route('/people/', methods=['PUT']) +def update_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + personaje.nombre = data["nombre"] + if "especie" in data: + validar_tipo(data["especie"], str, "especie") + personaje.especie = data["especie"] + if "altura" in data: + validar_tipo(data["altura"], int, "altura") + personaje.altura = data["altura"] + if "peso" in data: + validar_tipo(data["peso"], int, "peso") + personaje.peso = data["peso"] + if "genero" in data: + validar_tipo(data["genero"], str, "genero") + personaje.genero = data["genero"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + personaje.descripcion = data["descripcion"] + if "planeta_natal_id" in data: + validar_tipo(data["planeta_natal_id"], int, "planeta_natal_id") + personaje.planeta_natal_id = data["planeta_natal_id"] + if "vehiculo_id" in data: + validar_tipo(data["vehiculo_id"], int, "vehiculo_id") + personaje.vehiculo_id = data["vehiculo_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(personaje.serialize()), 200 + +# PUT Planetas + + +@app.route('/planets/', methods=['PUT']) +def update_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + planeta.nombre = data["nombre"] + if "diametro" in data: + validar_tipo(data["diametro"], int, "diametro") + planeta.diametro = data["diametro"] + if "clima" in data: + validar_tipo(data["clima"], str, "clima") + planeta.clima = data["clima"] + if "poblacion" in data: + validar_tipo(data["poblacion"], int, "poblacion") + planeta.poblacion = data["poblacion"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + planeta.descripcion = data["descripcion"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(planeta.serialize()), 200 + +# PUT Vehiculos + + +@app.route('/vehicles/', methods=['PUT']) +def update_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + vehiculo.nombre = data["nombre"] + if "modelo" in data: + validar_tipo(data["modelo"], str, "modelo") + vehiculo.modelo = data["modelo"] + if "longitud" in data: + validar_tipo(data["longitud"], float, "longitud") + vehiculo.longitud = data["longitud"] + if "velocidad_maxima" in data: + validar_tipo(data["velocidad_maxima"], int, "velocidad_maxima") + vehiculo.velocidad_maxima = data["velocidad_maxima"] + if "tripulacion" in data: + validar_tipo(data["tripulacion"], int, "tripulacion") + vehiculo.tripulacion = data["tripulacion"] + if "pasajeros" in data: + validar_tipo(data["pasajeros"], int, "pasajeros") + vehiculo.pasajeros = data["pasajeros"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + vehiculo.descripcion = data["descripcion"] + if "personaje_id" in data: + validar_tipo(data["personaje_id"], int, "personaje_id") + vehiculo.personaje_id = data["personaje_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(vehiculo.serialize()), 200 # this only runs if `$ python src/app.py` is executed diff --git a/src/models/__init__.py b/src/models/__init__.py index 0b5e605a4..68ae2180f 100644 --- a/src/models/__init__.py +++ b/src/models/__init__.py @@ -1,8 +1,6 @@ -from .favoritos import PersonajeFavorito, VehiculoFavorito, PlanetaFavorito +from .database import db from .usuario import Usuario -from .planeta import Planeta -from .vehiculo import Vehiculo from .personaje import Personaje -from flask_sqlalchemy import SQLAlchemy - -db = SQLAlchemy() +from .vehiculo import Vehiculo +from .planeta import Planeta +from .associations import PersonajeFavorito, PlanetaFavorito, VehiculoFavorito diff --git a/src/models/personaje.py b/src/models/personaje.py index a7d7e6eb6..be4390e30 100644 --- a/src/models/personaje.py +++ b/src/models/personaje.py @@ -2,6 +2,8 @@ from sqlalchemy import String, Integer, ForeignKey, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from .vehiculo import Vehiculo +from .planeta import Planeta +from .associations import PersonajeFavorito class Personaje(db.Model): @@ -20,14 +22,16 @@ class Personaje(db.Model): vehiculo_id: Mapped[int] = mapped_column( ForeignKey("vehiculos.id"), unique=True, nullable=True) - vehiculo = relationship( + vehiculo: Mapped["Vehiculo"] = relationship( "Vehiculo", + foreign_keys=[vehiculo_id], back_populates="personaje", - uselist=False, - # foreign_keys=[Vehiculo.personaje_id] + uselist=False ) - planeta_natal = relationship("Planeta", back_populates="personajes") - favoritos = relationship("PersonajeFavorito", back_populates="personaje") + planeta_natal: Mapped["Planeta"] = relationship( + "Planeta", back_populates="personajes") + favoritos: Mapped["PersonajeFavorito"] = relationship( + "PersonajeFavorito", back_populates="personaje") def serialize(self): return { @@ -38,7 +42,9 @@ def serialize(self): "peso": self.peso, "genero": self.genero, "imagen_url": self.imagen_url, - "descripcion": self.descripcion + "descripcion": self.descripcion, + "planeta_natal_id": self.planeta_natal_id, + "vehiculo_id": self.vehiculo_id } def serialize_with_relations(self): diff --git a/src/models/planeta.py b/src/models/planeta.py index c2f47b37f..80a1a4edd 100644 --- a/src/models/planeta.py +++ b/src/models/planeta.py @@ -1,6 +1,9 @@ from .database import db from sqlalchemy import String, Integer, Text from sqlalchemy.orm import Mapped, mapped_column, relationship +from .personaje import Personaje +from .associations import PlanetaFavorito + class Planeta(db.Model): __tablename__ = "planetas" @@ -13,8 +16,10 @@ class Planeta(db.Model): imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) descripcion: Mapped[str] = mapped_column(Text, nullable=True) - personajes = relationship("Personaje", back_populates="planeta_natal") - favoritos = relationship("PlanetaFavorito", back_populates="planeta") + personajes: Mapped["Personaje"] = relationship( + "Personaje", back_populates="planeta_natal") + favoritos: Mapped["PlanetaFavorito"] = relationship( + "PlanetaFavorito", back_populates="planeta") def serialize(self): return { @@ -26,8 +31,9 @@ def serialize(self): "imagen_url": self.imagen_url, "descripcion": self.descripcion } + def serialize_with_relations(self): data = self.serialize() data['personajes'] = [p.serialize() for p in self.personajes] data['favoritos'] = [f.serialize() for f in self.favoritos] - return data \ No newline at end of file + return data diff --git a/src/models/usuario.py b/src/models/usuario.py index 66b2b3335..436a6c7b5 100644 --- a/src/models/usuario.py +++ b/src/models/usuario.py @@ -2,6 +2,8 @@ from sqlalchemy import String, Boolean, DateTime, func from sqlalchemy.orm import Mapped, mapped_column, relationship from datetime import datetime +from .associations import PersonajeFavorito, PlanetaFavorito, VehiculoFavorito + class Usuario(db.Model): __tablename__ = "usuarios" @@ -9,16 +11,22 @@ class Usuario(db.Model): id: Mapped[int] = mapped_column(primary_key=True) nombre: Mapped[str] = mapped_column(String(100), nullable=False) apellido: Mapped[str] = mapped_column(String(100), nullable=False) - email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) + email: Mapped[str] = mapped_column( + String(100), unique=True, nullable=False) password: Mapped[str] = mapped_column(String(255), nullable=False) - fecha_registro: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - ultima_conexion: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + fecha_registro: Mapped[datetime] = mapped_column( + DateTime, default=func.now()) + ultima_conexion: Mapped[datetime] = mapped_column( + DateTime, default=func.now()) activo: Mapped[bool] = mapped_column(Boolean(), default=True) # Relaciones - planetas_favoritos = relationship("PlanetaFavorito", back_populates="usuario") - personajes_favoritos = relationship("PersonajeFavorito", back_populates="usuario") - vehiculos_favoritos = relationship("VehiculoFavorito", back_populates="usuario") + planetas_favoritos: Mapped["PlanetaFavorito"] = relationship( + "PlanetaFavorito", back_populates="usuario") + personajes_favoritos: Mapped["PersonajeFavorito"] = relationship( + "PersonajeFavorito", back_populates="usuario") + vehiculos_favoritos: Mapped["VehiculoFavorito"] = relationship( + "VehiculoFavorito", back_populates="usuario") def serialize(self): return { @@ -30,10 +38,13 @@ def serialize(self): "ultima_conexion": self.ultima_conexion, "activo": self.activo } - + def serialize_with_relations(self): data = self.serialize() - data['planetas_favoritos'] = [pf.serialize() for pf in self.planetas_favoritos] - data['personajes_favoritos'] = [pef.serialize() for pef in self.personajes_favoritos] - data['vehiculos_favoritos'] = [vf.serialize() for vf in self.vehiculos_favoritos] - return data \ No newline at end of file + data['planetas_favoritos'] = [pf.serialize() + for pf in self.planetas_favoritos] + data['personajes_favoritos'] = [pef.serialize() + for pef in self.personajes_favoritos] + data['vehiculos_favoritos'] = [vf.serialize() + for vf in self.vehiculos_favoritos] + return data diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index d95c0ea6e..f9a6ff462 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -1,6 +1,8 @@ from .database import db from sqlalchemy import String, Integer, ForeignKey, Float, Text from sqlalchemy.orm import Mapped, mapped_column, relationship +from .personaje import Personaje +from .associations import VehiculoFavorito class Vehiculo(db.Model): @@ -16,15 +18,18 @@ class Vehiculo(db.Model): imagen_url: Mapped[str] = mapped_column(String(255), nullable=True) descripcion: Mapped[str] = mapped_column(Text, nullable=True) personaje_id: Mapped[int] = mapped_column( - ForeignKey("personajes.id"), unique=True, nullable=True) + ForeignKey("personajes.id"), nullable=True) - personaje = relationship( + personaje: Mapped["Personaje"] = relationship( "Personaje", back_populates="vehiculo", uselist=False, - # foreign_keys=[personaje_id] + foreign_keys="Personaje.vehiculo_id", + remote_side="Personaje.vehiculo_id", + primaryjoin="Vehiculo.id == Personaje.vehiculo_id" ) - favoritos = relationship("VehiculoFavorito", back_populates="vehiculo") + favoritos: Mapped["VehiculoFavorito"] = relationship( + "VehiculoFavorito", back_populates="vehiculo") def serialize(self): return { @@ -36,7 +41,8 @@ def serialize(self): "tripulacion": self.tripulacion, "pasajeros": self.pasajeros, "imagen_url": self.imagen_url, - "descripcion": self.descripcion + "descripcion": self.descripcion, + "personaje_id": self.personaje_id } def serialize_with_relations(self): From c3061185fd4e576cdd575587c4e61ff2dea9b25c Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Fri, 9 May 2025 13:47:00 +0000 Subject: [PATCH 13/16] routes --- src/app.py | 455 +-------------------------------- src/models/associations.py | 36 ++- src/models/personaje.py | 7 +- src/models/planeta.py | 5 +- src/models/vehiculo.py | 5 +- src/routes/__init__.py | 5 + src/routes/favoritos_routes.py | 118 +++++++++ src/routes/personaje_routes.py | 104 ++++++++ src/routes/planeta_routes.py | 91 +++++++ src/routes/usuario_routes.py | 26 ++ src/routes/vehiculo_routes.py | 108 ++++++++ 11 files changed, 496 insertions(+), 464 deletions(-) create mode 100644 src/routes/__init__.py create mode 100644 src/routes/favoritos_routes.py create mode 100644 src/routes/personaje_routes.py create mode 100644 src/routes/planeta_routes.py create mode 100644 src/routes/usuario_routes.py create mode 100644 src/routes/vehiculo_routes.py diff --git a/src/app.py b/src/app.py index 45b8ddc27..112ec4a03 100644 --- a/src/app.py +++ b/src/app.py @@ -9,6 +9,7 @@ from utils import APIException, generate_sitemap from admin import setup_admin from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito +from routes import personaje_bp, planeta_bp, vehiculo_bp, usuario_bp, favoritos_bp app = Flask(__name__) @@ -33,6 +34,13 @@ print("Tablas registradas:", db.metadata.tables.keys()) db.create_all() +app.register_blueprint(personaje_bp) +app.register_blueprint(planeta_bp) +app.register_blueprint(vehiculo_bp) +app.register_blueprint(usuario_bp) +app.register_blueprint(favoritos_bp) + + # Manejo global de errores personalizados @@ -47,459 +55,12 @@ def handle_invalid_usage(error): def sitemap(): return generate_sitemap(app) -# ENDPOINTS DE PERSONAJES - - -@app.route('/people', methods=['GET']) -def get_people(): - people = Personaje.query.all() - return jsonify([personaje.serialize() for personaje in people]), 200 - - -@app.route('/people/', methods=['GET']) -def get_personaje(people_id): - personaje = Personaje.query.get_or_404(people_id) - return jsonify(personaje.serialize()), 200 - -# ENDPOINTS DE PLANETAS - - -@app.route('/planets', methods=['GET']) -def get_planetas(): - planetas = Planeta.query.all() - return jsonify([planeta.serialize() for planeta in planetas]), 200 - - -@app.route('/planets/', methods=['GET']) -def get_planeta(planet_id): - planeta = Planeta.query.get_or_404(planet_id) - return jsonify(planeta.serialize()), 200 - -# ENDPOINTS DE VEHICULOS - - -@app.route('/vehicles', methods=['GET']) -def get_vehiculos(): - vehiculos = Vehiculo.query.all() - return jsonify([vehiculo.serialize() for vehiculo in vehiculos]), 200 - - -@app.route('/vehicles/', methods=['GET']) -def get_vehiculo(vehicle_id): - vehiculo = Vehiculo.query.get_or_404(vehicle_id) - return jsonify(vehiculo.serialize()), 200 - -# ENDPOINTS DE USUARIOS Y FAVORITOS - - -@app.route('/users', methods=['GET']) -def get_users(): - response = Usuario.query.all() - return jsonify(response), 200 - - -@app.route('/users/favorites', methods=['GET']) -def get_favorites(): - user_id = Usuario.query.get(Usuario.id) - - if not user_id: - return jsonify({"error": "User not found"}), 404 - - favorites = { - "planetas": [p.serialize() for p in Usuario.planetas_favoritos], - "personajes": [p.serialize() for p in Usuario.personajes_favoritos], - "vehiculos": [v.serialize() for v in Usuario.vehiculos_favoritos], - } - return jsonify(favorites), 200 - -# ENDPOINTS DE FAVORITOS - ELIMINAR - - -@app.route('/favorite/planet/', methods=['DELETE']) -def delete_fav_planet(planet_id): - user_id = Usuario.query.get(Usuario.id) - favorite = PlanetaFavorito.query.filter_by( - usuario_id=user_id, planeta_id=planet_id).first() - - if not favorite: - return jsonify({"message": "Favorite not found"}), 404 - - db.session.delete(favorite) - db.session.commit() - - updated_favorites = PlanetaFavorito.query.filter_by( - usuario_id=user_id).all() - return jsonify([fav.serialize() for fav in updated_favorites]), 200 - - -@app.route('/favorite/people/', methods=['DELETE']) -def delete_fav_person(people_id): - user_id = Usuario.query.get(Usuario.id) - favorite = PersonajeFavorito.query.filter_by( - usuario_id=user_id, personaje_id=people_id).first() - - if not favorite: - return jsonify({"message": "Person not found"}), 404 - - db.session.delete(favorite) - db.session.commit() - - updated_favorites = PersonajeFavorito.query.filter_by( - usuario_id=user_id).all() - return jsonify([p.serialize() for p in updated_favorites]), 200 - - -@app.route('/favorite/vehicle/', methods=['DELETE']) -def delete_fav_vehiculo(vehicle_id): - user_id = Usuario.query.get(Usuario.id) - favorite = VehiculoFavorito.query.filter_by( - usuario_id=user_id, vehiculo_id=vehicle_id).first() - - if not favorite: - return jsonify({"message": "Vehículo favorito no encontrado"}), 404 - - db.session.delete(favorite) - db.session.commit() - - updated_favorites = VehiculoFavorito.query.filter_by( - usuario_id=user_id).all() - return jsonify([p.serialize() for p in updated_favorites]), 200 - -# ENDPOINTS DE ELIMINAR - - -@app.route('/planets/', methods=['DELETE']) -def delete_planeta(planet_id): - planeta = Planeta.query.get_or_404(planet_id) - - db.session.delete(planeta) - db.session.commit() - return jsonify({"msg": f"Planeta {planet_id} eliminado"}), 200 - - -@app.route('/people/', methods=['DELETE']) -def delete_personaje(people_id): - personaje = Personaje.query.get_or_404(people_id) - - db.session.delete(personaje) - db.session.commit() - return jsonify({"msg": f"Personaje {people_id} eliminado"}), 200 - - -@app.route('/vehicles/', methods=['DELETE']) -def delete_vehiculo(vehicle_id): - vehiculo = Vehiculo.query.get_or_404(vehicle_id) - - db.session.delete(vehiculo) - db.session.commit() - return jsonify({"msg": f"Vehículo {vehicle_id} eliminado"}), 200 - -# ENDPOINTS DE FAVORITOS - AÑADIR - - -@app.route('/favorite/planet/', methods=['POST']) -def add_favorite_planet(planet_id): - user = Usuario.query.get(Usuario.id) - planeta = Planeta.query.get(planet_id) - - if not planeta: - return jsonify({'error': 'Planeta no encontrado'}), 404 - - favorito_existente = next( - (fav for fav in user.planetas_favoritos if fav.planeta_id == planet_id), None) - if favorito_existente: - return jsonify({'msg': 'El planeta ya está en favoritos'}), 400 - nuevo_favorito = PlanetaFavorito(usuario_id=user.id, planeta_id=planet_id) - db.session.add(nuevo_favorito) - db.session.commit() - - return jsonify({'msg': 'Planeta añadido a favoritos', 'planeta': planeta.nombre}), 201 - - -@app.route('/favorite/people/', methods=['POST']) -def add_favorite_personaje(people_id): - user = Usuario.query.get(Usuario.id) - personaje = Personaje.query.get(people_id) - - if not personaje: - return jsonify({'error': 'Personaje no encontrado'}), 404 - - favorito_existente = next( - (fav for fav in user.persoanjes_favoritos if fav.personaje_id == people_id), None) - if favorito_existente: - return jsonify({'msg': 'El personaje ya está en favoritos'}), 400 - - nuevo_favorito = PersonajeFavorito( - usuario_id=user.id, personaje_id=people_id) - db.session.add(nuevo_favorito) - db.session.commit() - - return jsonify({'msg': 'Personaje añadido a favoritos', 'personaje': personaje.nombre}), 201 - - -@app.route('/favorite/vehicle/', methods=['POST']) -def add_favorite_vehiculo(vehicle_id): - user = Usuario.query.get(Usuario.id) - vehiculo = Vehiculo.query.get(vehicle_id) - - if not vehiculo: - return jsonify({'error': 'Vehículo no encontrado'}), 404 - - favorito_existente = next( - (fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) - if favorito_existente: - return jsonify({'msg': 'El vehículo ya está en favoritos'}), 400 - - nuevo_favorito = VehiculoFavorito( - usuario_id=user.id, vehiculo_id=vehicle_id) - db.session.add(nuevo_favorito) - db.session.commit() - - return jsonify({'msg': 'Vehículo añadido a favoritos', 'vehiculo': vehiculo.nombre}), 201 - -# POSTs - -# POST Personajes - - -@app.route('/people', methods=['POST']) -def post_people(): - request_body = request.get_json() - - if not request_body: - return jsonify({"error": "Empty request body"}), 400 - - # Validate required fields and their types - if not isinstance(request_body.get("nombre"), str): - return jsonify({"error": "'nombre' must be a string"}), 400 - if not isinstance(request_body.get("especie"), str): - return jsonify({"error": "'especie' must be a string"}), 400 - if not isinstance(request_body.get("altura"), (int, float)): - return jsonify({"error": "'altura' must be a number"}), 400 - if not isinstance(request_body.get("peso"), (int, float)): - return jsonify({"error": "'peso' must be a number"}), 400 - if not isinstance(request_body.get("genero"), str): - return jsonify({"error": "'genero' must be a string"}), 400 - if not isinstance(request_body.get("imagen_url"), str): - return jsonify({"error": "'imagen_url' must be a string"}), 400 - if not isinstance(request_body.get("descripcion"), str): - return jsonify({"error": "'descripcion' must be a string"}), 400 - - new_personaje = Personaje( - nombre=request_body['nombre'], - especie=request_body['especie'], - altura=request_body['altura'], - peso=request_body['peso'], - genero=request_body['genero'], - imagen_url=request_body['imagen_url'], - descripcion=request_body['descripcion'] - ) - - db.session.add(new_personaje) - db.session.commit() - - return jsonify({"message": "Personaje created successfully"}), 201 - -# POST Planetas - - -@app.route('/planeta', methods=['POST']) -def post_planeta(): - request_body = request.get_json() - if not request_body: - return jsonify({"error": "Empty request body"}), 400 - - # Validate required fields and their types - if not isinstance(request_body.get("nombre"), str): - return jsonify({"error": "'nombre' must be a string"}), 400 - if not isinstance(request_body.get("diametro"), int): - return jsonify({"error": "'diametro' must be a string"}), 400 - if not isinstance(request_body.get("clima"), str): - return jsonify({"error": "'clima' must be a number"}), 400 - if not isinstance(request_body.get("poblacion"), int): - return jsonify({"error": "'poblacion' must be a number"}), 400 - if not isinstance(request_body.get("imagen_url"), str): - return jsonify({"error": "'imagen_url' must be a string"}), 400 - if not isinstance(request_body.get("descripcion"), str): - return jsonify({"error": "'descripcion' must be a string"}), 400 - - new_planeta = Planeta( - nombre=request_body['nombre'], - diametro=request_body['diametro'], - clima=request_body['clima'], - poblacion=request_body['poblacion'], - imagen_url=request_body['imagen_url'], - descripcion=request_body['descripcion'] - ) - - db.session.add(new_planeta) - db.session.commit() - - return jsonify({"message": "Planet created successfully"}), 201 - -# POST Vehiculo - - -@app.route('/vehiculo', methods=['POST']) -def post_vehiculo(): - request_body = request.get_json() - - if not request_body: - return jsonify({"error": "Empty request body"}), 400 - - # Validate required fields and their types - if not isinstance(request_body.get("nombre"), str): - return jsonify({"error": "'nombre' must be a string"}), 400 - if not isinstance(request_body.get("modelo"), str): - return jsonify({"error": "'modelo' must be a string"}), 400 - if not isinstance(request_body.get("longitud"), (int, float)): - return jsonify({"error": "'longitud' must be a number"}), 400 - if not isinstance(request_body.get("velocidad_maxima"), (int, float)): - return jsonify({"error": "'velocidad_maxima' must be a number"}), 400 - if not isinstance(request_body.get("tripulacion"), int): - return jsonify({"error": "'tripulacion' must be a string"}), 400 - if not isinstance(request_body.get("pasajeros"), int): - return jsonify({"error": "'pasajeros' must be a string"}), 400 - if not isinstance(request_body.get("image_url"), str): - return jsonify({"error": "'imagen_url' must be a string"}), 400 - if not isinstance(request_body.get("descripcion"), str): - return jsonify({"error": "'descripcion' must be a string"}), 400 - - new_vehiculo = Planeta( - nombre=request_body['nombre'], - modelo=request_body['modelo'], - longitud=request_body['longitud'], - velocidad_maxima=request_body['velocidad_maxima'], - tripulacion=request_body['tripulacion'], - pasajeros=request_body['pasajeros'], - imagen_url=request_body['imagen_url'], - descripcion=request_body['descripcion'] - ) - - db.session.add(new_vehiculo) - db.session.commit() - - return jsonify({"message": "Vehiculo created successfully"}), 201 - - -# PUTs def validar_tipo(valor, tipo_esperado, nombre_campo): if valor is not None and not isinstance(valor, tipo_esperado): raise ValueError( f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") -# PUT Personajes - - -@app.route('/people/', methods=['PUT']) -def update_personaje(people_id): - personaje = Personaje.query.get_or_404(people_id) - data = request.get_json() - - try: - if "nombre" in data: - validar_tipo(data["nombre"], str, "nombre") - personaje.nombre = data["nombre"] - if "especie" in data: - validar_tipo(data["especie"], str, "especie") - personaje.especie = data["especie"] - if "altura" in data: - validar_tipo(data["altura"], int, "altura") - personaje.altura = data["altura"] - if "peso" in data: - validar_tipo(data["peso"], int, "peso") - personaje.peso = data["peso"] - if "genero" in data: - validar_tipo(data["genero"], str, "genero") - personaje.genero = data["genero"] - if "descripcion" in data: - validar_tipo(data["descripcion"], str, "descripcion") - personaje.descripcion = data["descripcion"] - if "planeta_natal_id" in data: - validar_tipo(data["planeta_natal_id"], int, "planeta_natal_id") - personaje.planeta_natal_id = data["planeta_natal_id"] - if "vehiculo_id" in data: - validar_tipo(data["vehiculo_id"], int, "vehiculo_id") - personaje.vehiculo_id = data["vehiculo_id"] - - except ValueError as e: - return jsonify({"error": str(e)}), 400 - - db.session.commit() - return jsonify(personaje.serialize()), 200 - -# PUT Planetas - - -@app.route('/planets/', methods=['PUT']) -def update_planeta(planet_id): - planeta = Planeta.query.get_or_404(planet_id) - data = request.get_json() - - try: - if "nombre" in data: - validar_tipo(data["nombre"], str, "nombre") - planeta.nombre = data["nombre"] - if "diametro" in data: - validar_tipo(data["diametro"], int, "diametro") - planeta.diametro = data["diametro"] - if "clima" in data: - validar_tipo(data["clima"], str, "clima") - planeta.clima = data["clima"] - if "poblacion" in data: - validar_tipo(data["poblacion"], int, "poblacion") - planeta.poblacion = data["poblacion"] - if "descripcion" in data: - validar_tipo(data["descripcion"], str, "descripcion") - planeta.descripcion = data["descripcion"] - - except ValueError as e: - return jsonify({"error": str(e)}), 400 - - db.session.commit() - return jsonify(planeta.serialize()), 200 - -# PUT Vehiculos - - -@app.route('/vehicles/', methods=['PUT']) -def update_vehiculo(vehicle_id): - vehiculo = Vehiculo.query.get_or_404(vehicle_id) - data = request.get_json() - - try: - if "nombre" in data: - validar_tipo(data["nombre"], str, "nombre") - vehiculo.nombre = data["nombre"] - if "modelo" in data: - validar_tipo(data["modelo"], str, "modelo") - vehiculo.modelo = data["modelo"] - if "longitud" in data: - validar_tipo(data["longitud"], float, "longitud") - vehiculo.longitud = data["longitud"] - if "velocidad_maxima" in data: - validar_tipo(data["velocidad_maxima"], int, "velocidad_maxima") - vehiculo.velocidad_maxima = data["velocidad_maxima"] - if "tripulacion" in data: - validar_tipo(data["tripulacion"], int, "tripulacion") - vehiculo.tripulacion = data["tripulacion"] - if "pasajeros" in data: - validar_tipo(data["pasajeros"], int, "pasajeros") - vehiculo.pasajeros = data["pasajeros"] - if "descripcion" in data: - validar_tipo(data["descripcion"], str, "descripcion") - vehiculo.descripcion = data["descripcion"] - if "personaje_id" in data: - validar_tipo(data["personaje_id"], int, "personaje_id") - vehiculo.personaje_id = data["personaje_id"] - - except ValueError as e: - return jsonify({"error": str(e)}), 400 - - db.session.commit() - return jsonify(vehiculo.serialize()), 200 - # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': diff --git a/src/models/associations.py b/src/models/associations.py index 7cc1c712e..8c612db6c 100644 --- a/src/models/associations.py +++ b/src/models/associations.py @@ -3,13 +3,17 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from datetime import datetime + class PlanetaFavorito(db.Model): __tablename__ = "planetas_favoritos" id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - planeta_id: Mapped[int] = mapped_column(ForeignKey("planetas.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + usuario_id: Mapped[int] = mapped_column( + ForeignKey("usuarios.id"), nullable=False) + planeta_id: Mapped[int] = mapped_column( + ForeignKey("planetas.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column( + DateTime, default=func.now()) def serialize(self): return { @@ -21,16 +25,19 @@ def serialize(self): usuario = relationship("Usuario", back_populates="planetas_favoritos") planeta = relationship("Planeta", back_populates="favoritos") - + class PersonajeFavorito(db.Model): __tablename__ = "personajes_favoritos" id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - personaje_id: Mapped[int] = mapped_column(ForeignKey("personajes.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) - + usuario_id: Mapped[int] = mapped_column( + ForeignKey("usuarios.id"), nullable=False) + personaje_id: Mapped[int] = mapped_column( + ForeignKey("personajes.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column( + DateTime, default=func.now()) + def serialize(self): return { "id": self.id, @@ -47,9 +54,12 @@ class VehiculoFavorito(db.Model): __tablename__ = "vehiculos_favoritos" id: Mapped[int] = mapped_column(primary_key=True) - usuario_id: Mapped[int] = mapped_column(ForeignKey("usuarios.id"), nullable=False) - vehiculo_id: Mapped[int] = mapped_column(ForeignKey("vehiculos.id"), nullable=False) - fecha_agregado: Mapped[datetime] = mapped_column(DateTime, default=func.now()) + usuario_id: Mapped[int] = mapped_column( + ForeignKey("usuarios.id"), nullable=False) + vehiculo_id: Mapped[int] = mapped_column( + ForeignKey("vehiculos.id"), nullable=False) + fecha_agregado: Mapped[datetime] = mapped_column( + DateTime, default=func.now()) def serialize(self): return { @@ -58,6 +68,6 @@ def serialize(self): "vehiculo_id": self.planeta_id, "fecha_agregado": self.fecha_agregado } - + usuario = relationship("Usuario", back_populates="vehiculos_favoritos") - vehiculo = relationship("Vehiculo", back_populates="favoritos") \ No newline at end of file + vehiculo = relationship("Vehiculo", back_populates="favoritos") diff --git a/src/models/personaje.py b/src/models/personaje.py index be4390e30..8eb3162c9 100644 --- a/src/models/personaje.py +++ b/src/models/personaje.py @@ -1,9 +1,12 @@ from .database import db from sqlalchemy import String, Integer, ForeignKey, Text from sqlalchemy.orm import Mapped, mapped_column, relationship -from .vehiculo import Vehiculo -from .planeta import Planeta from .associations import PersonajeFavorito +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .vehiculo import Vehiculo + from .planeta import Planeta class Personaje(db.Model): diff --git a/src/models/planeta.py b/src/models/planeta.py index 80a1a4edd..791a47aec 100644 --- a/src/models/planeta.py +++ b/src/models/planeta.py @@ -1,8 +1,11 @@ from .database import db from sqlalchemy import String, Integer, Text from sqlalchemy.orm import Mapped, mapped_column, relationship -from .personaje import Personaje from .associations import PlanetaFavorito +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .personaje import Personaje class Planeta(db.Model): diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index f9a6ff462..10b5366f0 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -1,8 +1,11 @@ from .database import db from sqlalchemy import String, Integer, ForeignKey, Float, Text from sqlalchemy.orm import Mapped, mapped_column, relationship -from .personaje import Personaje from .associations import VehiculoFavorito +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .personaje import Personaje class Vehiculo(db.Model): diff --git a/src/routes/__init__.py b/src/routes/__init__.py new file mode 100644 index 000000000..ce31c8055 --- /dev/null +++ b/src/routes/__init__.py @@ -0,0 +1,5 @@ +from routes.personaje_routes import personaje_bp +from routes.planeta_routes import planeta_bp +from routes.vehiculo_routes import vehiculo_bp +from routes.usuario_routes import usuario_bp +from routes.favoritos_routes import favoritos_bp diff --git a/src/routes/favoritos_routes.py b/src/routes/favoritos_routes.py new file mode 100644 index 000000000..f5e113a01 --- /dev/null +++ b/src/routes/favoritos_routes.py @@ -0,0 +1,118 @@ +from flask import Blueprint, jsonify, request +from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito +from models.database import db +from app import validar_tipo + +favoritos_bp = Blueprint('favoritos', __name__, url_prefix='/favorite') + + +@favoritos_bp.route('/favorite/planet/', methods=['DELETE']) +def delete_fav_planet(planet_id): + user_id = Usuario.query.get(Usuario.id) + favorite = PlanetaFavorito.query.filter_by( + usuario_id=user_id, planeta_id=planet_id).first() + + if not favorite: + return jsonify({"message": "Favorite not found"}), 404 + + db.session.delete(favorite) + db.session.commit() + + updated_favorites = PlanetaFavorito.query.filter_by( + usuario_id=user_id).all() + return jsonify([fav.serialize() for fav in updated_favorites]), 200 + + +@favoritos_bp.route('/favorite/people/', methods=['DELETE']) +def delete_fav_person(people_id): + user_id = Usuario.query.get(Usuario.id) + favorite = PersonajeFavorito.query.filter_by( + usuario_id=user_id, personaje_id=people_id).first() + + if not favorite: + return jsonify({"message": "Person not found"}), 404 + + db.session.delete(favorite) + db.session.commit() + + updated_favorites = PersonajeFavorito.query.filter_by( + usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_favorites]), 200 + + +@favoritos_bp.route('/favorite/vehicle/', methods=['DELETE']) +def delete_fav_vehiculo(vehicle_id): + user_id = Usuario.query.get(Usuario.id) + favorite = VehiculoFavorito.query.filter_by( + usuario_id=user_id, vehiculo_id=vehicle_id).first() + + if not favorite: + return jsonify({"message": "Vehículo favorito no encontrado"}), 404 + + db.session.delete(favorite) + db.session.commit() + + updated_favorites = VehiculoFavorito.query.filter_by( + usuario_id=user_id).all() + return jsonify([p.serialize() for p in updated_favorites]), 200 + + +@favoritos_bp.route('/favorite/planet/', methods=['POST']) +def add_favorite_planet(planet_id): + user = Usuario.query.get(Usuario.id) + planeta = Planeta.query.get(planet_id) + + if not planeta: + return jsonify({'error': 'Planeta no encontrado'}), 404 + + favorito_existente = next( + (fav for fav in user.planetas_favoritos if fav.planeta_id == planet_id), None) + if favorito_existente: + return jsonify({'msg': 'El planeta ya está en favoritos'}), 400 + nuevo_favorito = PlanetaFavorito(usuario_id=user.id, planeta_id=planet_id) + db.session.add(nuevo_favorito) + db.session.commit() + + return jsonify({'msg': 'Planeta añadido a favoritos', 'planeta': planeta.nombre}), 201 + + +@favoritos_bp.route('/favorite/people/', methods=['POST']) +def add_favorite_personaje(people_id): + user = Usuario.query.get(Usuario.id) + personaje = Personaje.query.get(people_id) + + if not personaje: + return jsonify({'error': 'Personaje no encontrado'}), 404 + + favorito_existente = next( + (fav for fav in user.persoanjes_favoritos if fav.personaje_id == people_id), None) + if favorito_existente: + return jsonify({'msg': 'El personaje ya está en favoritos'}), 400 + + nuevo_favorito = PersonajeFavorito( + usuario_id=user.id, personaje_id=people_id) + db.session.add(nuevo_favorito) + db.session.commit() + + return jsonify({'msg': 'Personaje añadido a favoritos', 'personaje': personaje.nombre}), 201 + + +@favoritos_bp.route('/favorite/vehicle/', methods=['POST']) +def add_favorite_vehiculo(vehicle_id): + user = Usuario.query.get(Usuario.id) + vehiculo = Vehiculo.query.get(vehicle_id) + + if not vehiculo: + return jsonify({'error': 'Vehículo no encontrado'}), 404 + + favorito_existente = next( + (fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) + if favorito_existente: + return jsonify({'msg': 'El vehículo ya está en favoritos'}), 400 + + nuevo_favorito = VehiculoFavorito( + usuario_id=user.id, vehiculo_id=vehicle_id) + db.session.add(nuevo_favorito) + db.session.commit() + + return jsonify({'msg': 'Vehículo añadido a favoritos', 'vehiculo': vehiculo.nombre}), 201 diff --git a/src/routes/personaje_routes.py b/src/routes/personaje_routes.py new file mode 100644 index 000000000..304c3bd6b --- /dev/null +++ b/src/routes/personaje_routes.py @@ -0,0 +1,104 @@ +from flask import Blueprint, jsonify, request +from models.personaje import Personaje +from models.database import db +from app import validar_tipo + +personaje_bp = Blueprint('personajes', __name__, url_prefix='/people') + + +@personaje_bp.route('/', methods=['GET']) +def get_people(): + people = Personaje.query.all() + return jsonify([personaje.serialize() for personaje in people]), 200 + + +@personaje_bp.route('/', methods=['GET']) +def get_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + return jsonify(personaje.serialize()), 200 + + +@personaje_bp.route('/', methods=['DELETE']) +def delete_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + + db.session.delete(personaje) + db.session.commit() + return jsonify({"msg": f"Personaje {people_id} eliminado"}), 200 + + +@personaje_bp.route('/', methods=['POST']) +def post_people(): + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("especie"), str): + return jsonify({"error": "'especie' must be a string"}), 400 + if not isinstance(request_body.get("altura"), (int, float)): + return jsonify({"error": "'altura' must be a number"}), 400 + if not isinstance(request_body.get("peso"), (int, float)): + return jsonify({"error": "'peso' must be a number"}), 400 + if not isinstance(request_body.get("genero"), str): + return jsonify({"error": "'genero' must be a string"}), 400 + if not isinstance(request_body.get("imagen_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + + new_personaje = Personaje( + nombre=request_body['nombre'], + especie=request_body['especie'], + altura=request_body['altura'], + peso=request_body['peso'], + genero=request_body['genero'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_personaje) + db.session.commit() + + return jsonify({"message": "Personaje created successfully"}), 201 + + +@personaje_bp.route('/', methods=['PUT']) +def update_personaje(people_id): + personaje = Personaje.query.get_or_404(people_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + personaje.nombre = data["nombre"] + if "especie" in data: + validar_tipo(data["especie"], str, "especie") + personaje.especie = data["especie"] + if "altura" in data: + validar_tipo(data["altura"], int, "altura") + personaje.altura = data["altura"] + if "peso" in data: + validar_tipo(data["peso"], int, "peso") + personaje.peso = data["peso"] + if "genero" in data: + validar_tipo(data["genero"], str, "genero") + personaje.genero = data["genero"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + personaje.descripcion = data["descripcion"] + if "planeta_natal_id" in data: + validar_tipo(data["planeta_natal_id"], int, "planeta_natal_id") + personaje.planeta_natal_id = data["planeta_natal_id"] + if "vehiculo_id" in data: + validar_tipo(data["vehiculo_id"], int, "vehiculo_id") + personaje.vehiculo_id = data["vehiculo_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(personaje.serialize()), 200 diff --git a/src/routes/planeta_routes.py b/src/routes/planeta_routes.py new file mode 100644 index 000000000..0acc69287 --- /dev/null +++ b/src/routes/planeta_routes.py @@ -0,0 +1,91 @@ +from flask import Blueprint, jsonify, request +from models.planeta import Planeta +from models.database import db +from app import validar_tipo + +planeta_bp = Blueprint('planetas', __name__, url_prefix='/planets') + + +@planeta_bp.route('/', methods=['GET']) +def get_planetas(): + planetas = Planeta.query.all() + return jsonify([planeta.serialize() for planeta in planetas]), 200 + + +@planeta_bp.route('/', methods=['GET']) +def get_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) + return jsonify(planeta.serialize()), 200 + + +@planeta_bp.route('/', methods=['DELETE']) +def delete_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) + + db.session.delete(planeta) + db.session.commit() + return jsonify({"msg": f"Planeta {planet_id} eliminado"}), 200 + + +@planeta_bp.route('/', methods=['POST']) +def post_planeta(): + request_body = request.get_json() + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("diametro"), int): + return jsonify({"error": "'diametro' must be a string"}), 400 + if not isinstance(request_body.get("clima"), str): + return jsonify({"error": "'clima' must be a number"}), 400 + if not isinstance(request_body.get("poblacion"), int): + return jsonify({"error": "'poblacion' must be a number"}), 400 + if not isinstance(request_body.get("imagen_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + + new_planeta = Planeta( + nombre=request_body['nombre'], + diametro=request_body['diametro'], + clima=request_body['clima'], + poblacion=request_body['poblacion'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_planeta) + db.session.commit() + + return jsonify({"message": "Planet created successfully"}), 201 + + +@planeta_bp.route('/', methods=['PUT']) +def update_planeta(planet_id): + planeta = Planeta.query.get_or_404(planet_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + planeta.nombre = data["nombre"] + if "diametro" in data: + validar_tipo(data["diametro"], int, "diametro") + planeta.diametro = data["diametro"] + if "clima" in data: + validar_tipo(data["clima"], str, "clima") + planeta.clima = data["clima"] + if "poblacion" in data: + validar_tipo(data["poblacion"], int, "poblacion") + planeta.poblacion = data["poblacion"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + planeta.descripcion = data["descripcion"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(planeta.serialize()), 200 diff --git a/src/routes/usuario_routes.py b/src/routes/usuario_routes.py new file mode 100644 index 000000000..b16a156b8 --- /dev/null +++ b/src/routes/usuario_routes.py @@ -0,0 +1,26 @@ +from flask import Blueprint, jsonify, request +from models.usuario import Usuario +from models.database import db + +usuario_bp = Blueprint('usuario', __name__, url_prefix='/users') + + +@usuario_bp.route('/', methods=['GET']) +def get_users(): + response = Usuario.query.all() + return jsonify(response), 200 + + +@usuario_bp.route('/favorites', methods=['GET']) +def get_favorites(): + user_id = Usuario.query.get(Usuario.id) + + if not user_id: + return jsonify({"error": "User not found"}), 404 + + favorites = { + "planetas": [p.serialize() for p in Usuario.planetas_favoritos], + "personajes": [p.serialize() for p in Usuario.personajes_favoritos], + "vehiculos": [v.serialize() for v in Usuario.vehiculos_favoritos], + } + return jsonify(favorites), 200 diff --git a/src/routes/vehiculo_routes.py b/src/routes/vehiculo_routes.py new file mode 100644 index 000000000..7d6dbaf0c --- /dev/null +++ b/src/routes/vehiculo_routes.py @@ -0,0 +1,108 @@ +from flask import Blueprint, jsonify, request +from models.vehiculo import Vehiculo +from models.planeta import Planeta +from models.database import db +from app import validar_tipo + +vehiculo_bp = Blueprint('vehiculo', __name__, url_prefix='/vehicles') + + +@vehiculo_bp.route('/', methods=['GET']) +def get_vehiculos(): + vehiculos = Vehiculo.query.all() + return jsonify([vehiculo.serialize() for vehiculo in vehiculos]), 200 + + +@vehiculo_bp.route('/', methods=['GET']) +def get_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + return jsonify(vehiculo.serialize()), 200 + + +@vehiculo_bp.route('/', methods=['DELETE']) +def delete_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + + db.session.delete(vehiculo) + db.session.commit() + return jsonify({"msg": f"Vehículo {vehicle_id} eliminado"}), 200 + + +@vehiculo_bp.route('/', methods=['POST']) +def post_vehiculo(): + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("modelo"), str): + return jsonify({"error": "'modelo' must be a string"}), 400 + if not isinstance(request_body.get("longitud"), (int, float)): + return jsonify({"error": "'longitud' must be a number"}), 400 + if not isinstance(request_body.get("velocidad_maxima"), (int, float)): + return jsonify({"error": "'velocidad_maxima' must be a number"}), 400 + if not isinstance(request_body.get("tripulacion"), int): + return jsonify({"error": "'tripulacion' must be a string"}), 400 + if not isinstance(request_body.get("pasajeros"), int): + return jsonify({"error": "'pasajeros' must be a string"}), 400 + if not isinstance(request_body.get("image_url"), str): + return jsonify({"error": "'imagen_url' must be a string"}), 400 + if not isinstance(request_body.get("descripcion"), str): + return jsonify({"error": "'descripcion' must be a string"}), 400 + + new_vehiculo = Planeta( + nombre=request_body['nombre'], + modelo=request_body['modelo'], + longitud=request_body['longitud'], + velocidad_maxima=request_body['velocidad_maxima'], + tripulacion=request_body['tripulacion'], + pasajeros=request_body['pasajeros'], + imagen_url=request_body['imagen_url'], + descripcion=request_body['descripcion'] + ) + + db.session.add(new_vehiculo) + db.session.commit() + + return jsonify({"message": "Vehiculo created successfully"}), 201 + + +@vehiculo_bp.route('/', methods=['PUT']) +def update_vehiculo(vehicle_id): + vehiculo = Vehiculo.query.get_or_404(vehicle_id) + data = request.get_json() + + try: + if "nombre" in data: + validar_tipo(data["nombre"], str, "nombre") + vehiculo.nombre = data["nombre"] + if "modelo" in data: + validar_tipo(data["modelo"], str, "modelo") + vehiculo.modelo = data["modelo"] + if "longitud" in data: + validar_tipo(data["longitud"], float, "longitud") + vehiculo.longitud = data["longitud"] + if "velocidad_maxima" in data: + validar_tipo(data["velocidad_maxima"], int, "velocidad_maxima") + vehiculo.velocidad_maxima = data["velocidad_maxima"] + if "tripulacion" in data: + validar_tipo(data["tripulacion"], int, "tripulacion") + vehiculo.tripulacion = data["tripulacion"] + if "pasajeros" in data: + validar_tipo(data["pasajeros"], int, "pasajeros") + vehiculo.pasajeros = data["pasajeros"] + if "descripcion" in data: + validar_tipo(data["descripcion"], str, "descripcion") + vehiculo.descripcion = data["descripcion"] + if "personaje_id" in data: + validar_tipo(data["personaje_id"], int, "personaje_id") + vehiculo.personaje_id = data["personaje_id"] + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + + db.session.commit() + return jsonify(vehiculo.serialize()), 200 From ab5fbf1cc2f5481136eb93af45a665cc3213b337 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Fri, 9 May 2025 14:44:08 +0000 Subject: [PATCH 14/16] validar tipo --- src/app.py | 5 +---- src/routes/favoritos_routes.py | 1 - src/routes/personaje_routes.py | 6 +++++- src/routes/planeta_routes.py | 6 +++++- src/routes/vehiculo_routes.py | 6 +++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/app.py b/src/app.py index 112ec4a03..00d7194cc 100644 --- a/src/app.py +++ b/src/app.py @@ -56,10 +56,7 @@ def sitemap(): return generate_sitemap(app) -def validar_tipo(valor, tipo_esperado, nombre_campo): - if valor is not None and not isinstance(valor, tipo_esperado): - raise ValueError( - f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") + # this only runs if `$ python src/app.py` is executed diff --git a/src/routes/favoritos_routes.py b/src/routes/favoritos_routes.py index f5e113a01..ed53968ff 100644 --- a/src/routes/favoritos_routes.py +++ b/src/routes/favoritos_routes.py @@ -1,7 +1,6 @@ from flask import Blueprint, jsonify, request from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito from models.database import db -from app import validar_tipo favoritos_bp = Blueprint('favoritos', __name__, url_prefix='/favorite') diff --git a/src/routes/personaje_routes.py b/src/routes/personaje_routes.py index 304c3bd6b..64ab502a7 100644 --- a/src/routes/personaje_routes.py +++ b/src/routes/personaje_routes.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, request from models.personaje import Personaje from models.database import db -from app import validar_tipo + personaje_bp = Blueprint('personajes', __name__, url_prefix='/people') @@ -65,6 +65,10 @@ def post_people(): return jsonify({"message": "Personaje created successfully"}), 201 +def validar_tipo(valor, tipo_esperado, nombre_campo): + if valor is not None and not isinstance(valor, tipo_esperado): + raise ValueError( + f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") @personaje_bp.route('/', methods=['PUT']) def update_personaje(people_id): diff --git a/src/routes/planeta_routes.py b/src/routes/planeta_routes.py index 0acc69287..3c00d6621 100644 --- a/src/routes/planeta_routes.py +++ b/src/routes/planeta_routes.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, request from models.planeta import Planeta from models.database import db -from app import validar_tipo + planeta_bp = Blueprint('planetas', __name__, url_prefix='/planets') @@ -61,6 +61,10 @@ def post_planeta(): return jsonify({"message": "Planet created successfully"}), 201 +def validar_tipo(valor, tipo_esperado, nombre_campo): + if valor is not None and not isinstance(valor, tipo_esperado): + raise ValueError( + f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") @planeta_bp.route('/', methods=['PUT']) def update_planeta(planet_id): diff --git a/src/routes/vehiculo_routes.py b/src/routes/vehiculo_routes.py index 7d6dbaf0c..bbe5f8113 100644 --- a/src/routes/vehiculo_routes.py +++ b/src/routes/vehiculo_routes.py @@ -2,7 +2,7 @@ from models.vehiculo import Vehiculo from models.planeta import Planeta from models.database import db -from app import validar_tipo + vehiculo_bp = Blueprint('vehiculo', __name__, url_prefix='/vehicles') @@ -69,6 +69,10 @@ def post_vehiculo(): return jsonify({"message": "Vehiculo created successfully"}), 201 +def validar_tipo(valor, tipo_esperado, nombre_campo): + if valor is not None and not isinstance(valor, tipo_esperado): + raise ValueError( + f"Campo '{nombre_campo}' debe ser de tipo {tipo_esperado.__name__}") @vehiculo_bp.route('/', methods=['PUT']) def update_vehiculo(vehicle_id): From ff7c711950fa8fd8315be5cf1e8fb528c5ec447f Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Mon, 12 May 2025 11:13:10 +0000 Subject: [PATCH 15/16] cambios --- src/app.py | 5 ----- src/routes/favoritos_routes.py | 12 ++++++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app.py b/src/app.py index 00d7194cc..296d312f9 100644 --- a/src/app.py +++ b/src/app.py @@ -43,22 +43,17 @@ # Manejo global de errores personalizados - @app.errorhandler(APIException) def handle_invalid_usage(error): return jsonify(error.to_dict()), error.status_code # Genera un mapa visual de todas las rutas - @app.route('/') def sitemap(): return generate_sitemap(app) - - - # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': PORT = int(os.environ.get('PORT', 3000)) diff --git a/src/routes/favoritos_routes.py b/src/routes/favoritos_routes.py index ed53968ff..37d571f1b 100644 --- a/src/routes/favoritos_routes.py +++ b/src/routes/favoritos_routes.py @@ -5,7 +5,7 @@ favoritos_bp = Blueprint('favoritos', __name__, url_prefix='/favorite') -@favoritos_bp.route('/favorite/planet/', methods=['DELETE']) +@favoritos_bp.route('/planet/', methods=['DELETE']) def delete_fav_planet(planet_id): user_id = Usuario.query.get(Usuario.id) favorite = PlanetaFavorito.query.filter_by( @@ -22,7 +22,7 @@ def delete_fav_planet(planet_id): return jsonify([fav.serialize() for fav in updated_favorites]), 200 -@favoritos_bp.route('/favorite/people/', methods=['DELETE']) +@favoritos_bp.route('/people/', methods=['DELETE']) def delete_fav_person(people_id): user_id = Usuario.query.get(Usuario.id) favorite = PersonajeFavorito.query.filter_by( @@ -39,7 +39,7 @@ def delete_fav_person(people_id): return jsonify([p.serialize() for p in updated_favorites]), 200 -@favoritos_bp.route('/favorite/vehicle/', methods=['DELETE']) +@favoritos_bp.route('/vehicle/', methods=['DELETE']) def delete_fav_vehiculo(vehicle_id): user_id = Usuario.query.get(Usuario.id) favorite = VehiculoFavorito.query.filter_by( @@ -56,7 +56,7 @@ def delete_fav_vehiculo(vehicle_id): return jsonify([p.serialize() for p in updated_favorites]), 200 -@favoritos_bp.route('/favorite/planet/', methods=['POST']) +@favoritos_bp.route('/planet/', methods=['POST']) def add_favorite_planet(planet_id): user = Usuario.query.get(Usuario.id) planeta = Planeta.query.get(planet_id) @@ -75,7 +75,7 @@ def add_favorite_planet(planet_id): return jsonify({'msg': 'Planeta añadido a favoritos', 'planeta': planeta.nombre}), 201 -@favoritos_bp.route('/favorite/people/', methods=['POST']) +@favoritos_bp.route('/people/', methods=['POST']) def add_favorite_personaje(people_id): user = Usuario.query.get(Usuario.id) personaje = Personaje.query.get(people_id) @@ -96,7 +96,7 @@ def add_favorite_personaje(people_id): return jsonify({'msg': 'Personaje añadido a favoritos', 'personaje': personaje.nombre}), 201 -@favoritos_bp.route('/favorite/vehicle/', methods=['POST']) +@favoritos_bp.route('/vehicle/', methods=['POST']) def add_favorite_vehiculo(vehicle_id): user = Usuario.query.get(Usuario.id) vehiculo = Vehiculo.query.get(vehicle_id) From 426b4907d9ada13c207897010ce83d65258282e8 Mon Sep 17 00:00:00 2001 From: Adrianmrc94 Date: Mon, 12 May 2025 13:41:49 +0000 Subject: [PATCH 16/16] cambios finales todo funciona --- src/models/usuario.py | 8 ++--- src/models/vehiculo.py | 1 - src/routes/favoritos_routes.py | 32 ++++++++++---------- src/routes/usuario_routes.py | 54 ++++++++++++++++++++++++++++------ src/routes/vehiculo_routes.py | 4 +-- 5 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/models/usuario.py b/src/models/usuario.py index 436a6c7b5..b07231eea 100644 --- a/src/models/usuario.py +++ b/src/models/usuario.py @@ -3,7 +3,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from datetime import datetime from .associations import PersonajeFavorito, PlanetaFavorito, VehiculoFavorito - +from typing import List class Usuario(db.Model): __tablename__ = "usuarios" @@ -21,11 +21,11 @@ class Usuario(db.Model): activo: Mapped[bool] = mapped_column(Boolean(), default=True) # Relaciones - planetas_favoritos: Mapped["PlanetaFavorito"] = relationship( + planetas_favoritos: Mapped[List["PlanetaFavorito"]] = relationship( "PlanetaFavorito", back_populates="usuario") - personajes_favoritos: Mapped["PersonajeFavorito"] = relationship( + personajes_favoritos: Mapped[List["PersonajeFavorito"]] = relationship( "PersonajeFavorito", back_populates="usuario") - vehiculos_favoritos: Mapped["VehiculoFavorito"] = relationship( + vehiculos_favoritos: Mapped[List["VehiculoFavorito"]] = relationship( "VehiculoFavorito", back_populates="usuario") def serialize(self): diff --git a/src/models/vehiculo.py b/src/models/vehiculo.py index 10b5366f0..4a2c39e2d 100644 --- a/src/models/vehiculo.py +++ b/src/models/vehiculo.py @@ -27,7 +27,6 @@ class Vehiculo(db.Model): "Personaje", back_populates="vehiculo", uselist=False, - foreign_keys="Personaje.vehiculo_id", remote_side="Personaje.vehiculo_id", primaryjoin="Vehiculo.id == Personaje.vehiculo_id" ) diff --git a/src/routes/favoritos_routes.py b/src/routes/favoritos_routes.py index 37d571f1b..293e8648c 100644 --- a/src/routes/favoritos_routes.py +++ b/src/routes/favoritos_routes.py @@ -2,12 +2,12 @@ from models import db, Personaje, Planeta, Vehiculo, Usuario, PersonajeFavorito, PlanetaFavorito, VehiculoFavorito from models.database import db -favoritos_bp = Blueprint('favoritos', __name__, url_prefix='/favorite') +favoritos_bp = Blueprint('favoritos', __name__, url_prefix='/users//favorites') @favoritos_bp.route('/planet/', methods=['DELETE']) -def delete_fav_planet(planet_id): - user_id = Usuario.query.get(Usuario.id) +def delete_fav_planet(planet_id, user_id): + favorite = PlanetaFavorito.query.filter_by( usuario_id=user_id, planeta_id=planet_id).first() @@ -23,8 +23,8 @@ def delete_fav_planet(planet_id): @favoritos_bp.route('/people/', methods=['DELETE']) -def delete_fav_person(people_id): - user_id = Usuario.query.get(Usuario.id) +def delete_fav_person(people_id, user_id): + favorite = PersonajeFavorito.query.filter_by( usuario_id=user_id, personaje_id=people_id).first() @@ -40,8 +40,8 @@ def delete_fav_person(people_id): @favoritos_bp.route('/vehicle/', methods=['DELETE']) -def delete_fav_vehiculo(vehicle_id): - user_id = Usuario.query.get(Usuario.id) +def delete_fav_vehiculo(vehicle_id, user_id): + favorite = VehiculoFavorito.query.filter_by( usuario_id=user_id, vehiculo_id=vehicle_id).first() @@ -57,15 +57,15 @@ def delete_fav_vehiculo(vehicle_id): @favoritos_bp.route('/planet/', methods=['POST']) -def add_favorite_planet(planet_id): - user = Usuario.query.get(Usuario.id) +def add_favorite_planet(planet_id, user_id): + user = Usuario.query.get(user_id) planeta = Planeta.query.get(planet_id) if not planeta: return jsonify({'error': 'Planeta no encontrado'}), 404 favorito_existente = next( - (fav for fav in user.planetas_favoritos if fav.planeta_id == planet_id), None) + (fav for fav in (user.planetas_favoritos or []) if fav.planeta_id == planet_id), None) if favorito_existente: return jsonify({'msg': 'El planeta ya está en favoritos'}), 400 nuevo_favorito = PlanetaFavorito(usuario_id=user.id, planeta_id=planet_id) @@ -76,15 +76,15 @@ def add_favorite_planet(planet_id): @favoritos_bp.route('/people/', methods=['POST']) -def add_favorite_personaje(people_id): - user = Usuario.query.get(Usuario.id) +def add_favorite_personaje(user_id, people_id): + user = Usuario.query.get(user_id) personaje = Personaje.query.get(people_id) if not personaje: return jsonify({'error': 'Personaje no encontrado'}), 404 favorito_existente = next( - (fav for fav in user.persoanjes_favoritos if fav.personaje_id == people_id), None) + (fav for fav in (user.personajes_favoritos or []) if fav.personaje_id == people_id), None) if favorito_existente: return jsonify({'msg': 'El personaje ya está en favoritos'}), 400 @@ -97,15 +97,15 @@ def add_favorite_personaje(people_id): @favoritos_bp.route('/vehicle/', methods=['POST']) -def add_favorite_vehiculo(vehicle_id): - user = Usuario.query.get(Usuario.id) +def add_favorite_vehiculo(vehicle_id, user_id): + user = Usuario.query.get(user_id) vehiculo = Vehiculo.query.get(vehicle_id) if not vehiculo: return jsonify({'error': 'Vehículo no encontrado'}), 404 favorito_existente = next( - (fav for fav in user.vehiculos_favoritos if fav.vehiculo_id == vehicle_id), None) + (fav for fav in (user.vehiculos_favoritos or []) if fav.vehiculo_id == vehicle_id), None) if favorito_existente: return jsonify({'msg': 'El vehículo ya está en favoritos'}), 400 diff --git a/src/routes/usuario_routes.py b/src/routes/usuario_routes.py index b16a156b8..e4ffae6d3 100644 --- a/src/routes/usuario_routes.py +++ b/src/routes/usuario_routes.py @@ -4,23 +4,59 @@ usuario_bp = Blueprint('usuario', __name__, url_prefix='/users') - @usuario_bp.route('/', methods=['GET']) def get_users(): response = Usuario.query.all() - return jsonify(response), 200 + return jsonify([u.serialize() for u in response]), 200 +@usuario_bp.route('//favorites', methods=['GET']) +def get_favorites(user_id): -@usuario_bp.route('/favorites', methods=['GET']) -def get_favorites(): - user_id = Usuario.query.get(Usuario.id) + user = Usuario.query.get(user_id) - if not user_id: + if not user: return jsonify({"error": "User not found"}), 404 + + planetas_fav = [fav.planeta.serialize() for fav in user.planetas_favoritos] if user.planetas_favoritos else [] + personajes_fav = [fav.personaje.serialize() for fav in user.personajes_favoritos] if user.personajes_favoritos else [] + vehiculos_fav = [fav.vehiculo.serialize() for fav in user.vehiculos_favoritos] if user.vehiculos_favoritos else [] favorites = { - "planetas": [p.serialize() for p in Usuario.planetas_favoritos], - "personajes": [p.serialize() for p in Usuario.personajes_favoritos], - "vehiculos": [v.serialize() for v in Usuario.vehiculos_favoritos], + "planetas": planetas_fav, + "personajes": personajes_fav, + "vehiculos": vehiculos_fav } + + if not any(favorites.values()): + return jsonify({"msg": "El usuario no tiene favoritos aún."}), 200 + return jsonify(favorites), 200 + +@usuario_bp.route('/',methods=['POST']) +def post_user(): + user_list = Usuario.query.all() + user_id = len(user_list)+1 + + request_body = request.get_json() + + if not request_body: + return jsonify({"error": "Empty request body"}), 400 + # Validate required fields and their types + if not isinstance(request_body.get("nombre"), str): + return jsonify({"error": "'nombre' must be a string"}), 400 + if not isinstance(request_body.get("apellido"), str): + return jsonify({"error": "'apellido' must be a string"}), 400 + if not isinstance(request_body.get("email"), (str)): + return jsonify({"error": "'email' must be a string"}), 400 + if not isinstance(request_body.get("password"), (str)): + return jsonify({"error": "'password' must be a string"}), 400 + + new_user = Usuario( + nombre=request_body['nombre'], + apellido=request_body['apellido'], + email=request_body['email'], + password=request_body['password'], + ) + db.session.add(new_user) + db.session.commit() + return jsonify({"message": "User created successfully"}), 201 \ No newline at end of file diff --git a/src/routes/vehiculo_routes.py b/src/routes/vehiculo_routes.py index bbe5f8113..6fa5b09ee 100644 --- a/src/routes/vehiculo_routes.py +++ b/src/routes/vehiculo_routes.py @@ -48,12 +48,12 @@ def post_vehiculo(): return jsonify({"error": "'tripulacion' must be a string"}), 400 if not isinstance(request_body.get("pasajeros"), int): return jsonify({"error": "'pasajeros' must be a string"}), 400 - if not isinstance(request_body.get("image_url"), str): + if not isinstance(request_body.get("imagen_url"), str): return jsonify({"error": "'imagen_url' must be a string"}), 400 if not isinstance(request_body.get("descripcion"), str): return jsonify({"error": "'descripcion' must be a string"}), 400 - new_vehiculo = Planeta( + new_vehiculo = Vehiculo( nombre=request_body['nombre'], modelo=request_body['modelo'], longitud=request_body['longitud'],