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

Zoisite - Jackie Aponte #28

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Zoisite - Jackie Aponte #28

wants to merge 8 commits into from

Conversation

japonte08
Copy link

No description provided.

Copy link

@yangashley yangashley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job with Solar System!

I left some comments that I'm hoping you can apply to your work on Task List!

Comment on lines +17 to +23
planets_response = []
for planet in planets:
planets_response.append({
"id": planet.id,
"name": planet.name,
"description": planet.description
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a good spot to try out list comprehension. You could create an instance method in the Planet class to_dict that turns an instance of planet into a dictionary representing a planet. We need to send back objects represented by dictionaries since our client doesn't understand python.

class Planet(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String)
description = db.Column(db.String)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add the instance method to_dict and class method from_dict to this class, which will shorten up the code in your routes.

    def to_dict(self):
        return dict(
            id = self.id,
            name = self.name,
            description = self.description
        )

    @classmethod
    def from_dict(cls, planet_data):
        return cls(
            name = planet_data["name"],
            description = planet_data["description"]
        )

No problem if you don't have time to refactor here, but try implementing this for Task List.


planets_bp = Blueprint("planets", __name__, url_prefix="/planets")

@planets_bp.route("", methods=["GET", "POST"])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer that GET and POST are separate routes

})
return jsonify(planets_response)

elif request.method == "POST":

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Older patterns for designing routes in Flask have shown a route that handles different REST methods and then uses if/elif to conditionally return the correct response. By separating the two request types out into their own methods, the code becomes more readable and each function has a single responsibility.

Comment on lines +27 to +29
request_body = request.get_json()
new_planet = Planet(name=request_body["name"],
description=request_body["description"])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add in some error handling here since request_body is the data that a client sends via the request body. We should assume that requests could have bad data, either missing keys or values or the keys could be misspelled.

If the request_body didn't have "name" but had "planet_name" then line 28 would throw an unhandled exception.

return planet


@planets_bp.route("/<planet_id>", methods=["GET", "PUT", "DELETE"])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer each request type to have their own function.


db.session.add_all([planet_mars, planet_jupiter])

db.session.commit()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that the Learn reading has a two_saved_books that looks like this. It creates two objects, adds them and commits to the DB. It would be nice if this fixture (while not shown in Learn) actually returns the objects it creates so that you can use them for comparison in your unit tests, like this and this

Comment on lines +31 to +40
assert response_body == [{
"id": 1,
"name": "Mars",
"description": "Smallest planet"
},
{
"id": 2,
"name": "Jupiter",
"description": "Largest planet"
}]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the fixture two_saved_planets returned two planet objects, you'd be able to compare what's in the response body against what was used to create records in the database (as we do in Flasky)

Comment on lines +45 to +53
response = client.post("/planets", json={
"name": "Earth",
"description": "Water planet"
})
response_body = response.get_json()

# Assert
assert response.status_code == 201
assert response_body == "Planet Earth successfully created"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to using the response to write assertions, your test would be more robust if after the post request, you query the database for the object you just created. Since we run fixtures before each test, we know that before your test runs we will have a database with empty tables, after your post request in the test we will have one planet record. We can query for that one record by assuming it's id will be 1 and compare its attributes to the request body you sent with the post request. Like this


db.session.commit()

return make_response(f"Planet #{planet.id} successfully updated")

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(), the datatype that gets sent back as a header in the response is HTML. However, your GET /planets and POST /planets routes return jsonify'd objects which have a datatype of JSON. Therefore, one API is sending a mix of HTML and JSON responses, which isn't ideal.

I know this is what was shown in Learn 😖. In the last reading from Part 6 Testing, there's mention of using jsonify like this:
return make_response(jsonify(f"Planet #{planet.id} successfully updated"))

Doing so changes the header data type from HTML to JSON. Making the changes in the DELETE requests would make your API only return JSON responses, which is good.

An even better way to go about this, I think, would be for your PUT and your POST requests to just return a dictionary representing the planet that was inserted into the DB or the planet that was updated in the DB. So for your PUT and your POST request you could return a response like:

return jsonify(planet.to_dict()), 201 # or whatever correct status code you want to pass in the response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants