-
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 - Jande R. and Lindsey S. #7
base: main
Are you sure you want to change the base?
Conversation
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.
Part 1 looks great 👍
app/__init__.py
Outdated
|
||
from .routes import planets_bp | ||
app.register_blueprint(planets_bp) | ||
|
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.
👍 Looks good! We can also move this file into a routes folder which would change the path of this import. Creating a routes folder makes sense if we have different routes for specific objects for example planet_routes
, moon_routes
, galaxy_routes
, etc.
app/routes.py
Outdated
class Planet(): | ||
def __init__(self, id, name, description, circumference, length_of_year): | ||
self.id = id | ||
self.name = name | ||
self.description = description | ||
self.circumference = circumference | ||
self.length_of_year = length_of_year | ||
|
||
def to_json(self): | ||
return { | ||
"id" : self.id, | ||
"name" : self.name, | ||
"description" : self.description, | ||
"circumference" : self.circumference, | ||
"length_of_year" : self.length_of_year | ||
} |
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.
👍 LGTM (looks good to me)
app/routes.py
Outdated
Planet(6, "Saturn", "sun's bae with all 7 rings", 235185, 10620), | ||
Planet(7, "Uranus", "can only be seen with a telescope", 99739, 30240), | ||
Planet(8, "Neptune", "it is an intense blue color", 96645, 59400), | ||
Planet(9, "Pluto", "no dwarf in my book", 7144, 88920) |
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.
Yes! Pluto is a planet 💯
app/routes.py
Outdated
def get_all_planets(): | ||
planets_response = [] | ||
for planet in planets: | ||
planets_response.append({ | ||
"id": planet.id, | ||
"name": planet.name, | ||
"description": planet.description, | ||
"circumference": planet.circumference, | ||
"length of year": planet.length_of_year | ||
}) | ||
return jsonify(planets_response) |
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.
This is where we can use our handy dandy to_json()
helper method in the Planet class. And we should also return a 200 status code.
def get_all_planets():
planets_response = []
for planet in planets:
planets_response.append(planet.to_json())
return jsonify(planets_response), 200
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.
We can also use list comprehension to build planets_response
.
app/routes.py
Outdated
def get_one_planet(id): | ||
try: | ||
id = int(id) | ||
except: | ||
return abort(make_response({"message":f"Planet {id} is invalid."}, 400)) | ||
|
||
for planet in planets: | ||
if planet.id == id: | ||
return jsonify(planet.to_json()) | ||
return abort(make_response({"message":f"Planet {id} not found."}, 404)) |
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 use of a try/except. The validation for bad id's and for id's with no record looks good. We can also consider moving the validation portion of this function into a helper method.
app/routes.py
Outdated
|
||
for planet in planets: | ||
if planet.id == id: | ||
return jsonify(planet.to_json()) |
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.
Don't forget the status code!
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.
Jande and Lindsey! Great work in finishing solar system. I have provided comments on small ways y'all can improve the API.
🦈 ✨
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__) | ||
|
||
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 app.models.planet import Planet | ||
|
||
from .routes import planets_bp | ||
app.register_blueprint(planets_bp) | ||
|
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.
👍 Good work setting up the Flask app!
from flask import abort, make_response | ||
from .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: | ||
abort(make_response({"message": f"planet {id} not found"}, 404)) | ||
|
||
return planet |
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.
Helpers look great! 👍 We may want to consider making the validation code as generic as possible to process any class and id or renaming this file to helper_planet_routes
or planet_routes_helper
. Either would be helpful if we expand this project to contain more routes for moons
, suns
, etc.
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.
A more generic validation helper method would look like:
from flask import make_response, abort
def validate_object(cls, id):
try:
id = int(id)
except:
return abort(make_response({"message": f"{cls} {id} is invalid"}, 400))
obj = cls.query.get(id)
if not obj:
abort(make_response({"message": f"{cls} {id} not found"},404))
return obj
class Planet(db.Model): | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
name = db.Column(db.String) | ||
description = db.Column(db.String) | ||
circumference = db.Column(db.Integer) | ||
length_of_year= db.Column(db.Integer) |
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.
Should we be able to make planets with no name? To prevent our API from creating a planet with a null
name, we can set that column to nullable = False
.
class Planet(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String, nullable=False)
description = db.Column(db.String)
circumference = db.Column(db.Integer)
length_of_year= db.Column(db.Integer)
def to_json(self): | ||
return { | ||
"id" : self.id, | ||
"name" : self.name, | ||
"description" : self.description, | ||
"circumference" : self.circumference, | ||
"length_of_year" : self.length_of_year | ||
} | ||
|
||
#update | ||
def update(self, request_body): | ||
self.name = request_body["name"] | ||
self.description = request_body["description"] | ||
self.circumference = request_body["circumference"] | ||
self.length_of_year = request_body["length_of_year"] | ||
|
||
@classmethod | ||
def create(cls, request_body): | ||
new_planet = cls(name = request_body["name"], description = request_body["description"], circumference = request_body["circumference"], length_of_year = request_body["length_of_year"]) | ||
|
||
return new_planet |
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 helper methods and use of a class method!
@planets_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 jsonify(f"Planet {new_planet.name} has been successfully created"), 201 #use make response when you want to return something that is not json |
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.
Looks great! We could also consider validating the request_body in a separate helper function.
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.
To add onto your comment about make_response
, we can also use it when we want to add additional headers to a response. Here are some examples in the documentation:
https://flask.palletsprojects.com/en/2.0.x/api/#flask.make_response
|
||
# { | ||
# name: | ||
# description: | ||
# circumference: | ||
# length_of_year: | ||
# } |
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.
We can get rid of unneeded comments when submitting code for review.
@planets_bp.route("/<id>", methods = ["GET"]) | ||
def get_one_planet(id): | ||
planet = validate_planet(id) | ||
return jsonify(planet.to_json()), 200 |
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 helper! Also, if a view function returns a dictionary (planet.to_json()
in this case), Flask will automatically convert the dictionary into a JSON response object (aka Flask will turn dictionaries into JSON data for us). This means you can leave out make_response()
like so:
@planets_bp.route("/<planet_id>", methods=["GET"])
def read_one_planet(planet_id):
planet = validate_planet(planet_id)
return planet.to_json(), 200
Here's the documentation with more info:
https://flask.palletsprojects.com/en/2.0.x/quickstart/#apis-with-json
@planets_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 #{id} was successfully deleted"), 200 |
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.
When we pass a string to make_response
, Flask will assume that we want to return HTML rather than JSON data. make_response
is useful when we want to attach headers to a response but in this case, we're simply returning JSON, so jsonify()
is sufficient enough.
return jsonify(f"Planet {id} successfully deleted"), 200
@pytest.fixture | ||
def two_saved_planets(app): | ||
# Arrange | ||
mercury = Planet(name="Mercury", | ||
description="made mostly of rocks", circumference=9522, length_of_year = 88) | ||
venus = Planet(name="Venus", | ||
description="most like Earth", circumference=23617, length_of_year=225) | ||
|
||
db.session.add_all([mercury, venus]) | ||
# Alternatively, we could do# db.session.add(mercury)# db.session.add(venus) | ||
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 on setting up the fixtures!
@@ -0,0 +1,70 @@ | |||
|
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 on the tests!
No description provided.