-
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?
Changes from all commits
57f27d3
0efeb9a
769c4cf
6595fd2
93954be
ff2ec6e
a8fae40
cc4aec6
443e0ca
2f0d8eb
32bbf87
92d9344
645b5e6
048f1fc
d0d1fd7
88f5073
a5f903f
8fd9181
ba996be
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 |
---|---|---|
@@ -1,7 +1,32 @@ | ||
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__) | ||
|
||
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) | ||
|
||
return app |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
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 | ||
Comment on lines
+1
to
+15
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. 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 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. 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from app import db | ||
from flask import abort, make_response | ||
|
||
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) | ||
Comment on lines
+4
to
+9
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. Should we be able to make planets with no name? To prevent our API from creating a planet with a 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 | ||
Comment on lines
+11
to
+31
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 helper methods and use of a class method! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,160 @@ | ||
from flask import Blueprint | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
from app import db | ||
from app.models.planet import Planet | ||
from app.models.helpers import validate_planet | ||
|
||
|
||
planets_bp = Blueprint("planets", __name__, url_prefix = "/planets") | ||
|
||
# CREATE PLANET | ||
@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 | ||
Comment on lines
+10
to
+19
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. 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 commentThe reason will be displayed to describe this comment to others. Learn more. To add onto your comment about |
||
|
||
# GET ALL | ||
@planets_bp.route("", methods = ["GET"]) | ||
def get_all_planets(): | ||
|
||
days_query = request.args.get("length_of_year") | ||
name_query = request.args.get("name") | ||
description_query = request.args.get("description") | ||
circumference_query = request.args.get("circumference") | ||
|
||
if days_query: | ||
planets = Planet.query.filter_by(length_of_year = days_query) | ||
elif name_query: | ||
planets = Planet.query.filter_by(name = name_query) | ||
elif description_query: | ||
planets = Planet.query.filter_by(description = description_query) | ||
elif circumference_query: | ||
planets = Planet.query.filter_by(circumference = circumference_query) | ||
else: | ||
planets = Planet.query.all() | ||
|
||
planets_response = [] | ||
|
||
for planet in planets: | ||
planets_response.append(planet.to_json()) | ||
|
||
return jsonify(planets_response) | ||
Comment on lines
+23
to
+46
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. Loving the exploration with filters here! We can also consider making a helper method (especially class method) in |
||
|
||
# GET ONE PLANET | ||
@planets_bp.route("/<id>", methods = ["GET"]) | ||
def get_one_planet(id): | ||
planet = validate_planet(id) | ||
return jsonify(planet.to_json()), 200 | ||
Comment on lines
+49
to
+52
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 helper! Also, if a view function returns a dictionary ( @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: |
||
|
||
|
||
|
||
|
||
# { | ||
# name: | ||
# description: | ||
# circumference: | ||
# length_of_year: | ||
# } | ||
Comment on lines
+56
to
+62
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. We can get rid of unneeded comments when submitting code for review. |
||
|
||
#PUT or update ONE PLANET | ||
@planets_bp.route("/<id>", methods=["PUT"]) | ||
def update_planet(id): | ||
planet = validate_planet(id) | ||
|
||
request_body = request.get_json() | ||
|
||
planet.update(request_body) | ||
# planet.name = request_body["name"] | ||
# planet.description = request_body["description"] | ||
# planet.circumference = request_body["circumference"] | ||
# planet.length_of_year = request_body["length_of_year"] | ||
|
||
db.session.commit() | ||
|
||
return make_response(f"Planet #{id} succesffully updated"), 200 | ||
|
||
#MAKE PATCH REQUEST | ||
|
||
#DELETE ONE PLANET | ||
@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 | ||
Comment on lines
+84
to
+91
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. When we pass a string to return jsonify(f"Planet {id} successfully deleted"), 200 |
||
# {"name": "Mercury", | ||
# "description": "made mostly of rocks", | ||
# "circumference": 9522, | ||
# "length_of_year": 88 | ||
# } | ||
|
||
|
||
# { | ||
# "name": Venus, | ||
# "description": most like Earth, | ||
# "circumference": 23617, | ||
# "length_of_year": 225 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Earth" | ||
# "description": "you are here" | ||
# "circumference": 24889 | ||
# "length_of_year": 365 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Mars", | ||
# "description": "the red planet", | ||
# "circumference": 13256, | ||
# "length_of_year": 687 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Jupiter", | ||
# "description": "largest planet", | ||
# "circumference": 278985, | ||
# "length_of_year": 4320 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Saturn", | ||
# "description": "sun's bae with all 7 rings", | ||
# "circumference": 235185, | ||
# "length_of_year": 10620 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Uranus", | ||
# "description": "can only be seen with a telescope", | ||
# "circumference": 99739, | ||
# "length_of_year": 30240 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Neptune", | ||
# "description": "it is an intense blue color", | ||
# "circumference": 96645, | ||
# "length_of_year": 59400 | ||
# } | ||
|
||
|
||
# { | ||
# "name": "Pluto", | ||
# "description": "no dwarf in my book", | ||
# "circumference": 7144, | ||
# "length_of_year": 88920 | ||
# } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import pytest | ||
from app.models.planet import Planet | ||
from app import create_app | ||
from app import db | ||
from flask.signals import request_finished | ||
|
||
|
||
@pytest.fixture | ||
def app(): | ||
app = create_app({"TESTING": True}) | ||
|
||
# everytime you finish a request this removes any saved data (like cleaning our a cache) | ||
@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): | ||
# 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() | ||
Comment on lines
+29
to
+39
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 work on setting up the fixtures! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
|
||
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 work on the tests! |
||
def test_get_all_planets_successfully(client): | ||
# Act | ||
response = client.get("/planets") | ||
# response has more than just json in it so we just want the json | ||
response_body = response.get_json() | ||
|
||
# Assert | ||
assert response.status_code == 200 | ||
assert response_body == [] # comparing to empty list bc we are in our test database | ||
|
||
|
||
|
||
def test_create_one_planet(client): | ||
# Act | ||
response = client.post("/planets", json={ | ||
"name": "Goofy", | ||
"description": "goofy", | ||
"circumference": 1, | ||
"length_of_year": 1 | ||
}) | ||
response_body = response.get_json() | ||
|
||
# Assert | ||
assert response.status_code == 201 | ||
assert response_body == "Planet Goofy has been successfully created" | ||
|
||
def test_get_one_planet_from_fixture_successfully(client, two_saved_planets): | ||
#act | ||
response = client.get("/planets/1") | ||
|
||
response_body = response.get_json() | ||
#assert | ||
assert response.status_code == 200 | ||
assert response_body == {"name": "Mercury", | ||
"description": "made mostly of rocks", | ||
"circumference": 9522, | ||
"length_of_year": 88, | ||
"id": 1 | ||
} | ||
|
||
def test_get_one_planet_with_no_data_returns_404(client): | ||
#act | ||
response = client.get("/planets/1") | ||
|
||
response_body = response.get_json() | ||
#assert | ||
assert response.status_code == 404 | ||
assert response_body == {'message': 'planet 1 not found'} | ||
|
||
def test_get_planet_from_fixture_successfully(client, two_saved_planets): | ||
#act | ||
response = client.get("/planets") | ||
|
||
response_body = response.get_json() | ||
#assert | ||
assert response.status_code == 200 | ||
assert response_body == [{"name": "Mercury", | ||
"description": "made mostly of rocks", | ||
"circumference": 9522, | ||
"length_of_year": 88, | ||
"id": 1 | ||
}, | ||
{ | ||
"name": "Venus", | ||
"description": "most like Earth", | ||
"circumference": 23617, | ||
"length_of_year": 225, | ||
'id': 2, | ||
}] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Generic single-database configuration. |
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!