Checkpoint: A Dynamic Web App
This checkpoint ties together everything you've learned — templates, url_for , cookies, uploads, config, contexts, hooks, and class-based views — into one small dynamic web app.
Learn Checkpoint: A Dynamic Web App in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Instead of new theory, you'll recap the core ideas of this section, then prove you can combine them: a build challenge with a runnable starter and full solution, a short quiz, and a recap pointing to what comes next.
Your task is to build one small app that combines four ideas from this section into a single working page. None of the pieces are large on their own — the skill is wiring them together so they cooperate on every request.
Start from the scaffold below. It already runs and prints output — your job is to replace the TODO comments so the counter increments and the header comes from g . Run it as you go to see your progress.
Out of the box this prints a 200 status, the placeholder header, and the rendered task list. Once you finish the TODOs, the body should also report the visit number, and the header should come from g .
The request flows top to bottom: the before_request hook fires first, the view builds the page and cookie, and the after_request hook stamps the header just before the response leaves.
⏱ Timed Quiz
Try to answer each question in your head first, then expand it to check. These cover the load-bearing ideas from the whole section.
A: url_for . You pass the endpoint name (the view function's name) plus any route parameters, and Flask returns the correct URL. Because nothing is hard-coded, changing a route's path later doesn't break your links.
A: Build a response with make_response(...) , then call response.set_cookie("name", "value") . You can't set a cookie on a plain string return — you need a real response object. The browser sends the cookie back on later requests, where you read it with request.cookies.get(...) .
A: It must return the response object (usually the same one it received, after modifying it). If you forget the return , the hook replaces the response with None and Flask raises an error.
A: The get(self) method. A MethodView dispatches each HTTP method to the same-named method on the class — POST to post , PUT to put , and so on — which keeps a resource's logic grouped instead of branching on request.method .
A: Debug mode shows the interactive traceback (the Werkzeug debugger), which can expose source code, configuration, and even let a visitor run arbitrary code on your server. It also auto-reloads on file changes. Both are fine locally but dangerous in production, so set DEBUG = False there.
A: g is a per-request scratchpad for data you want to share across functions during a single request — a database connection, the current user, or a request id. It's created fresh for every request and discarded when the request ends, so a value set in a before_request hook is readable in the view and the after_request hook.
❌ Forgetting to return the response from after_request
The hook must return response . Without it, the response becomes None and Flask errors out. This is the single most common mistake when wiring up request hooks.
Cookies are always strings. Reading request.cookies.get("visits", "0") and adding 1 directly would concatenate, so convert with int(...) first.
Checkpoint complete — you can build a real dynamic app!
You combined templates, a cookie, request hooks with g , and a MethodView into one working page — the same shape that powers production Flask apps. Everything from this section now fits together in your hands.
🚀 Up next: User Login with Flask-Login — add real authentication so your app can recognize and protect individual users.
Practice quiz
Which Flask function builds URLs to your routes instead of hard-coding paths?
- redirect
- make_response
- url_for
- render_template_string
Answer: url_for. url_for builds URLs from the endpoint name plus route parameters, so changing a route's path later does not break your links.
How do you set a cookie on a response in Flask?
- Build a response with make_response(...) and call response.set_cookie(...)
- return a plain string with the cookie appended
- Assign to request.cookies directly
- Call session.set_cookie(...)
Answer: Build a response with make_response(...) and call response.set_cookie(...). You need a real response object: make_response(...) then response.set_cookie('name', 'value'); you cannot set a cookie on a plain string return.
What must an after_request handler return?
- None
- A status code integer
- The request object
- The response object it received (after modifying it)
Answer: The response object it received (after modifying it). An after_request handler must return the response object; forgetting the return replaces it with None and Flask raises an error.
When a GET request hits a MethodView, which method runs?
- handle()
- get(self)
- dispatch(self)
- index(self)
Answer: get(self). A MethodView dispatches each HTTP method to a same-named method, so a GET calls get(self), a POST calls post(self), and so on.
Why must DEBUG be False in production?
- The interactive debugger can expose source/config and allow arbitrary code execution
- It speeds up template rendering
- It enables cookies
- It is required by url_for
Answer: The interactive debugger can expose source/config and allow arbitrary code execution. Debug mode shows the Werkzeug interactive traceback, which can leak source and config and even let a visitor run arbitrary code on the server.
What is the g object used for in Flask?
- Global config shared across all requests forever
- A cookie storage helper
- A per-request scratchpad for data shared across functions during one request
- A template rendering engine
Answer: A per-request scratchpad for data shared across functions during one request. g is a per-request scratchpad created fresh each request, so a value set in before_request is readable in the view and after_request.
Since cookies are always strings, how do you safely use a visit counter cookie as a number?
- Add 1 to it directly
- Convert it with int(...) before adding
- Store it as bytes
- Use float() on the response
Answer: Convert it with int(...) before adding. Cookies are strings, so int(request.cookies.get('visits', '0')) + 1 converts before adding; adding 1 directly would concatenate.
Which Jinja2 construct loops over a list of tasks inside a template?
- {{ for t in tasks }}
- <for each='tasks'>
- @for tasks
- {% for t in tasks %} ... {% endfor %}
Answer: {% for t in tasks %} ... {% endfor %}. Jinja2 uses statement tags {% for t in tasks %} ... {% endfor %} to loop, while {{ }} is for outputting expressions.
How do you register a MethodView subclass on a URL?
- @app.route('/') above the class
- app.add_url_rule('/', view_func=HomeView.as_view('home'))
- app.register(HomeView)
- HomeView.route('/')
Answer: app.add_url_rule('/', view_func=HomeView.as_view('home')). You call as_view('endpoint') to turn the class into a view function and pass it to app.add_url_rule(path, view_func=...).
In the request lifecycle, when does the before_request hook run relative to the view?
- After the view returns
- Only on POST requests
- Before every view, so g can be set for the view to read
- Never, unless registered per route
Answer: Before every view, so g can be set for the view to read. before_request runs before every view, so an id stashed on g there is available to the view and to the after_request hook.