Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
a90f1514
Commit
a90f1514
authored
Mar 19, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored model admin.
parent
4049b93a
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
275 additions
and
220 deletions
+275
-220
TODO.txt
TODO.txt
+3
-2
simple.py
examples/sqla/simple.py
+9
-3
base.py
flask_adminex/base.py
+40
-35
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+41
-179
model.py
flask_adminex/model.py
+180
-0
master.html
flask_adminex/templates/admin/master.html
+2
-1
No files found.
TODO.txt
View file @
a90f1514
- Core
- Core
- Pregenerate URLs for menu
- Pregenerate URLs for menu
- Page titles
- Page titles
- Create base model-admin class and derive SQLA admin from it
- SQLA Model Admin
- SQLA Model Admin
- Ability to override displayed columns in a list
- Ability to override displayed form fields
- Ability to override displayed form fields
- Ability to provide form validators without messing with form creation
- Ability to rename list columns
- Ability to change form without messing with form creation
- Filtering
- Filtering
- Many2Many editing
- Many2Many editing
- Many2One editor
- Many2One editor
...
...
examples/sqla/simple.py
View file @
a90f1514
...
@@ -44,11 +44,17 @@ def index():
...
@@ -44,11 +44,17 @@ def index():
return
'<a href="/admin/">Click me to get to Admin!</a>'
return
'<a href="/admin/">Click me to get to Admin!</a>'
class
PostAdmin
(
sqlamodel
.
ModelView
):
list_columns
=
(
'title'
,
'user'
)
def
__init__
(
self
,
session
):
super
(
PostAdmin
,
self
)
.
__init__
(
Post
,
session
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
# Create admin
# Create admin
admin
=
adminex
.
Admin
()
admin
=
adminex
.
Admin
(
'Simple Models'
)
admin
.
add_view
(
sqlamodel
.
ModelView
(
User
,
db
.
session
,
category
=
'News'
))
admin
.
add_view
(
sqlamodel
.
ModelView
(
User
,
db
.
session
))
admin
.
add_view
(
sqlamodel
.
ModelView
(
Post
,
db
.
session
,
category
=
'News'
))
admin
.
add_view
(
PostAdmin
(
db
.
session
))
admin
.
apply
(
app
)
admin
.
apply
(
app
)
# Create DB
# Create DB
...
...
flask_adminex/base.py
View file @
a90f1514
...
@@ -117,53 +117,58 @@ class AdminIndexView(BaseView):
...
@@ -117,53 +117,58 @@ class AdminIndexView(BaseView):
return
render_template
(
'admin/index.html'
,
view
=
self
)
return
render_template
(
'admin/index.html'
,
view
=
self
)
class
Admin
(
object
):
class
MenuItem
(
object
):
class
MenuItem
(
object
):
def
__init__
(
self
,
name
,
view
=
None
):
def
__init__
(
self
,
name
,
view
=
None
):
self
.
name
=
name
self
.
name
=
name
self
.
_view
=
view
self
.
_view
=
view
self
.
_children
=
[]
self
.
_children
=
[]
self
.
_children_urls
=
set
()
self
.
_children_urls
=
set
()
self
.
url
=
None
self
.
url
=
None
if
view
is
not
None
:
if
view
is
not
None
:
self
.
url
=
view
.
url
self
.
url
=
view
.
url
def
add_child
(
self
,
view
):
def
add_child
(
self
,
view
):
self
.
_children
.
append
(
view
)
self
.
_children
.
append
(
view
)
self
.
_children_urls
.
add
(
view
.
url
)
self
.
_children_urls
.
add
(
view
.
url
)
def
get_url
(
self
):
def
get_url
(
self
):
if
self
.
_view
is
None
:
if
self
.
_view
is
None
:
return
None
return
None
return
url_for
(
'
%
s.
%
s'
%
(
self
.
_view
.
endpoint
,
self
.
_view
.
_default_view
))
return
url_for
(
'
%
s.
%
s'
%
(
self
.
_view
.
endpoint
,
self
.
_view
.
_default_view
))
def
is_active
(
self
,
view
):
def
is_active
(
self
,
view
):
if
view
==
self
.
_view
:
if
view
==
self
.
_view
:
return
True
return
True
return
view
.
url
in
self
.
_children_urls
return
view
.
url
in
self
.
_children_urls
def
is_accessible
(
self
):
if
self
.
_view
is
None
:
return
False
def
is_accessible
(
self
):
return
self
.
_view
.
is_accessible
()
if
self
.
_view
is
None
:
return
False
return
self
.
_view
.
is_accessible
()
def
is_category
(
self
):
return
self
.
_view
is
None
def
is_category
(
self
):
def
get_children
(
self
):
return
self
.
_view
is
None
return
[
c
for
c
in
self
.
_children
if
c
.
is_accessible
()]
def
get_children
(
self
):
def
__repr__
(
self
):
return
[
c
for
c
in
self
.
_children
if
c
.
is_accessible
()]
return
'MenuItem
%
s (
%
s)'
%
(
self
.
name
,
repr
(
self
.
_children
))
def
__repr__
(
self
):
return
'MenuItem
%
s (
%
s)'
%
(
self
.
name
,
repr
(
self
.
_children
))
def
__init__
(
self
,
index_view
=
None
):
class
Admin
(
object
):
def
__init__
(
self
,
name
=
None
,
index_view
=
None
):
self
.
_views
=
[]
self
.
_views
=
[]
self
.
_menu
=
[]
self
.
_menu
=
[]
if
name
is
None
:
name
=
'Flask'
self
.
name
=
name
if
index_view
is
None
:
if
index_view
is
None
:
index_view
=
AdminIndexView
()
index_view
=
AdminIndexView
()
...
@@ -189,16 +194,16 @@ class Admin(object):
...
@@ -189,16 +194,16 @@ class Admin(object):
for
v
in
self
.
_views
:
for
v
in
self
.
_views
:
if
v
.
category
is
None
:
if
v
.
category
is
None
:
self
.
_menu
.
append
(
self
.
MenuItem
(
v
.
name
,
v
))
self
.
_menu
.
append
(
MenuItem
(
v
.
name
,
v
))
else
:
else
:
category
=
categories
.
get
(
v
.
category
)
category
=
categories
.
get
(
v
.
category
)
if
category
is
None
:
if
category
is
None
:
category
=
self
.
MenuItem
(
v
.
category
)
category
=
MenuItem
(
v
.
category
)
categories
[
v
.
category
]
=
category
categories
[
v
.
category
]
=
category
self
.
_menu
.
append
(
category
)
self
.
_menu
.
append
(
category
)
category
.
add_child
(
self
.
MenuItem
(
v
.
name
,
v
))
category
.
add_child
(
MenuItem
(
v
.
name
,
v
))
def
menu
(
self
):
def
menu
(
self
):
return
self
.
_menu
return
self
.
_menu
flask_adminex/ext/sqlamodel.py
View file @
a90f1514
from
sqlalchemy.orm.properties
import
RelationshipProperty
,
ColumnProperty
from
sqlalchemy.orm.properties
import
RelationshipProperty
,
ColumnProperty
from
sqlalchemy.orm.interfaces
import
MANYTOONE
from
sqlalchemy.orm.interfaces
import
MANYTOONE
from
sqlalchemy.sql.expression
import
desc
from
sqlalchemy.sql.expression
import
desc
from
sqlalchemy
import
exc
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
from
flask
import
request
,
render_template
,
url_for
,
redirect
,
flash
from
flask
import
flash
from
flaskext
import
wtf
from
flaskext
import
wtf
from
flask.ext.adminex
import
BaseView
,
expose
from
flask.ext.adminex
.model
import
BaseModelView
class
AdminModelConverter
(
ModelConverter
):
class
AdminModelConverter
(
ModelConverter
):
...
@@ -46,73 +45,62 @@ class AdminModelConverter(ModelConverter):
...
@@ -46,73 +45,62 @@ class AdminModelConverter(ModelConverter):
return
super
(
AdminModelConverter
,
self
)
.
convert
(
model
,
mapper
,
prop
,
field_args
)
return
super
(
AdminModelConverter
,
self
)
.
convert
(
model
,
mapper
,
prop
,
field_args
)
class
ModelView
(
BaseView
):
class
ModelView
(
BaseModelView
):
# Permissions
can_create
=
True
can_edit
=
True
can_delete
=
True
# Templates
list_template
=
'admin/model/list.html'
edit_template
=
'admin/model/edit.html'
create_template
=
'admin/model/edit.html'
# Various settings
page_size
=
3
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
# If name not provided, it is modelname
if
name
is
None
:
name
=
'
%
s'
%
self
.
_prettify_name
(
model
.
__name__
)
# If endpoint not provided, it is modelname + 'view'
if
endpoint
is
None
:
endpoint
=
(
'
%
sview'
%
model
.
__name__
)
.
lower
()
super
(
ModelView
,
self
)
.
__init__
(
name
,
category
,
endpoint
,
url
)
self
.
session
=
session
self
.
session
=
session
self
.
model
=
model
# Scaffolding
super
(
ModelView
,
self
)
.
__init__
(
model
,
name
,
category
,
endpoint
,
url
)
self
.
list_columns
=
self
.
get_list_columns
()
self
.
create_form
=
self
.
scaffold_create_form
()
self
.
edit_form
=
self
.
scaffold_edit_form
()
# Public API
# Public API
def
get_list_columns
(
self
):
def
get_list_columns
(
self
):
mapper
=
self
.
model
.
_sa_class_manager
.
mapper
columns
=
self
.
list_columns
columns
=
[]
if
columns
is
None
:
mapper
=
self
.
model
.
_sa_class_manager
.
mapper
for
p
in
mapper
.
iterate_properties
:
columns
=
[]
if
isinstance
(
p
,
RelationshipProperty
):
if
p
.
direction
is
MANYTOONE
:
columns
.
append
(
p
.
key
)
elif
isinstance
(
p
,
ColumnProperty
):
# TODO: Check for multiple columns
column
=
p
.
columns
[
0
]
if
column
.
foreign_keys
or
column
.
primary_key
:
for
p
in
mapper
.
iterate_properties
:
continue
if
isinstance
(
p
,
RelationshipProperty
):
if
p
.
direction
is
MANYTOONE
:
columns
.
append
(
p
.
key
)
elif
isinstance
(
p
,
ColumnProperty
):
# TODO: Check for multiple columns
column
=
p
.
columns
[
0
]
if
column
.
foreign_keys
or
column
.
primary_key
:
continue
columns
.
append
(
p
.
key
)
columns
.
append
(
p
.
key
)
return
[(
c
,
self
.
prettify_name
(
c
))
for
c
in
columns
]
return
[(
c
,
self
.
prettify_name
(
c
))
for
c
in
columns
]
def
scaffold_form
(
self
):
def
scaffold_form
(
self
):
return
model_form
(
self
.
model
,
wtf
.
Form
,
None
,
converter
=
AdminModelConverter
(
self
.
session
))
return
model_form
(
self
.
model
,
wtf
.
Form
,
self
.
form_columns
,
converter
=
AdminModelConverter
(
self
.
session
))
def
scaffold_create_form
(
self
):
# Database-related API
return
self
.
scaffold_form
()
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
):
query
=
self
.
session
.
query
(
self
.
model
)
def
scaffold_edit_form
(
self
):
count
=
query
.
count
()
return
self
.
scaffold_form
()
# Database-related API
if
sort_column
is
not
None
:
def
get_query
(
self
):
# Validate first
return
self
.
session
.
query
(
self
.
model
)
if
sort_column
>=
0
and
sort_column
<
len
(
self
.
list_columns
):
if
sort_desc
:
query
=
query
.
order_by
(
desc
(
self
.
list_columns
[
sort_column
][
0
]))
else
:
query
=
query
.
order_by
(
self
.
list_columns
[
sort_column
][
0
])
if
page
is
not
None
:
if
page
<
1
:
page
=
1
query
=
query
.
offset
((
page
-
1
)
*
self
.
page_size
)
query
=
query
.
limit
(
self
.
page_size
)
return
count
,
query
.
all
()
def
get_one
(
self
,
id
):
def
get_one
(
self
,
id
):
return
self
.
session
.
query
(
self
.
model
)
.
get
(
id
)
return
self
.
session
.
query
(
self
.
model
)
.
get
(
id
)
...
@@ -126,7 +114,6 @@ class ModelView(BaseView):
...
@@ -126,7 +114,6 @@ class ModelView(BaseView):
self
.
session
.
commit
()
self
.
session
.
commit
()
return
True
return
True
except
Exception
,
ex
:
except
Exception
,
ex
:
# TODO: Error logging
flash
(
'Failed to create model. '
+
str
(
ex
),
'error'
)
flash
(
'Failed to create model. '
+
str
(
ex
),
'error'
)
return
False
return
False
...
@@ -142,128 +129,3 @@ class ModelView(BaseView):
...
@@ -142,128 +129,3 @@ class ModelView(BaseView):
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
self
.
session
.
delete
(
model
)
self
.
session
.
delete
(
model
)
self
.
session
.
commit
()
self
.
session
.
commit
()
# Various helpers
def
prettify_name
(
self
,
name
):
return
' '
.
join
(
x
.
capitalize
()
for
x
in
name
.
split
(
'_'
))
# URL generation helper
def
_get_extra_args
(
self
):
page
=
request
.
args
.
get
(
'page'
,
None
,
type
=
int
)
sort
=
request
.
args
.
get
(
'sort'
,
None
,
type
=
int
)
sort_desc
=
request
.
args
.
get
(
'desc'
,
None
,
type
=
int
)
return
page
,
sort
,
sort_desc
def
_get_url
(
self
,
view
,
page
,
sort
,
sort_desc
):
return
url_for
(
view
,
page
=
page
,
sort
=
sort
,
desc
=
sort_desc
)
# Views
@
expose
(
'/'
)
def
index_view
(
self
):
data
=
self
.
get_query
()
page
,
sort
,
sort_desc
=
self
.
_get_extra_args
()
# Sorting
if
sort
is
not
None
:
# Validate first
if
sort
>=
0
and
sort
<
len
(
self
.
list_columns
):
if
sort_desc
:
data
=
data
.
order_by
(
desc
(
self
.
list_columns
[
sort
][
0
]))
else
:
data
=
data
.
order_by
(
self
.
list_columns
[
sort
][
0
])
# Paging
count
=
data
.
count
()
num_pages
=
count
/
self
.
page_size
if
count
%
self
.
page_size
!=
0
:
num_pages
+=
1
if
page
is
not
None
:
if
page
<
1
:
page
=
1
data
=
data
.
offset
((
page
-
1
)
*
self
.
page_size
)
data
=
data
.
limit
(
self
.
page_size
)
# Various URL generation helpers
def
pager_url
(
p
):
return
self
.
_get_url
(
'.index_view'
,
p
,
sort
,
sort_desc
)
def
sort_url
(
column
,
invert
=
False
):
desc
=
None
if
invert
and
not
sort_desc
:
desc
=
1
return
self
.
_get_url
(
'.index_view'
,
page
,
column
,
desc
)
return
render_template
(
self
.
list_template
,
view
=
self
,
data
=
data
,
# Return URL
return_url
=
self
.
_get_url
(
'.index_view'
,
page
,
sort
,
sort_desc
),
# Pagination
pager_url
=
pager_url
,
num_pages
=
num_pages
,
page
=
page
,
# Sorting
sort_column
=
sort
,
sort_desc
=
sort_desc
,
sort_url
=
sort_url
)
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
def
create_view
(
self
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_create
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
create_form
(
request
.
form
)
if
form
.
validate_on_submit
():
if
self
.
create_model
(
form
):
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
create_template
,
view
=
self
,
form
=
form
)
@
expose
(
'/edit/<int:id>/'
,
methods
=
(
'GET'
,
'POST'
))
def
edit_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_edit
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
if
model
is
None
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
edit_form
(
request
.
form
,
model
)
if
form
.
validate_on_submit
():
if
self
.
update_model
(
form
,
model
):
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
edit_template
,
view
=
self
,
form
=
form
,
return_url
=
return_url
or
url_for
(
'.index_view'
))
@
expose
(
'/delete/<int:id>/'
)
def
delete_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
# TODO: Use post
if
not
self
.
can_delete
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
if
model
:
self
.
delete_model
(
model
)
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
flask_adminex/model.py
0 → 100644
View file @
a90f1514
from
flask
import
request
,
url_for
,
render_template
,
redirect
from
.base
import
BaseView
,
expose
class
BaseModelView
(
BaseView
):
# Permissions
can_create
=
True
can_edit
=
True
can_delete
=
True
# Templates
list_template
=
'admin/model/list.html'
edit_template
=
'admin/model/edit.html'
create_template
=
'admin/model/edit.html'
# Customizations
list_columns
=
None
form_columns
=
None
# Various settings
page_size
=
20
def
__init__
(
self
,
model
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
# If name not provided, it is modelname
if
name
is
None
:
name
=
'
%
s'
%
self
.
_prettify_name
(
model
.
__name__
)
# If endpoint not provided, it is modelname + 'view'
if
endpoint
is
None
:
endpoint
=
(
'
%
sview'
%
model
.
__name__
)
.
lower
()
super
(
BaseModelView
,
self
)
.
__init__
(
name
,
category
,
endpoint
,
url
)
self
.
model
=
model
# Scaffolding
self
.
list_columns
=
self
.
get_list_columns
()
self
.
create_form
=
self
.
scaffold_create_form
()
self
.
edit_form
=
self
.
scaffold_edit_form
()
# Public API
def
get_list_columns
(
self
):
raise
NotImplemented
(
'Please implement get_list_columns method'
)
def
scaffold_form
(
self
):
raise
NotImplemented
(
'Please implement scaffold_form method'
)
def
scaffold_create_form
(
self
):
return
self
.
scaffold_form
()
def
scaffold_edit_form
(
self
):
return
self
.
scaffold_form
()
# Database-related API
def
get_list
(
self
,
page
,
sort_field
,
sort_desc
):
raise
NotImplemented
(
'Please implement get_list method'
)
def
get_one
(
self
,
id
):
raise
NotImplemented
(
'Please implement get_one method'
)
# Model handlers
def
create_model
(
self
,
form
):
raise
NotImplemented
()
def
update_model
(
self
,
form
,
model
):
raise
NotImplemented
()
def
delete_model
(
self
,
model
):
raise
NotImplemented
()
# Various helpers
def
prettify_name
(
self
,
name
):
return
' '
.
join
(
x
.
capitalize
()
for
x
in
name
.
split
(
'_'
))
# URL generation helper
def
_get_extra_args
(
self
):
page
=
request
.
args
.
get
(
'page'
,
None
,
type
=
int
)
sort
=
request
.
args
.
get
(
'sort'
,
None
,
type
=
int
)
sort_desc
=
request
.
args
.
get
(
'desc'
,
None
,
type
=
int
)
return
page
,
sort
,
sort_desc
def
_get_url
(
self
,
view
,
page
,
sort
,
sort_desc
):
return
url_for
(
view
,
page
=
page
,
sort
=
sort
,
desc
=
sort_desc
)
# Views
@
expose
(
'/'
)
def
index_view
(
self
):
# Grab parameters from URL
page
,
sort
,
sort_desc
=
self
.
_get_extra_args
()
# Get count and data
count
,
data
=
self
.
get_list
(
page
,
sort
,
sort_desc
)
# Calculate number of pages
num_pages
=
count
/
self
.
page_size
if
count
%
self
.
page_size
!=
0
:
num_pages
+=
1
# Various URL generation helpers
def
pager_url
(
p
):
return
self
.
_get_url
(
'.index_view'
,
p
,
sort
,
sort_desc
)
def
sort_url
(
column
,
invert
=
False
):
desc
=
None
if
invert
and
not
sort_desc
:
desc
=
1
return
self
.
_get_url
(
'.index_view'
,
page
,
column
,
desc
)
return
render_template
(
self
.
list_template
,
view
=
self
,
data
=
data
,
# Return URL
return_url
=
self
.
_get_url
(
'.index_view'
,
page
,
sort
,
sort_desc
),
# Pagination
pager_url
=
pager_url
,
num_pages
=
num_pages
,
page
=
page
,
# Sorting
sort_column
=
sort
,
sort_desc
=
sort_desc
,
sort_url
=
sort_url
)
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
def
create_view
(
self
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_create
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
create_form
(
request
.
form
)
if
form
.
validate_on_submit
():
if
self
.
create_model
(
form
):
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
create_template
,
view
=
self
,
form
=
form
)
@
expose
(
'/edit/<int:id>/'
,
methods
=
(
'GET'
,
'POST'
))
def
edit_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_edit
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
if
model
is
None
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
edit_form
(
request
.
form
,
model
)
if
form
.
validate_on_submit
():
if
self
.
update_model
(
form
,
model
):
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
edit_template
,
view
=
self
,
form
=
form
,
return_url
=
return_url
or
url_for
(
'.index_view'
))
@
expose
(
'/delete/<int:id>/'
)
def
delete_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
# TODO: Use post
if
not
self
.
can_delete
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
if
model
:
self
.
delete_model
(
model
)
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
flask_adminex/templates/admin/master.html
View file @
a90f1514
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
<head>
<head>
<title>
{% block title %}
Flask ExtAdmin
{% endblock %}
</title>
<title>
{% block title %}
{% if view.category %}{{ view.category }} - {% endif %}{{ view.name }} - {{ view.admin.name }}
{% endblock %}
</title>
{% block head %}
{% block head %}
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"description"
content=
""
>
<meta
name=
"description"
content=
""
>
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
<div
class=
"navbar navbar-fixed-top"
>
<div
class=
"navbar navbar-fixed-top"
>
<div
class=
"navbar-inner"
>
<div
class=
"navbar-inner"
>
<div
class=
"container"
>
<div
class=
"container"
>
<span
class=
"brand"
>
{{ view.admin.name }}
</span>
<ul
class=
"nav"
>
<ul
class=
"nav"
>
{% for item in view.admin.menu() %}
{% for item in view.admin.menu() %}
{% if item.is_category() %}
{% if item.is_category() %}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment