-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sharks - Sana Pournaghshband and Camilla #20
base: main
Are you sure you want to change the base?
Changes from all commits
eb67210
eaa5fd0
bd072e3
7ab1975
8e32e29
101aa0e
6c98125
651bb49
0598417
fde39b3
4d63fbc
c98e261
7cf65b3
a6aeaaf
e1c2d9d
c419281
20d75b6
be80290
8e20245
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
web: gunicorn 'app:create_app()' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,36 @@ | ||
from flask import Flask | ||
from flask_sqlalchemy import SQLAlchemy | ||
from flask_migrate import Migrate | ||
from dotenv import load_dotenv | ||
import os | ||
|
||
|
||
db = SQLAlchemy() | ||
migrate = Migrate() | ||
load_dotenv() | ||
|
||
def create_app(test_config=None): | ||
app = Flask(__name__) | ||
from app.models.planet import Planet | ||
from app.models.moon import Moon | ||
|
||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | ||
|
||
# if not test_config: | ||
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get("SQLALCHEMY_DATABASE_URI") | ||
# else: | ||
# app.config["TESTING"] = True | ||
# app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_TEST_DATABASE_URI") | ||
|
||
db.init_app(app) | ||
migrate.init_app(app, db) | ||
|
||
|
||
from .routes.planet_routes import planet_bp | ||
from .routes.moon_routes import moon_bp | ||
|
||
app.register_blueprint(planet_bp) | ||
app.register_blueprint(moon_bp) | ||
|
||
|
||
return app | ||
return app |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from app import db | ||
|
||
class Moon(db.Model): | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
name = db.Column(db.String, nullable=False) | ||
planet_id = db.Column(db.Integer, db.ForeignKey('planet.id')) | ||
planet = db.relationship("Planet", back_populates="planet_moons") | ||
|
||
def to_json(self): | ||
return { | ||
"id": self.id, | ||
"name": self.name, | ||
} | ||
|
||
def update(self, request_body): | ||
self.name = request_body["name"] | ||
self.planet_id = request_body["planet_id"] | ||
|
||
@classmethod | ||
def create(cls, request_body): | ||
return cls( | ||
name=request_body["name"], | ||
planet_id=request_body["planet_id"], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from app import db | ||
|
||
''' | ||
Defined a Planet model with the attributes id, title, | ||
and description, and moons. Created instance method, update, to update our model. | ||
Class method, create, to create a new instance of planet. | ||
''' | ||
|
||
class Planet(db.Model): | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
title = db.Column(db.String, nullable=False) | ||
description = db.Column(db.String) | ||
moons = db.Column(db.Boolean, nullable=False) | ||
planet_moons = db.relationship("Moon", back_populates="planet") | ||
|
||
|
||
def to_json(self): | ||
return { | ||
"id": self.id, | ||
"title": self.title, | ||
"description": self.description, | ||
"moons": self.moons | ||
} | ||
|
||
def update(self, request_body): | ||
self.title = request_body["title"] | ||
self.description = request_body["description"] | ||
self.moons = request_body["moons"] | ||
|
||
@classmethod | ||
def create(cls,request_body): | ||
new_planet = cls( | ||
title=request_body["title"], | ||
description=request_body["description"], | ||
moons = request_body["moons"] | ||
) | ||
return new_planet | ||
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from flask import Blueprint, jsonify, make_response, abort, request | ||
from app.models.moon import Moon | ||
|
||
def validate_moon(id): | ||
try: | ||
id = int(id) | ||
except: | ||
return abort(make_response({"message": f"moon {id} is invalid"}, 400)) | ||
moon= Moon.query.get(id) | ||
|
||
if not moon: | ||
return abort(make_response({"message": f"moon {id} is not found"}, 404)) | ||
|
||
return moon |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
from app import db | ||
from app.models.planet import Planet | ||
from flask import Blueprint, jsonify, make_response, request , abort | ||
from app.models.moon import Moon | ||
from .moon_helpers import validate_moon | ||
|
||
|
||
moon_bp = Blueprint("moon_bp", __name__, url_prefix="/moons") | ||
|
||
|
||
# CREATE Moon | ||
@moon_bp.route("", methods=["POST"]) | ||
def create_moon(): | ||
request_body = request.get_json() | ||
|
||
new_moon = Moon.create(request_body) | ||
|
||
db.session.add(new_moon) | ||
db.session.commit() | ||
|
||
return make_response(f"Moon {new_moon.name} has been successfully created!",201) | ||
|
||
# GET ALL Moons | ||
@moon_bp.route("", methods=["GET"]) | ||
def read_all_moons(): | ||
name_query = request.args.get("name") | ||
|
||
if name_query: | ||
moons = Moon.query.filter_by(name =name_query) | ||
else: | ||
moons = Moon.query.all() | ||
|
||
moons_response = [] | ||
for moon in moons: | ||
moons_response.append(moon.to_json()) | ||
|
||
return jsonify(moons_response), 200 | ||
|
||
# GET one Moon | ||
@moon_bp.route("/<id>", methods = ["GET"]) | ||
def read_one_moon(id): | ||
moon = validate_moon(id) | ||
return jsonify(moon.to_json()), 200 | ||
|
||
@moon_bp.route("/<id>", methods = ["PUT"]) | ||
def update_one_moon(id): | ||
moon = validate_moon(id) | ||
request_body = request.get_json() | ||
|
||
moon.update(request_body) | ||
|
||
db.session.commit() | ||
return make_response(f"Moon #{moon.id} successfully updated"), 200 | ||
|
||
@moon_bp.route("/<id>", methods = ["DELETE"]) | ||
def delete_one_moon(id): | ||
moon = validate_moon(id) | ||
db.session.delete(moon) | ||
db.session.commit() | ||
|
||
return make_response(f"Moon #{moon.id} successfully deleted"), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from flask import Blueprint, jsonify, make_response, abort, request | ||
from app.models.planet import Planet | ||
|
||
def validate_planet(id): | ||
try: | ||
id = int(id) | ||
except: | ||
return abort(make_response({"message": f"planet {id} is invalid"}, 400)) | ||
planet= Planet.query.get(id) | ||
|
||
if not planet: | ||
return abort(make_response({"message": f"planet {id} is not found"}, 404)) | ||
|
||
return planet |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
from flask import Blueprint, jsonify, make_response, abort, request | ||
from app.models.planet import Planet | ||
from app import db | ||
from .planet_helpers import validate_planet | ||
|
||
|
||
|
||
planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") | ||
|
||
#Create planet | ||
@planet_bp.route("", methods=["POST"]) | ||
def create_planet(): | ||
request_body = request.get_json() | ||
new_planet = Planet.create(request_body) | ||
|
||
db.session.add(new_planet) | ||
db.session.commit() | ||
|
||
return make_response(f"Planet {new_planet.title} successfully created", 201) | ||
|
||
#Get all planets | ||
@planet_bp.route("", methods=["GET"]) | ||
def read_all_planets(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice job naming the method to describe what this route does. Since you put your routes in planet_routs.py in routes directory, you can even call this read_all() since we know that all the routes in planet_routes.py are related to the Planet class. |
||
title_query = request.args.get("title") | ||
if title_query: | ||
planets = Planet.query.filter_by(title=title_query) | ||
else: | ||
planets = Planet.query.all() | ||
|
||
planets_response = [] | ||
|
||
for planet in planets: | ||
planets_response.append( | ||
{ | ||
"id": planet.id, | ||
"title": planet.title, | ||
"description": planet.description, | ||
"moons": planet.moons | ||
} | ||
) | ||
|
||
return jsonify(planets_response) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding 200 status code adds clarity even though it happens by default return jsonify(planets_response), 200 |
||
|
||
''' | ||
Created the following endpoint(s). This API can handle requests such as the following: | ||
- to get one existing planet, so that I can see the id, name, description, and other data of the planet. | ||
- such that trying to get one non-existing planet responds | ||
with get a 404 response, so that I know the planet resource was not found. | ||
- such that trying to get one planet with an invalid planet_id responds | ||
with get a 400 response, so that I know the planet_id was invalid. | ||
''' | ||
|
||
#Get one planet | ||
@planet_bp.route("/<id>", methods=["GET"]) | ||
def read_one_planet(id): | ||
planet = validate_planet(id) | ||
|
||
return jsonify(planet.to_json(), 200) | ||
|
||
#Update one planet | ||
@planet_bp.route("/<id>", methods = ["PUT"]) | ||
def update_one_planet(id): | ||
planet = validate_planet(id) | ||
request_body = request.get_json() | ||
planet.update(request_body) | ||
db.session.commit() | ||
|
||
return make_response(jsonify(f"Planet # {planet.id} successfully updated"), 200) | ||
|
||
#Delete one planet | ||
@planet_bp.route("/<id>", methods = ["DELETE"]) | ||
def delete_one_planet(id): | ||
planet = validate_planet(id) | ||
db.session.delete(planet) | ||
db.session.commit() | ||
|
||
return make_response(f"Planet # {planet.id} successfully deleted"), 200 | ||
|
||
|
||
# @planet_bp.route("/<id>/cats", methods=["POST"]) | ||
# def create_moon(id): | ||
# planet = validate_planet(id) | ||
|
||
# request_body = request.get_json() | ||
# new_moon = Moon.create(request_body) | ||
|
||
# new_moon.planet = planet | ||
# db.session.add(new_moon) | ||
# db.session.commit() | ||
|
||
# return make_response(jsonify(f"Moon {new_moon.name} orbiting around \ | ||
# {new_moon.planet.title} successfully created"), 201) | ||
|
||
|
||
@planet_bp.route("/<id>/moons", methods=["GET"]) | ||
def read_moons(id): | ||
planet = validate_planet(id) | ||
|
||
moons_response = [] | ||
for moon in planet.planet_moons: | ||
moons_response.append(moon.to_json()) | ||
|
||
return jsonify(moons_response), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import pytest | ||
from app import create_app | ||
from app import db | ||
from flask.signals import request_finished | ||
from app.models.planet import Planet | ||
|
||
|
||
@pytest.fixture | ||
def app(): | ||
app = create_app({"TESTING": True}) | ||
|
||
@request_finished.connect_via(app) | ||
def expire_session(sender, response, **extra): | ||
db.session.remove() | ||
|
||
with app.app_context(): | ||
db.create_all() | ||
yield app | ||
|
||
with app.app_context(): | ||
db.drop_all() | ||
|
||
@pytest.fixture | ||
def client(app): | ||
return app.test_client() | ||
|
||
@pytest.fixture | ||
def two_saved_planets(app): | ||
mercury_planet = Planet(title="Mercury", description="Best planet, grey", moons=False) | ||
venus_planet = Planet(title="Venus", description="Hottest planet", moons=False) | ||
|
||
db.session.add_all([mercury_planet, venus_planet]) | ||
db.session.commit() | ||
|
||
@pytest.fixture | ||
def all_planets(app): | ||
mercury_planet = Planet(title="Mercury", description="Best planet, grey", moons=False) | ||
venus_planet = Planet(title="Venus", description="Hottest planet", moons=False) | ||
earth_planet = Planet(title="Earth", description="Our planet", moons=True) | ||
mars_planet = Planet(title="Mars", description="There's a Rover", moons=True) | ||
jupiter_planet = Planet(title="Jupiter", description="Big", moons=True) | ||
saturn_planet = Planet(title="Saturn", description="Has a ring", moons=True) | ||
uranus_planet = Planet(title="Uranus", description="Far away", moons=True) | ||
neptune_planet = Planet(title="Neptune", description="So lonely", moons=True) | ||
|
||
db.session.add_all([mercury_planet, venus_planet, earth_planet, mars_planet, jupiter_planet, saturn_planet, uranus_planet, neptune_planet]) | ||
db.session.commit() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work pulling these two methods into the model Planet to keep your routes short and sweet.
This comment applies to your create() and update() methods:
How would you add in validation to make sure that all the required fields are sent in the request to your server?
For example, if someone sent a POST request but left off moons, then line 33 would throw KeyError that it couldn't find moons.
it would be nice to handle the error and return a message so that the client knows their request was invalid and they need to include moons. Something to think about for Task List.