FTM301 · Examination paper
120 minutes · laptop with the repo · open repo, no internet · 40 marks · pass 20
Answer in ANSWERS.md with file paths and one sentence of mechanism per answer.
A path without a mechanism scores half.
Section A — where does this live? (18 marks, 12 × 1.5)
Section titled “Section A — where does this live? (18 marks, 12 × 1.5)”For each, give the file and the mechanism in one sentence.
- Where is the dashboard’s base template?
- Where is the Ninja API mounted, and in which file?
- Where are outbound emails rendered from?
- Which file decides whether an inbound mail came from a band or a client?
- Which field is generated rather than stored, and from what?
- Where is the arq worker’s entry module?
- Which function enqueues an arq job from Django?
- Where does the dashboard’s session middleware sit?
- Where do automated admin pages get their URLs?
- Which module holds S3 storage configuration?
- Where does the OpenAPI snapshot live?
- Which file holds the per-environment secrets?
Section B — trace one request (12 marks)
Section titled “Section B — trace one request (12 marks)”A logged-in musician opens
/pandemoniums/messages/2806551/ on musicians.staging.fixthemusic.com, and a
message renders.
Produce the chain: URLconf entry → view → queryset/models touched → template. For each hop, one line of code (file:line) that proves it. Then name two queries you would expect the page to issue and how you would cut them to one. (6 + 6)
Section C — migration safety review (10 marks)
Section titled “Section C — migration safety review (10 marks)”Given this migration on a live table with ~2M rows and running code:
class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='enquirymessage', name='priority', field=models.IntegerField(default=0, null=False), ), migrations.AlterField( model_name='enquirymessage', name='kind', field=models.CharField(max_length=4, choices=KIND_CHOICES, null=True), ), ]State every risk, whether the order matters, and what you would change. Name the
deploy sequence for the kind widening.
Marking guide
Section titled “Marking guide”| Section | Marks |
|---|---|
| A | 1.5 each — 0.75 file, 0.75 mechanism. Round down |
| B | 6 chain, 6 queries. Chain: 1.5 per correct hop. Queries: 3 each for the two queries and the join/prefetch fix |
| C | 4 risks, 2 order, 4 sequence |
Pass 20/40. Distinction 28. Section A must score at least 10 — this module is navigation, and a student who cannot find things has not learned it.
Expected answers:
- 1
bands/templates/dashboard/layout.html - 2
ftm/urls.py,include('bands.api_v2_urls')atmusician-api/v0/ - 3
emails/templates/bookings/enquiryband_correspondence/quote.htmlandto_client.html, rendered fromemails/ - 4
emails/views.py→find_side() - 5
EnquiryMessage.side— GeneratedField fromtoandkind - 6
worker/src/ftm_worker/__init__.py - 7
ftm/arq_client.py→enqueue_job_sync - 8
bands/dashboard/middleware.py/api_auth_middleware.py, ordered in settings - 9
get_urls()on aModelAdmin, templates underbooking/templates/admin/ - 10
ftm/storage.py - 11
bands/api_v2/openapi_snapshot.json - 12
deployment/backend-env.nix - B
ftm/urls.py→bands/dashboard/message_views→EnquiryMessagefiltered by band +select_relatedon enquiry →bands/templates/dashboard/…. N+1 on band/enquiry is the query to cut. - C
AddField(default=0, null=False)on 2M rows rewrites the table on older Postgres and takes a lock;AlterFieldwidening aCharFieldis metadata-only in Postgres (no rewrite) but the choices change is code-side and the old rows must still parse; order matters because the add should be nullable + backfill +SET NOT NULL; the deploy sequence is add-nullable → migrate → deploy writing both → backfill → constrain.