Skip to content

Flask-Admin Interface

Admin panels are useful for:

  • managing users
  • managing content
  • viewing tables quickly

Django includes admin by default, Flask does not.

Flask-Admin provides a flexible admin UI for Flask apps.

bash
pip install Flask-Admin
python
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
 
admin = Admin(app, name="Admin", template_mode="bootstrap4")
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

Do not expose admin routes publicly without protection.

Common approaches:

  • restrict to admin users only
  • require login and role checks
  • hide admin behind VPN/internal network

Treat admin as a separate feature area:

  • its own blueprint/route prefix
  • strict access control
  • logging/auditing of admin actions
admin.py
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
 
admin = Admin(app, name="Demo")
admin.add_view(ModelView(User, db.session))

Measured — the rules that single add_view() registered:

app.url_map, filtered to /admin
/admin/
/admin/static/<path:filename>
/admin/user/
/admin/user/action/
/admin/user/ajax/lookup/
/admin/user/ajax/update/
/admin/user/delete/
/admin/user/details/
/admin/user/edit/
/admin/user/export/<export_type>/
/admin/user/new/

Eleven routes, including new, edit, delete and export, from one line. That is the appeal and the entire risk in the same breath.

diagram Diagram mermaid

Measured, with no login of any kind configured:

requeststatus
GET /admin/200
GET /admin/user/200

Anyone who can reach the URL can list, edit, export and delete every row. There is no default credential and no warning at startup.

columns.py
class User(db.Model):
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    secret_note: Mapped[str | None]
measured column_list
[('name', 'Name'), ('secret_note', 'Secret Note')]

secret_note was listed without being asked for. A password hash, an internal score, a personal note — all of it appears in the list view and in the CSV export unless you exclude it:

restrict.py
class UserView(SecureModelView):
    column_list = ("id", "name", "created_at")   # an allow-list
    column_exclude_list = ("pw_hash",)           # or a deny-list
    form_excluded_columns = ("pw_hash",)         # editing is separate from listing
    can_delete = False
    can_export = False

Note that column_list and form_excluded_columns are different settings: hiding a column from the list does not stop it being editable on the form.

good fitpoor fit
an internal back office for staff you trustanything a customer can reach
quickly inspecting data during developmenta user-facing account area
CRUD over a stable, simple schemaworkflows with business rules

It generates a UI directly from your models, so it enforces exactly the constraints the database has and none of the rules your application has. A user-facing screen wants validation, permissions and audit trails that a generated admin does not provide.

sketch What one add_view() exposes p5.js
A single line registers eleven routes with no authentication. Adding is_accessible is what turns it into an admin rather than an open door.
pch.quizTag pch.quizDefaultTitle
  1. What protects Flask-Admin views by default?

    pch.quizShowAnswer

    B — nothing; measured, GET /admin/ and /admin/user/ both returned 200 with no login configured — Since /admin/user/delete/ is among the eleven registered routes, an exposed admin is a full database compromise rather than an information leak.

  2. How many routes did a single admin.add_view(ModelView(User, db.session)) register?

    pch.quizShowAnswer

    C — 11 — Measured eleven, including new, edit, delete, export and two ajax endpoints. That leverage is both the appeal and the risk.

  3. A model has a pw_hash column. What happens to it in Flask-Admin by default?

    pch.quizShowAnswer

    B — it appears in the list view, the edit form and the CSV export unless you exclude it — Measured column_list included secret_note without being asked. Note that column_list and form_excluded_columns are separate settings — hiding a column from the list does not stop it being editable.

  4. Which method must you override to require a login for an admin view?

    pch.quizShowAnswer

    B — is_accessible — is_accessible guards every view in the class. inaccessible_callback only decides where a refused user is sent, and does nothing on its own.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading