You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: ada-project-docs/optional-enhancements.md
+2-22Lines changed: 2 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,36 +28,16 @@ How would you write tests for it? How would you implement it?
28
28
29
29
Your decisions should not break the other tests.
30
30
31
-
### Model Instance Methods
32
-
33
-
We can define instance methods in our model classes.
34
-
35
-
Consider places in your code that deal with one model at a time. Is there any repeated logic or behavior?
36
-
37
-
Here are some ideas to start:
38
-
39
-
- Create an instance method in `Task` named `to_dict()`
40
-
- Converts a `Task` instance into JSON
41
-
- Returns a Python dictionary in the shape of the JSON our API returns in the `GET``/tasks` route
42
-
- Create a class method in `Task` named `from_json()`
43
-
- Converts JSON into a new instance of `Task`
44
-
- Takes in a dictionary in the shape of the JSON our API receives in the create and update routes
45
-
- Returns an instance of `Task`
46
-
47
31
### Use List Comprehensions
48
32
49
33
Use list comprehensions in your route functions where applicable.
50
34
51
-
### Route Helper Methods
52
-
53
-
If you have not already refactored your route files to use helper methods, do so now!
54
-
55
-
Consider code with complex or repetitive logic, and refactor it into helper methods. Watch your route files become cleaner and more readable!
56
-
57
35
### More Query Params
58
36
59
37
Create the tests and implementation so that the user may
60
38
61
39
- filter tasks by title
62
40
- sort tasks by id
63
41
- sort goals by title
42
+
43
+
Remember that Wave 2 already has a sorting feature for tasks by title, so we might practice creating another route helper method that can be used for sorting across multiple model types. Such a method could even be extended to perform the filtering as well!
Copy file name to clipboardExpand all lines: ada-project-docs/wave_01.md
+36-3Lines changed: 36 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,12 +36,30 @@ Tasks should contain these attributes. **The tests require the following columns
36
36
- We can assume that the value of each task's `completed_at` attribute will be `None`, until wave 3. (Read below for examples)
37
37
- We can assume that the API will designate `is_complete` as `false`, until wave 3. (Read below for examples)
38
38
39
+
## Model Helper Methods
40
+
41
+
We have seen that we can define instance methods in our model classes. We should practice those skills here by including model helper methods that can:
42
+
43
+
- Return a Python dictionary in the shape of the JSON our API expects to return for a model instance.
44
+
- Create a new model instance from a dictionary, typically retrieved from the JSON sent as a request body.
45
+
46
+
## Route Helper Methods
47
+
48
+
Beyond model helper methods, we should also practice creating route helper methods. Especially in this wave, we should consider creating a route helper method that can:
49
+
50
+
- Retrieve a model instance by its ID.
51
+
52
+
We might also consider creating a route helper method that can:
53
+
54
+
- Handle the creation of new instances of a model from a dictionary, returning the expected responses.
55
+
39
56
## CRUD for Tasks
40
57
41
58
### Tips
42
59
43
-
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any mispellings.
44
-
- That said, remember that dictionaries do not have an implied order. This is still true in JSON with objects. When you make Postman requests, the order of the key/value pairings within the response JSON object does not need to match the order specified in this document. (The term "object" in JSON is analagous to "dictionary" in Python.)
60
+
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any misspellings.
61
+
- That said, remember that dictionaries do not have an implied order. This is still true in JSON with objects. When you make Postman requests, the order of the key/value pairings within the response JSON object does not need to match the order specified in this document. (The term "object" in JSON is analogous to "dictionary" in Python.)
62
+
- Notice that the details for a Task in the response is shared across all the endpoints that return Task details. Rather than repeating the same literal `dict` structure in each response, we should create a helper method that returns the `dict` structure for a Task, and use it in each relevant endpoint. This will ensure that all our responses are consistent.
45
63
- Use the tests in `tests/test_wave_01.py` to guide your implementation.
46
64
- You may feel that there are missing tests and missing edge cases considered in this wave. This is intentional.
47
65
- You have fulfilled wave 1 requirements if all of the wave 1 tests pass.
@@ -87,6 +105,10 @@ and get this response:
87
105
88
106
so that I know I successfully created a Task that is saved in the database.
89
107
108
+
Remember that the knowledge of how to initialize a new model instance from the request dictionary is often left to the model itself, as it allows the model to control which fields are required and how they are initialized. We could add a class method to the Task model that initializes a new instance from a dictionary, and use this method in the route. If all of our models have this method, we could create a route helper method that initializes a new model instance from a dictionary, and use it in this route and any other route that creates a new model instance.
109
+
110
+
Further, notice that the data nested under the `"task"` key is a dictionary representation of the task that was created. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
111
+
90
112
#### Get Tasks: Getting Saved Tasks
91
113
92
114
As a client, I want to be able to make a `GET` request to `/tasks` when there is at least one saved task and get this response:
@@ -110,6 +132,8 @@ As a client, I want to be able to make a `GET` request to `/tasks` when there is
110
132
]
111
133
```
112
134
135
+
Notice that each data item in the list is a dictionary representation of a task. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
136
+
113
137
#### Get Tasks: No Saved Tasks
114
138
115
139
As a client, I want to be able to make a `GET` request to `/tasks` when there are zero saved tasks and get this response:
@@ -137,6 +161,10 @@ As a client, I want to be able to make a `GET` request to `/tasks/1` when there
137
161
}
138
162
```
139
163
164
+
Notice that the data nested under the `"task"` key is a dictionary representation of the task that was retrieved. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
165
+
166
+
Further, we should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method could start out only working for Task models. But knowing that we'll be working with Goal models later on, it might be worth generalizing this method to work with any model.
167
+
140
168
#### Update Task
141
169
142
170
As a client, I want to be able to make a `PUT` request to `/tasks/1` when there is at least one saved task with this request body:
@@ -156,6 +184,8 @@ The response should have a mimetype of "application/json" to keep our API respon
156
184
157
185
Note that the update endpoint does update the `completed_at` attribute. This will be updated with custom endpoints implemented in Wave 3.
158
186
187
+
We should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method could start out only working for Task models. But knowing that we'll be working with Goal models later on, it might be worth generalizing this method to work with any model.
188
+
159
189
#### Delete Task: Deleting a Task
160
190
161
191
As a client, I want to be able to make a `DELETE` request to `/tasks/1` when there is at least one saved task and get this response:
@@ -164,6 +194,8 @@ As a client, I want to be able to make a `DELETE` request to `/tasks/1` when the
164
194
165
195
The response should have a mimetype of "application/json" to keep our API response type consistent.
166
196
197
+
We should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method could start out only working for Task models. But knowing that we'll be working with Goal models later on, it might be worth generalizing this method to work with any model.
198
+
167
199
#### No Matching Task: Get, Update, and Delete
168
200
169
201
As a client, if I make any of the following requests:
@@ -179,7 +211,8 @@ The response code should be `404`.
179
211
You may choose the response body.
180
212
181
213
Make sure to complete the tests for non-existing tasks to check that the correct response body is returned.
182
-
214
+
215
+
By using a helper method to retrieve a model by its ID, we could ensure that the response for a non-existing model is consistent across all these routes.
183
216
184
217
#### Create a Task: Invalid Task With Missing Data
Copy file name to clipboardExpand all lines: ada-project-docs/wave_03.md
+12-2Lines changed: 12 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,7 +23,7 @@ The following are required routes for wave 3. Feel free to implement the routes
23
23
- SQL's value of `null` is similar to Python's value of `None`.
24
24
- Python has a [datetime library](https://docs.python.org/3/library/datetime.html#module-datetime) which we recommend using to represent dates in model attributes.
25
25
26
-
### Mark Complete on an Incompleted Task
26
+
### Mark Complete on an Incomplete Task
27
27
28
28
Given a task that has:
29
29
@@ -113,7 +113,11 @@ After I have made the `PATCH` request, I can submit a `GET` request to `/tasks/1
113
113
}
114
114
```
115
115
116
-
### Mark Incomplete on an Incompleted Task
116
+
Notice the same dictionary structure for the Task data as in our wave 1 routes. We should be able to use any response model helper that we are using in other Task routes to help build this response.
117
+
118
+
Also notice that fundamentally, this route requires that we look up a model by its ID, and then update that model. We should be able to reuse the same route helper methods that we have been using in other Task routes to help build this route.
119
+
120
+
### Mark Incomplete on an Incomplete Task
117
121
118
122
Given a task that has:
119
123
@@ -143,6 +147,10 @@ After I have made the `PATCH` request, I can submit a `GET` request to `/tasks/1
143
147
}
144
148
```
145
149
150
+
Notice the same dictionary structure for the Task data as in our wave 1 routes. We should be able to use any response model helper that we are using in other Task routes to help build this response.
151
+
152
+
Also notice that fundamentally, this route requires that we look up a model by its ID, and then update that model. We should be able to reuse the same route helper methods that we have been using in other Task routes to help build this route.
153
+
146
154
## Mark Complete and Mark Incomplete for Missing Tasks
147
155
148
156
Given that there are no tasks with the ID `1`,
@@ -154,3 +162,5 @@ Then I get a `404 Not Found`.
154
162
You may choose the response body.
155
163
156
164
Make sure to complete the tests for non-existing tasks to check that the correct response body is returned.
165
+
166
+
Notice that the behavior for a missing Task is consistent with invalid Task IDs in other Task routes. We should be able to use the same route helper methods that we have been using in other Task routes to help build this route.
Copy file name to clipboardExpand all lines: ada-project-docs/wave_05.md
+18-2Lines changed: 18 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,7 +17,7 @@ This wave requires more test writing.
17
17
- The tests you need to write are scaffolded in the `test_wave_05.py` file.
18
18
- These tests are currently skipped with `@pytest.mark.skip(reason="test to be completed by student")` and the function body has `pass` in it. Once you implement these tests you should remove the `skip` decorator and the `pass`.
19
19
- For the tests you write, use the requirements in this document to guide your test writing.
20
-
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any mispellings.
20
+
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any misspellings.
21
21
- You can model your tests off of the Wave 1 tests for Tasks.
22
22
- Some tests use a [fixture](https://docs.pytest.org/en/6.2.x/fixture.html) named `one_goal` that is defined in `tests/conftest.py`. This fixture saves a specific goal to the test database.
23
23
@@ -68,6 +68,10 @@ and get this response:
68
68
69
69
so that I know I successfully created a goal that is saved in the database.
70
70
71
+
Similar to the Task model, we could add a class method to the Goal model that initializes a new instance from a dictionary, and use this method in the route. If all of our models have this method, we could create a route helper method that initializes a new model instance from a dictionary, and use it in this route and any other route that creates a new model instance.
72
+
73
+
Also like the Task model, notice that the data nested under the `"goal"` key is a dictionary representation of the goal that was created. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
74
+
71
75
### Get Goals: Getting Saved Goals
72
76
73
77
As a client, I want to be able to make a `GET` request to `/goals` when there is at least one saved goal and get this response:
@@ -87,6 +91,8 @@ As a client, I want to be able to make a `GET` request to `/goals` when there is
87
91
]
88
92
```
89
93
94
+
Notice that each data item in the list is a dictionary representation of a goal. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
95
+
90
96
### Get Goals: No Saved Goals
91
97
92
98
As a client, I want to be able to make a `GET` request to `/goals` when there are zero saved goals and get this response:
@@ -112,6 +118,10 @@ As a client, I want to be able to make a `GET` request to `/goals/1` when there
112
118
}
113
119
```
114
120
121
+
Notice that the data nested under the `"goal"` key is a dictionary representation of the goal that was retrieved. Creating a model helper method to return this dictionary, which we can then use to help build this route response, will improve the consistency of our endpoints.
122
+
123
+
Further, we should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method would be very similar in functionality to retrieving a Task model by its ID, so rather than making an entirely new route helper method, we could generalize any similar Task model method to work also work with a Goal (or any other model).
124
+
115
125
116
126
### Update Goal
117
127
@@ -129,6 +139,8 @@ and get this response:
129
139
130
140
The response should have a mimetype of "application/json" to keep our API response type consistent.
131
141
142
+
We should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method could be written to work for Goal models, Task models, or any other model.
143
+
132
144
### Delete Goal: Deleting a Goal
133
145
134
146
As a client, I want to be able to make a `DELETE` request to `/goals/1` when there is at least one saved goal and get this response:
@@ -137,6 +149,8 @@ As a client, I want to be able to make a `DELETE` request to `/goals/1` when the
137
149
138
150
The response should have a mimetype of "application/json" to keep our API response type consistent.
139
151
152
+
We should remember that retrieving a model by its ID is a common operation. We should consider creating a route helper method that can retrieve a model by its ID, and use it in this route. This method could be written to work for Goal models, Task models, or any other model.
153
+
140
154
### No matching Goal: Get, Update, and Delete
141
155
142
156
As a client, if I make any of the following requests:
@@ -151,7 +165,9 @@ The response code should be `404`.
151
165
152
166
You may choose the response body.
153
167
154
-
Make sure to complete the tests for non-existing tasks to check that the correct response body is returned.
168
+
Make sure to complete the tests for non-existing tasks to check that the correct response body is returned.
169
+
170
+
By using a helper method to retrieve a model by its ID, we could ensure that the response for a non-existing model is consistent across all these routes.
155
171
156
172
### Create a Goal: Invalid Goal With Missing Title
Copy file name to clipboardExpand all lines: ada-project-docs/wave_06.md
+7-1Lines changed: 7 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ Secondly, we should create our new route, `/goals/<goal_id>/tasks`, so that our
18
18
19
19
- Use lesson materials and independent research to review how to set up a one-to-many relationship in Flask.
20
20
- Remember to run `flask db migrate` and `flask db upgrade` whenever there is a change to the model.
21
-
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any mispellings.
21
+
- Pay attention to the exact shape of the expected JSON. Double-check nested data structures and the names of the keys for any misspellings.
22
22
- Use the tests in `tests/test_wave_06.py` to guide your implementation.
23
23
- Some tests use a fixture named `one_task_belongs_to_one_goal` that is defined in `tests/conftest.py`. This fixture saves a task and a goal to the test database, and uses SQLAlchemy to associate the goal and task together.
24
24
@@ -63,6 +63,8 @@ Then the three `Task`s belong to the `Goal` and it gets updated in the database,
63
63
}
64
64
```
65
65
66
+
We will need to validate that the Goal ID, as well as each Task ID exists in the database. A route helper method that can resolve a model instance from its ID would help us validate the IDs in the request body.
67
+
66
68
### Getting Tasks of One Goal
67
69
68
70
Given a goal that has:
@@ -100,6 +102,10 @@ then I get this response:
100
102
}
101
103
```
102
104
105
+
Notice that if we have been using a model helper method to return a dictionary representation of a Task, we can use this method to help build this route response. However, we must notice that there is an additional key in the data for the Task models that are associated with the Goal. This doesn't necessarily mean that we should abandon the model helper method, but we may need to introduce logic to allow it to work in this context.
106
+
107
+
This is also true of the Goal model helper method. We may need to introduce logic to allow it to work in this context, or use the existing method to generate the basic dictionary representation of the Goal and then add the additional data for the associated Task models.
0 commit comments