Building the First Page¶
Goal¶
By the end of this part the Notes application has a model, a fixture, and an index page at / that lists notes from the database.
The work uses the file router and a single @context function.
Prerequisites¶
You have followed Installation and you can serve a placeholder page at http://127.0.0.1:8000/.
You have a Django application named notes registered in INSTALLED_APPS.
From Installation your config/urls.py already forwards URLs to next.dj through include("next.urls").
If / does not respond, revisit Installation.
Walkthrough¶
Model the Note¶
Create a single Django model that represents a note. Keep the fields minimal so the tutorial stays focused on the framework.
from django.db import models
class Note(models.Model):
title = models.CharField(max_length=120)
body = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ("-created_at",)
def __str__(self) -> str:
return self.title
Apply the migration and seed two rows so the index page has something to render.
uv run python manage.py makemigrations notes
uv run python manage.py migrate
uv run python manage.py shell <<'PY'
from notes.models import Note
Note.objects.create(title='First note', body='Hello, next.dj.')
Note.objects.create(title='Second note', body='Pages are directories.')
PY
Add the Index Page¶
The file router treats the notes/pages/ directory as the page root for the application.
The directory that contains a page.py becomes a URL.
notes/pages/page.py therefore answers the empty path /.
The router scans every application listed in INSTALLED_APPS for a pages/ directory.
Create the page module. A captioned code block holds the complete content of the named file unless the prose explicitly says to append to it.
from notes.models import Note
from next.pages import context
@context("notes")
def recent_notes() -> list[Note]:
return list(Note.objects.all())
Create the template.
<!doctype html>
<html>
<body>
<h1>Notes</h1>
<ul>
{% for note in notes %}
<li>
<strong>{{ note.title }}</strong>
{% if note.body %}<p>{{ note.body }}</p>{% endif %}
<small>{{ note.created_at|date:"Y-m-d H:i" }}</small>
</li>
{% endfor %}
</ul>
</body>
</html>
The framework composes the body of template.djx with any ancestor layout.djx.
You do not have a layout yet, so the page renders standalone.
Run the Server¶
Start the development server.
uv run python manage.py runserver
Visit http://127.0.0.1:8000/ and you should see the two seeded notes.
The development server restarts itself on every Python edit, so reloading the browser page shows the change without a manual restart.
Trace the URL Name¶
Every file-routed page receives a stable URL name in the next namespace.
For notes/pages/page.py that name is next:page_.
Open a Django shell and reverse it to confirm.
uv run python manage.py shell -c "
from django.urls import reverse
print(reverse('next:page_'))
"
The shell prints /.
You will use these names from templates through the standard {% url %} tag.
Inspect Through System Checks¶
next.dj contributes Django system checks for the page configuration.
They confirm each page.py has a render function or a paired template, that parameter directories carry a page.py, and that the request context processor is installed.
See System Checks for the full catalog.
Run them and confirm no warnings remain.
uv run python manage.py check
Checkpoint¶
At the end of this part the project layout looks like this.
notes/
models.py
migrations/
0001_initial.py
pages/
page.py
template.djx
The index page lists notes from the database.
The URL / responds with HTML and the URL name next:page_ reverses cleanly.
Common Pitfalls¶
- Page module is not discovered.
Confirm that
NEXT_FRAMEWORK["DEFAULT_PAGE_BACKENDS"][0]["APP_DIRS"]isTrue.- Template renders without the notes loop.
Make sure
notes/pages/template.djxsits next tonotes/pages/page.py. The framework pairs apage.pywith thetemplate.djxin the same directory.- ImportError for
Note. The
notesapp must be installed and migrated. Re-runuv run python manage.py migrate.
Next Steps¶
You have a page that renders dynamic data. The next part wraps every page in a layout and shares site-wide context.
See also
Adding Layouts and Context adds a layout and shared context.
File Router covers route shapes, captured parameters, and virtual routes.
Pages documents the rules behind pages/.
Pages Reference lists every public API on next.pages.