or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions-hooks.mdcharts.mdcli-tools.mdconstants-exceptions.mdcore-framework.mddatabase-models.mdforms-fields.mdindex.mdrest-api.mdsecurity.mdviews-crud.md

views-crud.mddocs/

0

# Views and CRUD Operations

1

2

Model views with automatic CRUD operations, form generation, validation, pagination, searching, and filtering capabilities. The view system provides comprehensive web interfaces for database models with minimal configuration while supporting extensive customization.

3

4

## Capabilities

5

6

### ModelView Class

7

8

Complete CRUD view implementation providing list, show, add, edit, and delete operations with automatic form generation, validation, and security integration.

9

10

```python { .api }

11

from flask_appbuilder import ModelView

12

from flask_appbuilder.models.sqla.interface import SQLAInterface

13

from flask_appbuilder.baseviews import expose

14

from flask_appbuilder.security.decorators import has_access

15

16

class ModelView(BaseCRUDView):

17

"""

18

Complete CRUD view for SQLAlchemy models with automatic

19

form generation and comprehensive database operations.

20

"""

21

22

@expose('/list/')

23

@has_access

24

def list(self):

25

"""

26

List view with pagination, search, and filtering.

27

28

Returns:

29

Rendered list template with model data

30

"""

31

32

@expose('/show/<pk>')

33

@has_access

34

def show(self, pk):

35

"""

36

Show single model instance details.

37

38

Parameters:

39

- pk: Primary key value

40

41

Returns:

42

Rendered show template with model data

43

"""

44

45

@expose('/add/', methods=['GET', 'POST'])

46

@has_access

47

def add(self):

48

"""

49

Add new model instance with form.

50

51

Returns:

52

GET: Rendered add form template

53

POST: Redirect after successful creation or form with errors

54

"""

55

56

@expose('/edit/<pk>', methods=['GET', 'POST'])

57

@has_access

58

def edit(self, pk):

59

"""

60

Edit existing model instance with form.

61

62

Parameters:

63

- pk: Primary key value

64

65

Returns:

66

GET: Rendered edit form template

67

POST: Redirect after successful update or form with errors

68

"""

69

70

@expose('/delete/<pk>')

71

@has_access

72

def delete(self, pk):

73

"""

74

Delete model instance by primary key.

75

76

Parameters:

77

- pk: Primary key value

78

79

Returns:

80

Redirect to list view with flash message

81

"""

82

83

@expose('/download/<pk>')

84

@has_access

85

def download(self, pk):

86

"""

87

Download file associated with model instance.

88

89

Parameters:

90

- pk: Primary key value

91

92

Returns:

93

File download response or 404

94

"""

95

96

@expose('/action/', methods=['POST'])

97

@has_access

98

def action_post(self):

99

"""

100

Execute bulk actions on selected model instances.

101

102

Returns:

103

Redirect with action results

104

"""

105

106

# Complete ModelView configuration example

107

class PersonModelView(ModelView):

108

# Required: data model interface

109

datamodel = SQLAInterface(Person)

110

111

# Column configurations

112

list_columns = ['name', 'email', 'department', 'active', 'created_on']

113

show_columns = ['name', 'email', 'phone', 'department', 'active', 'created_on', 'updated_on']

114

add_columns = ['name', 'email', 'phone', 'department', 'active']

115

edit_columns = ['name', 'email', 'phone', 'department', 'active']

116

117

# Search configuration

118

search_columns = ['name', 'email']

119

120

# Column labels and descriptions

121

label_columns = {

122

'name': 'Full Name',

123

'email': 'Email Address',

124

'created_on': 'Created Date'

125

}

126

127

description_columns = {

128

'email': 'Contact email address for notifications',

129

'active': 'Whether the person is currently active'

130

}

131

132

# Ordering and pagination

133

order_columns = ['name', 'email', 'created_on']

134

page_size = 25

135

136

# Base filters (always applied)

137

base_filters = [['active', FilterEqual, True]]

138

139

# Form field sets for organization

140

add_fieldsets = [

141

('Personal Info', {'fields': ['name', 'email', 'phone']}),

142

('Organization', {'fields': ['department', 'active']})

143

]

144

145

edit_fieldsets = [

146

('Personal Info', {'fields': ['name', 'email', 'phone']}),

147

('Organization', {'fields': ['department', 'active']})

148

]

149

150

# Register view with AppBuilder

151

appbuilder.add_view(

152

PersonModelView,

153

"List Persons",

154

icon="fa-address-book-o",

155

category="People"

156

)

157

```

158

159

### BaseCRUDView Class

160

161

Foundation class for CRUD operations providing the core functionality that ModelView extends, with lifecycle hooks and customization points.

162

163

```python { .api }

164

from flask_appbuilder.baseviews import BaseCRUDView

165

166

class BaseCRUDView(BaseModelView):

167

"""

168

Base class for CRUD views providing core database operations

169

and customization hooks for the CRUD lifecycle.

170

"""

171

172

# Internal CRUD methods (override for customization)

173

def _list(self, **kwargs):

174

"""

175

Internal list view logic.

176

177

Parameters:

178

- **kwargs: Additional context variables

179

180

Returns:

181

Dict with view context data

182

"""

183

184

def _show(self, pk):

185

"""

186

Internal show view logic.

187

188

Parameters:

189

- pk: Primary key value

190

191

Returns:

192

Dict with view context data

193

"""

194

195

def _add(self):

196

"""

197

Internal add view logic.

198

199

Returns:

200

Dict with view context data or redirect response

201

"""

202

203

def _edit(self, pk):

204

"""

205

Internal edit view logic.

206

207

Parameters:

208

- pk: Primary key value

209

210

Returns:

211

Dict with view context data or redirect response

212

"""

213

214

def _delete(self, pk):

215

"""

216

Internal delete view logic.

217

218

Parameters:

219

- pk: Primary key value

220

221

Returns:

222

Redirect response with flash message

223

"""

224

225

# Lifecycle hooks

226

def pre_add(self, item):

227

"""

228

Hook called before adding new item to database.

229

230

Parameters:

231

- item: Model instance to be added

232

"""

233

234

def post_add(self, item):

235

"""

236

Hook called after successfully adding item.

237

238

Parameters:

239

- item: Added model instance

240

"""

241

242

def pre_update(self, item):

243

"""

244

Hook called before updating item in database.

245

246

Parameters:

247

- item: Model instance to be updated

248

"""

249

250

def post_update(self, item):

251

"""

252

Hook called after successfully updating item.

253

254

Parameters:

255

- item: Updated model instance

256

"""

257

258

def pre_delete(self, item):

259

"""

260

Hook called before deleting item from database.

261

262

Parameters:

263

- item: Model instance to be deleted

264

"""

265

266

def post_delete(self, item):

267

"""

268

Hook called after successfully deleting item.

269

270

Parameters:

271

- item: Deleted model instance

272

"""

273

274

def prefill_form(self, form, pk):

275

"""

276

Pre-fill edit form with additional data.

277

278

Parameters:

279

- form: WTForms form instance

280

- pk: Primary key of item being edited

281

"""

282

283

def process_form(self, form, is_created):

284

"""

285

Process form data before database operation.

286

287

Parameters:

288

- form: WTForms form instance with submitted data

289

- is_created: Boolean indicating if this is create (True) or update (False)

290

291

Returns:

292

Model instance with processed data

293

"""

294

295

# CRUD View configuration properties

296

datamodel = None # SQLAlchemy interface (required)

297

298

# View titles

299

list_title = "List" # List view title

300

show_title = "Show Record" # Show view title

301

add_title = "Add Record" # Add form title

302

edit_title = "Edit Record" # Edit form title

303

304

# Column configurations

305

list_columns = [] # Columns shown in list view

306

show_columns = [] # Columns shown in show view

307

add_columns = [] # Columns in add form

308

edit_columns = [] # Columns in edit form

309

show_exclude_columns = [] # Columns excluded from show

310

add_exclude_columns = [] # Columns excluded from add form

311

edit_exclude_columns = [] # Columns excluded from edit form

312

order_columns = [] # Columns available for sorting

313

314

# Pagination and display

315

page_size = 20 # Default page size

316

show_template = "appbuilder/general/model/show.html"

317

list_template = "appbuilder/general/model/list.html"

318

add_template = "appbuilder/general/model/add.html"

319

edit_template = "appbuilder/general/model/edit.html"

320

321

# Widget classes

322

list_widget = None # List widget class

323

show_widget = None # Show widget class

324

add_widget = None # Add form widget class

325

edit_widget = None # Edit form widget class

326

327

# Form configurations

328

show_fieldsets = [] # Field groupings for show view

329

add_fieldsets = [] # Field groupings for add form

330

edit_fieldsets = [] # Field groupings for edit form

331

description_columns = {} # Column descriptions

332

validators_columns = {} # Column validators

333

formatters_columns = {} # Column formatters

334

add_form_extra_fields = {} # Extra fields for add form

335

edit_form_extra_fields = {} # Extra fields for edit form

336

add_form_query_rel_fields = {} # Related field queries for add

337

edit_form_query_rel_fields = {} # Related field queries for edit

338

339

# Forms (auto-generated)

340

add_form = None # Add form instance

341

edit_form = None # Edit form instance

342

343

# Related views and actions

344

related_views = [] # Related ModelView classes

345

actions = {} # Available actions dict

346

```

347

348

### SimpleFormView Class

349

350

Basic form view for simple form processing without database model integration, useful for contact forms, settings, and other non-CRUD operations.

351

352

```python { .api }

353

from flask_appbuilder import SimpleFormView

354

from wtforms import Form, StringField, validators

355

from wtforms.validators import DataRequired

356

357

class SimpleFormView(BaseFormView):

358

"""Simple form handling without model integration."""

359

360

@expose('/', methods=['GET', 'POST'])

361

def this_form_get(self):

362

"""Handle GET request - display form."""

363

364

@expose('/form', methods=['POST'])

365

def this_form_post(self):

366

"""Handle POST request - process form."""

367

368

# Example: Contact form

369

class ContactForm(Form):

370

name = StringField('Name', [DataRequired()])

371

email = StringField('Email', [DataRequired(), validators.Email()])

372

message = StringField('Message', [DataRequired()])

373

374

class ContactFormView(SimpleFormView):

375

form = ContactForm

376

form_title = "Contact Us"

377

378

def form_post(self, form):

379

# Process the form submission

380

if form.validate():

381

name = form.name.data

382

email = form.email.data

383

message = form.message.data

384

385

# Send email, save to database, etc.

386

send_contact_email(name, email, message)

387

388

flash("Thank you for your message!", "success")

389

return redirect(url_for('ContactFormView.this_form_get'))

390

391

# Form validation failed - return form with errors

392

return self.render_template(

393

self.form_template,

394

form=form,

395

form_title=self.form_title

396

)

397

398

# Register form view

399

appbuilder.add_view(

400

ContactFormView,

401

"Contact",

402

icon="fa-envelope"

403

)

404

```

405

406

### IndexView Class

407

408

Default index/home page view providing the application landing page and navigation entry point.

409

410

```python { .api }

411

from flask_appbuilder import IndexView

412

413

class IndexView(BaseView):

414

"""Default index page view for Flask-AppBuilder applications."""

415

416

route_base = "" # Root route

417

default_view = "index" # Default method

418

index_template = "appbuilder/index.html" # Index template

419

420

@expose('/')

421

def index(self):

422

"""

423

Index page endpoint.

424

425

Returns:

426

Rendered index template

427

"""

428

429

# Custom index view example

430

class CustomIndexView(IndexView):

431

index_template = "my_index.html"

432

433

@expose('/')

434

def index(self):

435

# Get dashboard data

436

user_count = db.session.query(User).count()

437

recent_activity = get_recent_activity()

438

439

return self.render_template(

440

self.index_template,

441

user_count=user_count,

442

recent_activity=recent_activity

443

)

444

445

# Use custom index view

446

appbuilder = AppBuilder(app, db.session, indexview=CustomIndexView)

447

```

448

449

### PublicFormView Class

450

451

Form view accessible without authentication, useful for registration forms, public contact forms, and other publicly accessible functionality.

452

453

```python { .api }

454

from flask_appbuilder import PublicFormView

455

456

class PublicFormView(SimpleFormView):

457

"""Form view accessible without authentication."""

458

459

# No @has_access decorator - publicly accessible

460

# Inherits form processing from SimpleFormView

461

462

# Example: Public registration form

463

class RegistrationForm(Form):

464

username = StringField('Username', [DataRequired()])

465

email = StringField('Email', [DataRequired(), validators.Email()])

466

password = PasswordField('Password', [DataRequired()])

467

468

class PublicRegistrationView(PublicFormView):

469

form = RegistrationForm

470

form_title = "Register"

471

472

def form_post(self, form):

473

if form.validate():

474

username = form.username.data

475

email = form.email.data

476

password = form.password.data

477

478

# Create user account

479

user = User(username=username, email=email)

480

user.password = generate_password_hash(password)

481

db.session.add(user)

482

db.session.commit()

483

484

flash("Registration successful!", "success")

485

return redirect(url_for('AuthDBView.login'))

486

487

return self.render_template(

488

self.form_template,

489

form=form,

490

form_title=self.form_title

491

)

492

493

# Register public view (no authentication required)

494

appbuilder.add_view_no_menu(PublicRegistrationView)

495

```

496

497

### MasterDetailView Class

498

499

Master-detail view pattern for displaying related models in a hierarchical interface, showing a master record with its related detail records.

500

501

```python { .api }

502

from flask_appbuilder import MasterDetailView

503

504

class MasterDetailView(BaseView):

505

"""Master-detail view pattern for related models."""

506

507

# Master model configuration

508

datamodel = None # Master model interface

509

510

# Detail models configuration

511

related_views = [] # List of detail view classes

512

513

# Example: Order with Order Items

514

class OrderMasterDetailView(MasterDetailView):

515

datamodel = SQLAInterface(Order)

516

related_views = [OrderItemModelView]

517

518

list_columns = ['order_number', 'customer', 'total', 'order_date']

519

show_columns = ['order_number', 'customer', 'total', 'order_date', 'status']

520

521

class OrderItemModelView(ModelView):

522

datamodel = SQLAInterface(OrderItem)

523

list_columns = ['product', 'quantity', 'price', 'subtotal']

524

525

# Register master-detail view

526

appbuilder.add_view(

527

OrderMasterDetailView,

528

"Orders",

529

icon="fa-shopping-cart",

530

category="Sales"

531

)

532

```

533

534

### CompactCRUDMixin

535

536

Mixin providing compact CRUD operations with streamlined interface for simple models requiring minimal configuration.

537

538

```python { .api }

539

from flask_appbuilder import CompactCRUDMixin, ModelView

540

541

class CompactCRUDMixin:

542

"""Mixin for compact CRUD operations with simplified interface."""

543

544

# Example: Simple model with compact interface

545

class TagModelView(CompactCRUDMixin, ModelView):

546

datamodel = SQLAInterface(Tag)

547

list_columns = ['name', 'color']

548

add_columns = ['name', 'color']

549

edit_columns = ['name', 'color']

550

551

# Compact interface reduces UI complexity

552

page_size = 10

553

show_fieldsets = [

554

('Tag Info', {'fields': ['name', 'color'], 'expanded': True})

555

]

556

557

appbuilder.add_view(

558

TagModelView,

559

"Tags",

560

icon="fa-tag",

561

category="Admin"

562

)

563

```

564

565

### MultipleView Class

566

567

Container view for organizing multiple related views in a single interface, useful for creating dashboard-style pages or grouped functionality.

568

569

```python { .api }

570

from flask_appbuilder import MultipleView

571

572

class MultipleView(BaseView):

573

"""Multiple views in single interface."""

574

575

views = [] # List of view instances

576

577

# Example: Admin dashboard with multiple views

578

class AdminDashboardView(MultipleView):

579

views = [

580

{'view': UserStatsView(), 'size': 6}, # Half width

581

{'view': SystemHealthView(), 'size': 6}, # Half width

582

{'view': RecentActivityView(), 'size': 12} # Full width

583

]

584

585

appbuilder.add_view(

586

AdminDashboardView,

587

"Admin Dashboard",

588

icon="fa-dashboard",

589

category="Admin"

590

)

591

```