Conversation
mikellewade
left a comment
There was a problem hiding this comment.
Nice work on your Task List API, Qiaoqiao!
| from .routes.task_routes import tasks_bp | ||
| from .routes.goal_routes import goals_bp |
There was a problem hiding this comment.
Don't forget that Flask convention is to name your Blueprints bp and then inside of our __init__.py file we can import with as.
| from dotenv import load_dotenv | ||
| load_dotenv() |
There was a problem hiding this comment.
Nice, using load_dotenv to ensure that your environmental variables are loaded before being accessed.
| app.register_blueprint(tasks_bp) | ||
| app.register_blueprint(goals_bp) |
|
|
||
| class Goal(db.Model): | ||
| id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) | ||
| title: Mapped[str] |
There was a problem hiding this comment.
Love this! This is the pattern that we showed you in class! Leveraging the Declarative Mapping Annotation to declare this column as a non-nullable string.
| class Goal(db.Model): | ||
| id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) | ||
| title: Mapped[str] | ||
| tasks: Mapped[list["Task"]] = relationship(back_populates="goal") |
There was a problem hiding this comment.
Perfect! You are making a relationship attribute on the Goal model. This attribute is going to be a list of Task models. You then use relationship with back_populates to tell SQLAlchemy to sync this attribute with relationship attribute called goal on the Task model.
| title_param = request.args.get("title") | ||
| if title_param: | ||
| query = query.where(Task.title.ilike(f"%{title_param}%")) | ||
|
|
||
| sort_param = request.args.get("sort") | ||
| if sort_param: | ||
| if sort_param.lower() == "asc": | ||
| query = query.order_by(asc(Task.title)) | ||
| elif sort_param.lower() == "desc": | ||
| query = query.order_by(desc(Task.title)) | ||
|
|
||
| query = query.order_by(Task.id) | ||
|
|
||
| tasks = db.session.scalars(query) | ||
|
|
||
| tasks_response = [] | ||
| for task in tasks: | ||
| tasks_response.append(task.to_dict()) | ||
| return tasks_response |
There was a problem hiding this comment.
We could D.R.Y. this up as well with get_model_with_filters
| def get_one_task(task_id): | ||
| task = validate_model(Task, task_id) | ||
|
|
||
| return {"task": task.to_dict()} |
There was a problem hiding this comment.
| return {"task": task.to_dict()} | |
| return { "task": task.to_dict() } |
| task.title = request_body["title"] | ||
| task.description = request_body["description"] |
There was a problem hiding this comment.
This is another opportunity to D.R.Y. up our code! Notice how in this route and the PUT route for goal_routes.py we follow the pattern of:
object.ATTRIBUTE = request_body["ATTRIBUTE"]We use hasattr and setattr to make a helper function to update our Task and Goal model. It would look like this:
def update_model(obj, data):
for attr, value in data.items():
if hasattr(obj, attr):
setattr(obj, attr, value)
db.session.commit()
return Response(status=204, mimetype="application/json")This refactor not only makes our code D.R.Y but shows that we recognize logic that has higher level usability while handling cases of keys not being found!
|
|
||
| # Assert | ||
| assert response.status_code == 404 | ||
| assert response_body == {'message': 'Task 1 not found'} |
| query = db.select(Goal).where(Goal.id == 1) | ||
| goal = db.session.scalar(query) |
No description provided.