or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-interface.mdapi.mdcontent-fields.mdcontrib.mdindex.mdmedia.mdpage-models.mdsearch.mdsystem-integration.mdtemplates.mdworkflows.md

page-models.mddocs/

0

# Page Models and Content Structure

1

2

Core page functionality providing the foundation for all content in Wagtail. The Page model hierarchy enables tree-based content organization with powerful inheritance and customization capabilities.

3

4

## Capabilities

5

6

### Base Page Model

7

8

The core Page model provides the foundation for all pages in Wagtail, including hierarchical structure, URL routing, publishing workflow, and content management.

9

10

```python { .api }

11

class Page(

12

WorkflowMixin,

13

PreviewableMixin,

14

DraftStateMixin,

15

LockableMixin,

16

RevisionMixin,

17

TranslatableMixin,

18

SpecificMixin,

19

MP_Node,

20

index.Indexed,

21

ClusterableModel

22

):

23

"""

24

Base page model with full CMS functionality.

25

26

Core Fields:

27

title (str): Page title

28

draft_title (str): Draft version of title (auto-managed)

29

slug (str): URL slug for the page

30

content_type (ForeignKey): Content type for polymorphic behavior

31

url_path (str): Full URL path from site root (auto-managed)

32

seo_title (str): SEO-optimized title

33

search_description (str): Meta description for search engines

34

show_in_menus (bool): Whether page appears in navigation

35

owner (User): Page owner for permissions

36

37

Publishing Fields (from DraftStateMixin):

38

live (bool): Whether page is published and visible

39

has_unpublished_changes (bool): Whether there are unpublished changes

40

first_published_at (datetime): When page was first published

41

last_published_at (datetime): When page was last published

42

latest_revision_created_at (datetime): When latest revision was created

43

go_live_at (datetime): Scheduled publish time

44

expire_at (datetime): Scheduled expiration time

45

expired (bool): Whether page has expired

46

live_revision (ForeignKey): Currently live revision

47

48

Locking Fields (from LockableMixin):

49

locked (bool): Whether page is locked for editing

50

locked_by (User): User who locked the page

51

locked_at (datetime): When page was locked

52

53

Revision Fields (from RevisionMixin):

54

latest_revision (ForeignKey): Most recent revision

55

56

Translation Fields (from TranslatableMixin):

57

locale (ForeignKey): Language/locale for this page

58

translation_key (UUID): Links translations together

59

60

Tree Fields (from MP_Node):

61

path (str): Tree path for hierarchical queries

62

depth (int): Tree depth level

63

numchild (int): Number of direct children

64

65

Other Fields:

66

alias_of (ForeignKey): Original page if this is an alias

67

"""

68

# Core fields

69

title: str

70

draft_title: str

71

slug: str

72

content_type: ContentType

73

url_path: str

74

seo_title: str

75

search_description: str

76

show_in_menus: bool

77

owner: User

78

79

# Publishing state (DraftStateMixin)

80

live: bool

81

has_unpublished_changes: bool

82

first_published_at: datetime

83

last_published_at: datetime

84

latest_revision_created_at: datetime

85

go_live_at: datetime

86

expire_at: datetime

87

expired: bool

88

live_revision: Revision

89

90

# Locking (LockableMixin)

91

locked: bool

92

locked_by: User

93

locked_at: datetime

94

95

# Revisions (RevisionMixin)

96

latest_revision: Revision

97

98

# Translation (TranslatableMixin)

99

locale: Locale

100

translation_key: UUID

101

102

# Tree structure (MP_Node)

103

path: str

104

depth: int

105

numchild: int

106

107

# Aliases

108

alias_of: Page

109

110

# URL and routing methods

111

def get_url(self, request=None, current_site=None):

112

"""Get the URL for this page."""

113

114

def get_full_url(self, request=None):

115

"""Get the absolute URL including domain for this page."""

116

117

def route(self, request, path_components):

118

"""Route a request to this page or its descendants."""

119

120

def set_url_path(self, parent):

121

"""Update the url_path field based on this page's slug and parent."""

122

123

@classmethod

124

def route_for_request(cls, request, path):

125

"""Find the page to serve for the given request path."""

126

127

@classmethod

128

def find_for_request(cls, request, path):

129

"""Find page matching the request, returns (page, args, kwargs)."""

130

131

# Content serving methods

132

def serve(self, request):

133

"""Serve this page for the given request."""

134

135

def get_context(self, request):

136

"""Get template context for rendering this page."""

137

138

def get_template(self, request, *args, **kwargs):

139

"""Get template for rendering this page."""

140

141

# Admin interface methods

142

def get_admin_display_title(self):

143

"""Get title for display in admin interface."""

144

145

def get_admin_base_path(self):

146

"""Get the base path for admin URLs for this page."""

147

148

# Lifecycle methods

149

def save(self, *args, **kwargs):

150

"""Save the page, updating URL paths and search index."""

151

152

def delete(self):

153

"""Delete the page and all its descendants."""

154

155

def clean(self):

156

"""Validate the page before saving."""

157

158

# Content management methods

159

def copy(self, recursive=False, to=None, update_attrs=None, copy_revisions=True):

160

"""Create a copy of this page."""

161

162

def move(self, target, pos=None):

163

"""Move this page to a new location in the tree."""

164

165

def update_aliases(self, *, revision=None, _content_json=None, user=None):

166

"""Update any aliases of this page with new content."""

167

168

# Tree traversal methods

169

def get_children(self):

170

"""Get direct children of this page."""

171

172

def get_descendants(self, inclusive=False):

173

"""Get all descendants of this page."""

174

175

def get_ancestors(self, inclusive=False):

176

"""Get all ancestors of this page."""

177

178

def get_siblings(self, inclusive=False):

179

"""Get sibling pages at the same level."""

180

181

def is_site_root(self):

182

"""Check if this page is the root of a site."""

183

184

# Validation methods

185

@classmethod

186

def can_exist_under(cls, parent):

187

"""Check if this page type can be created under the parent."""

188

189

@classmethod

190

def can_create_at(cls, parent):

191

"""Check if this page type can be created under the parent."""

192

193

def can_move_to(self, parent):

194

"""Check if this page can be moved under the parent."""

195

196

@classmethod

197

def allowed_parent_page_models(cls):

198

"""Get allowed parent page models for this page type."""

199

200

@classmethod

201

def allowed_subpage_models(cls):

202

"""Get allowed subpage models under this page type."""

203

204

# Permission methods

205

def permissions_for_user(self, user):

206

"""Get permission tester for the given user."""

207

208

def get_view_restrictions(self):

209

"""Get view restrictions applying to this page."""

210

211

# Workflow methods

212

def get_workflow(self):

213

"""Get the workflow assigned to this page."""

214

215

# SEO and sitemap methods

216

def get_sitemap_urls(self, request=None):

217

"""Get URLs for XML sitemap generation."""

218

219

# Caching methods

220

def get_cache_key_components(self):

221

"""Get components for generating cache keys."""

222

223

# HTTP method validation

224

@classmethod

225

def allowed_http_method_names(cls):

226

"""Get allowed HTTP methods for this page type."""

227

```

228

229

### Model Mixins

230

231

Mixins that provide specific functionality for custom models and pages.

232

233

```python { .api }

234

class RevisionMixin:

235

"""

236

Adds revision capabilities to models.

237

238

Methods provide version control and content history.

239

"""

240

def save_revision(self, user=None, approved_go_live_at=None, changed=True, log_action=False, previous_revision=None, clean=True):

241

"""

242

Save a new revision of this object.

243

244

Parameters:

245

user: User who made the changes

246

approved_go_live_at: When changes should go live

247

changed: Whether content has changed

248

log_action: Whether to log this action

249

previous_revision: Previous revision for comparison

250

clean: Whether to clean the object before saving

251

"""

252

253

def get_latest_revision(self):

254

"""Get the most recent revision."""

255

256

def get_latest_revision_as_object(self):

257

"""Get the content of the latest revision as an object."""

258

259

class DraftStateMixin:

260

"""

261

Adds draft/live state management to models.

262

263

Provides publishing workflow capabilities.

264

"""

265

live: bool

266

has_unpublished_changes: bool

267

first_published_at: datetime

268

last_published_at: datetime

269

go_live_at: datetime

270

expire_at: datetime

271

expired: bool

272

live_revision: Revision

273

274

def publish(self, revision=None, user=None):

275

"""Publish this content, making it live."""

276

277

def unpublish(self, set_expired=False, commit=True, user=None):

278

"""Remove this content from live site."""

279

280

def get_latest_revision_as_object(self):

281

"""Get latest revision content as object (overrides RevisionMixin)."""

282

283

def get_scheduled_revision_as_object(self):

284

"""Get scheduled revision if one exists."""

285

286

@property

287

def status_string(self):

288

"""Get human-readable status for admin display."""

289

290

class LockableMixin:

291

"""

292

Adds content locking functionality to prevent concurrent editing.

293

"""

294

locked: bool

295

locked_by: User

296

locked_at: datetime

297

298

def get_lock(self):

299

"""Get lock object if this content is locked."""

300

301

def with_content_json(self, content):

302

"""Return a copy with the given content restored."""

303

304

class WorkflowMixin:

305

"""

306

Adds workflow support for approval processes.

307

"""

308

@property

309

def current_workflow_task_state(self):

310

"""Get current workflow task state."""

311

312

@property

313

def current_workflow_task(self):

314

"""Get current workflow task."""

315

316

@property

317

def workflow_states(self):

318

"""Get all workflow states for this content."""

319

320

@classmethod

321

def get_default_workflow(cls):

322

"""Get default workflow for this content type."""

323

324

def get_workflow(self):

325

"""Get the workflow assigned to this content."""

326

327

class PreviewableMixin:

328

"""

329

Adds preview functionality for draft content.

330

"""

331

@property

332

def preview_modes(self):

333

"""Get available preview modes."""

334

335

@property

336

def preview_sizes(self):

337

"""Get available preview sizes."""

338

339

def serve_preview(self, request, mode_name):

340

"""Serve a preview of this content."""

341

342

def make_preview_request(self, request=None, mode_name=''):

343

"""Create a fake request for previewing content."""

344

345

def get_preview_context(self, request, mode_name):

346

"""Get context for preview rendering."""

347

348

def get_preview_template(self, request, mode_name):

349

"""Get template for preview rendering."""

350

351

def is_previewable(self):

352

"""Check if this content can be previewed."""

353

354

class TranslatableMixin:

355

"""

356

Adds translation support for multi-language content.

357

"""

358

locale: Locale

359

translation_key: UUID

360

361

def get_translations(self, inclusive=False):

362

"""Get all translations of this content."""

363

364

def copy_for_translation(self, locale):

365

"""Create a copy for translation to another locale."""

366

```

367

368

### Content Management Models

369

370

Models for managing content lifecycle, organization, and access control.

371

372

```python { .api }

373

class Revision:

374

"""

375

Represents a saved version of page or model content.

376

"""

377

content_object: Model

378

base_content_type: ContentType

379

object_str: str

380

created_at: datetime

381

user: User

382

approved_go_live_at: datetime

383

384

def as_object(self):

385

"""Return the content of this revision as a model instance."""

386

387

def publish(self):

388

"""Publish this revision, making it live."""

389

390

def is_latest_revision(self):

391

"""Check if this is the latest revision."""

392

393

def get_previous(self):

394

"""Get the previous revision."""

395

396

def get_next(self):

397

"""Get the next revision."""

398

399

class Collection:

400

"""

401

Organizes media and content with hierarchical permissions.

402

"""

403

name: str

404

path: str

405

406

def get_descendants(self, inclusive=False):

407

"""Get all descendant collections."""

408

409

def get_ancestors(self, inclusive=False):

410

"""Get all ancestor collections."""

411

412

def get_view_restrictions(self):

413

"""Get view restrictions for this collection."""

414

415

class Site:

416

"""

417

Represents a website with its own domain and root page.

418

"""

419

hostname: str

420

port: int

421

site_name: str

422

root_page: Page

423

root_url: str

424

425

@classmethod

426

def find_for_request(cls, request):

427

"""Find the Site object for the given HTTP request."""

428

429

@classmethod

430

def get_site_root_paths(cls):

431

"""Get URL paths for all site root pages."""

432

433

class Locale:

434

"""

435

Represents a language/region combination for internationalization.

436

"""

437

language_code: str

438

region_code: str

439

440

@classmethod

441

def get_default(cls):

442

"""Get the default locale for the site."""

443

444

@classmethod

445

def get_active(cls):

446

"""Get all active locales."""

447

```

448

449

### Permission Models

450

451

Models for managing user permissions and access control.

452

453

```python { .api }

454

class GroupPagePermission:

455

"""

456

Assigns page permissions to user groups.

457

"""

458

group: Group

459

page: Page

460

permission: Permission

461

462

objects: GroupPagePermissionManager

463

464

class PageViewRestriction:

465

"""

466

Restricts page viewing to specific users or groups.

467

"""

468

page: Page

469

restriction_type: str # 'password', 'groups', 'login'

470

password: str

471

groups: ManyToManyField[Group]

472

473

def save(self, **kwargs):

474

"""Save with audit logging."""

475

476

def delete(self, **kwargs):

477

"""Delete with audit logging."""

478

479

class PagePermissionTester:

480

"""

481

Utility class for testing user permissions on pages.

482

"""

483

def __init__(self, user, page):

484

"""Initialize with user and page to test."""

485

486

def can_edit(self):

487

"""Check if user can edit the page."""

488

489

def can_delete(self):

490

"""Check if user can delete the page."""

491

492

def can_publish(self):

493

"""Check if user can publish the page."""

494

495

def can_unpublish(self):

496

"""Check if user can unpublish the page."""

497

```

498

499

## Usage Examples

500

501

### Creating Custom Page Models

502

503

```python

504

from wagtail.models import Page

505

from wagtail.fields import RichTextField

506

from wagtail.admin.panels import FieldPanel

507

from django.db import models

508

509

class BlogPage(Page):

510

"""Custom page model for blog posts."""

511

date = models.DateField("Post date")

512

intro = models.CharField(max_length=250)

513

body = RichTextField(blank=True)

514

515

content_panels = Page.content_panels + [

516

FieldPanel('date'),

517

FieldPanel('intro'),

518

FieldPanel('body'),

519

]

520

521

def get_context(self, request):

522

context = super().get_context(request)

523

context['recent_posts'] = BlogPage.objects.live().order_by('-date')[:5]

524

return context

525

```

526

527

### Working with Page Hierarchy

528

529

```python

530

# Get all pages under a section

531

section_page = Page.objects.get(slug='news')

532

news_articles = section_page.get_children().live().order_by('-first_published_at')

533

534

# Move a page to a new parent

535

page_to_move = Page.objects.get(slug='old-location')

536

new_parent = Page.objects.get(slug='new-section')

537

page_to_move.move(new_parent, pos='last-child')

538

539

# Copy a page with all its content

540

original_page = Page.objects.get(slug='template-page')

541

copied_page = original_page.copy(

542

recursive=True, # Copy child pages too

543

update_attrs={'title': 'New Page Title', 'slug': 'new-page-slug'}

544

)

545

```

546

547

### Using Mixins for Custom Models

548

549

```python

550

from wagtail.models import RevisionMixin, DraftStateMixin

551

from django.db import models

552

553

class Article(RevisionMixin, DraftStateMixin, models.Model):

554

"""Custom model with revision and publishing capabilities."""

555

title = models.CharField(max_length=255)

556

content = models.TextField()

557

558

def save(self, *args, **kwargs):

559

super().save(*args, **kwargs)

560

# Save revision after saving the model

561

self.save_revision()

562

563

def go_live(self):

564

"""Publish this article."""

565

self.live = True

566

self.save()

567

self.publish()

568

```