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
```