Django named URL · route contract workbench — Bind a template link to a namespaced URL name and required argument, revise the URLconf path and verify the resolved path changes without…

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Django named URL · route contract workbench</title><style>
*{box-sizing:border-box}body{margin:0;background:#f5f7f6;color:#203b32;font:15px/1.55 system-ui,sans-serif}main{max-width:1130px;margin:auto;padding:30px}h1{font-size:clamp(27px,4vw,40px);letter-spacing:-.035em;margin:4px 0 8px}h2{font-size:20px;margin:0 0 13px}p{margin:0 0 12px}.eyebrow{font-size:11px;font-weight:750;letter-spacing:.13em;text-transform:uppercase;color:#507360}.sub,.small{color:#586f63}.small{font-size:13px}.binding{background:#173d2c;color:#e0f3e8;border-radius:10px;padding:21px 25px;margin:25px 0;display:flex;align-items:center;gap:18px;flex-wrap:wrap}.binding code{font:21px ui-monospace,Consolas,monospace;color:white}.binding span{font-size:13px}.layout{display:grid;grid-template-columns:280px 1fr;gap:24px}.panel{background:white;border:1px solid #d2dfd6;border-radius:10px;padding:22px}.switch{display:flex;gap:8px;margin-bottom:20px}button{font:650 14px system-ui;min-height:44px;padding:10px 16px;background:#27714f;color:white;border:1px solid #27714f;border-radius:6px;cursor:pointer}button.secondary{background:white;color:#27714f}button[aria-pressed=true]{background:#173d2c;color:white}:focus-visible{outline:3px solid #b97922;outline-offset:3px}label{font-size:13px;font-weight:700;display:block}input,select{font:15px/1.4 system-ui;width:100%;padding:10px;border:1px solid #9eb5a5;border-radius:5px;display:block;margin:5px 0 17px;background:white;color:#203b32}pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font:14px/1.7 ui-monospace,Consolas,monospace}.code{background:#f2f6f3;padding:17px;border-radius:6px;border:1px solid #dce6df}.result{background:#e7f2ea;border:1px solid #b4d0bb;border-left:5px solid #318456;padding:20px;border-radius:6px;margin:18px 0}.path{font:750 clamp(25px,4vw,39px)/1.3 ui-monospace,Consolas,monospace;overflow-wrap:anywhere;letter-spacing:-.03em;margin:8px 0}.trace{display:grid;grid-template-columns:1fr 1fr;gap:16px}.trace h3{font-size:14px;margin:0 0 10px}.trace div{background:#f9fbfa;padding:15px;border:1px solid #dce6df;border-radius:6px}.trace code{font-size:17px;word-break:break-word}.error{color:#a33626;min-height:23px;font-size:14px}.ledger{margin-top:25px}.cases{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}.case{border-top:3px solid #a8c5b1;padding:12px 0}.case strong{display:block}.case p{font-size:13px;color:#586f63;margin:5px 0}.packet{margin-top:24px}.packet textarea{display:block;width:100%;min-height:340px;font:13px/1.6 ui-monospace,Consolas,monospace;color:#203b32;background:#f9fbfa;border:1px solid #bfd0c3;border-radius:6px;padding:15px;margin:12px 0;resize:vertical}.packet .row{display:flex;justify-content:space-between;align-items:center;gap:15px;flex-wrap:wrap}button:disabled{opacity:.5;cursor:default}footer{margin-top:24px;font-size:13px;color:#586f63;max-width:95ch}@media(max-width:780px){main{padding:23px 18px}.layout{grid-template-columns:1fr}.cases{grid-template-columns:1fr 1fr}.panel{padding:20px}.binding{padding:18px}.trace{grid-template-columns:1fr}}@media(max-width:400px){main{padding:20px 12px}.panel{padding:16px}.cases{grid-template-columns:1fr}.switch button{flex:1}}
</style></head><body><main><p class="eyebrow">Django 5.2 reference · original notebook fixture</p><h1>Change the route. Keep the name.</h1><p class="sub">Compare a path revision while the namespaced template binding stays intact.</p><div class="binding"><code>notebook:detail</code><span>+ note_id</span><span>→ one root-mounted default instance · no extra prefix</span></div><div class="layout"><aside class="panel"><h2>Route contract</h2><div class="switch"><button type="button" id="s1" aria-pressed="true">S1 · notes</button><button type="button" id="s2" class="secondary" aria-pressed="false">S2 · entries</button></div><label for="segment">Route segment<input id="segment" value="notes" maxlength="40" spellcheck="false"></label><label for="note-id">note_id · nonnegative integer<input id="note-id" value="7" inputmode="numeric" maxlength="6"></label><label for="scenario">Tag case<select id="scenario"><option value="detail">Required detail link</option><option value="missing">E1 · missing route name</option><option value="argument">E2 · missing note_id</option><option value="optional">Optional missing help route</option></select></label><p class="small">app_name = notebook<br>Instance = notebook<br>Root include = ""<br>View stub = views.detail</p><button id="reset" type="button" class="secondary">Restore S1 fixture</button></aside><section class="panel"><h2>Unchanged link binding</h2><pre class="code" id="template-code"></pre><div class="result"><p class="eyebrow">Expected under this contract · local model</p><div id="expected-path" class="path" role="status">/notes/7/</div><p id="expected-note" class="small">Expected path only; native reverse and template render have not been run.</p></div><p id="error" class="error" role="alert"></p><div class="trace"><div><h3>Application URLconf · current</h3><pre id="route-code"></pre></div><div><h3>Literal-path maintenance trap</h3><code>/notes/7/</code><p id="literal-comparison" class="small" style="margin-top:8px">Matches S1's expected location, but bypasses the named contract.</p></div></div><p id="revision-status" class="small" style="margin:17px 0 0">S1 source selected. Native evidence: not supplied.</p></section></div><section class="panel ledger"><h2>Native evidence to obtain</h2><div class="cases"><div class="case"><strong>S1 · source notes/</strong><p>Expected /notes/7/</p><p>Actual reverse/render: not supplied</p></div><div class="case"><strong>S2 · source entries/</strong><p>Expected /entries/7/</p><p>Actual reverse/render: not supplied</p></div><div class="case"><strong>E1 / E2 · required</strong><p>Expected NoReverseMatch</p><p>Actual exception: not supplied</p></div><div class="case"><strong>Optional help</strong><p>Expected no anchor</p><p>Actual render: not supplied</p></div></div><p class="small">Record installed version, settings, source revision, mount, namespace, template, context, and actual output. Changed inputs need fresh evidence; retain older observations with their original revision.</p></section><section class="panel packet"><div class="row"><h2>Native source packet · manual copy</h2><button id="select-packet" type="button" class="secondary">Select text</button></div><p class="small">Copy the selected source into your own project for native verification. This page does not execute it.</p><label for="packet">Current URLconf, template, view stub, and verification notes<textarea id="packet" readonly spellcheck="false"></textarea></label><p id="selection-status" class="small" role="status"></p></section><footer>This HTML companion models only the explicit single-instance route contract. It does not run Django, resolve project namespaces, execute a view, open a note, query a database, establish authentication, or verify a live link. All native actual-result cells remain unsupplied. The reference version is 5.2, not a claim about an installed or latest version.</footer></main><script>
'use strict';const $=id=>document.getElementById(id);let revision='S1';
function contract(segment,id,scenario){if(!/^[A-Za-z][A-Za-z0-9_-]{0,39}$/.test(segment))return{error:'Route segment must start with a letter and contain only letters, digits, underscores, or hyphens.'};if(!/^[0-9]{1,6}$/.test(id))return{error:'note_id must contain 1–6 digits, with no sign or decimal point.'};id=String(Number(id));if(scenario==='missing')return{path:'NoReverseMatch',note:'Expected required-tag failure: notebook:missing has no route in this fixture.',template:"{% url 'notebook:missing' note_id="+id+' %}'};if(scenario==='argument')return{path:'NoReverseMatch',note:'Expected required-tag failure: notebook:detail requires note_id.',template:"{% url 'notebook:detail' %}"};if(scenario==='optional')return{path:'No anchor emitted',note:'Expected optional behavior: help is absent and the conditional anchor is skipped.',template:"{% url 'notebook:help' as optional_url %}\n{% if optional_url %}\n  <a href=\"{{ optional_url }}\">Notebook help</a>\n{% endif %}"};return{path:'/'+segment+'/'+id+'/',note:'Expected path only; native reverse and template render have not been run.',template:'<a href="{% url \'notebook:detail\' note_id='+id+' %}">Open demonstration note '+id+'</a>'};}
function render(){const segment=$('segment').value.trim(),rawId=$('note-id').value.trim(),id=/^[0-9]{1,6}$/.test(rawId)?String(Number(rawId)):rawId,scenario=$('scenario').value,r=contract(segment,id,scenario);$('error').textContent=r.error||'';$('selection-status').textContent='';$('select-packet').disabled=!!r.error;$('segment').setAttribute('aria-invalid',String(!/^[A-Za-z][A-Za-z0-9_-]{0,39}$/.test(segment)));$('note-id').setAttribute('aria-invalid',String(!/^[0-9]{1,6}$/.test(id)));$('expected-path').textContent=r.error?'Not calculated':r.path;$('expected-note').textContent=r.error?'Correct the contract input to rebuild this expectation.':r.note;$('template-code').textContent=r.error?'':r.template;$('route-code').textContent=r.error?'':'app_name = "notebook"\npath("'+segment+'/<int:note_id>/",\n     views.detail, name="detail")';$('literal-comparison').textContent=r.error?'Comparison cleared until inputs are valid.':segment==='notes'&&id==='7'?'Matches S1’s expected location, but bypasses the named contract.':'The old literal /notes/7/ stays unchanged. Current required-detail expectation: /'+segment+'/'+id+'/.';$('revision-status').textContent=revision+' source selected. Current-input native evidence: not supplied; previous observations cannot establish this result.';for(const s of ['s1','s2'])$(s).setAttribute('aria-pressed',String(revision===s.toUpperCase()));$('packet').value=r.error?'':['# Original notebook fixture — source specification, not executed','# Django 5.2 reference; record your installed version/settings separately.','# notebook/urls.py','from django.urls import path','from . import views','app_name = "notebook"','urlpatterns = [','    path("'+segment+'/<int:note_id>/", views.detail, name="detail"),',']','','# project/urls.py','from django.urls import include, path','urlpatterns = [path("", include("notebook.urls"))]','','# notebook/views.py','from django.http import HttpResponse','def detail(request, note_id):','    return HttpResponse("Demonstration route only.")','','# link template',r.template,'','# Expected local contract result: '+r.path,'# Native actual reverse/render/error output: NOT SUPPLIED','# For required detail, test reverse("notebook:detail", kwargs={"note_id": '+id+'})','# Render the actual template under the same settings and source revision.','# Required missing route / missing note_id: inspect actual NoReverseMatch.','# Optional missing help: inspect actual absence of an anchor.','# Keep URLconf, settings, context, template, installed version and output together.'].join('\n');}
for(const name of ['s1','s2'])$(name).addEventListener('click',()=>{revision=name.toUpperCase();$('segment').value=name==='s1'?'notes':'entries';render();});for(const id of ['segment','note-id'])$(id).addEventListener('input',()=>{revision='Custom';render();});$('scenario').addEventListener('change',render);$('reset').addEventListener('click',()=>{revision='S1';$('segment').value='notes';$('note-id').value='7';$('scenario').value='detail';render();});$('select-packet').addEventListener('click',()=>{$('packet').focus();$('packet').select();$('selection-status').textContent='Source text selected. Use your device’s copy command and paste into your own native project.';});render();
</script></body></html>