Skip to content
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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn 'app:create_app()'
31 changes: 30 additions & 1 deletion app/__init__.py
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
Empty file added app/models/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions app/models/moon.py
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"],
)
37 changes: 37 additions & 0 deletions app/models/planet.py
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
Comment on lines +25 to +37

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.

2 changes: 0 additions & 2 deletions app/routes.py

This file was deleted.

Empty file added app/routes/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions app/routes/moon_helpers.py
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
61 changes: 61 additions & 0 deletions app/routes/moon_routes.py
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
14 changes: 14 additions & 0 deletions app/routes/planet_helpers.py
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
103 changes: 103 additions & 0 deletions app/routes/planet_routes.py
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():

Choose a reason for hiding this comment

The 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)

Choose a reason for hiding this comment

The 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
Empty file added app/tests/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions app/tests/conftest.py
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()
Loading