Commit 512301a9 authored by PJ Janse van Rensburg's avatar PJ Janse van Rensburg

Merge branch 'nested-categories'

parents 108cbd75 259e6363
...@@ -156,12 +156,9 @@ Customizing Built-in Views ...@@ -156,12 +156,9 @@ Customizing Built-in Views
**** ****
The built-in `ModelView` class is great for getting started quickly. But, you'll want When inheriting from `ModelView`, values can be specified for numerous
to configure its functionality to suit your particular models. This is done by setting configuration parameters. Use these to customize the views to suit your
values for the configuration attributes that are made available in the `ModelView` class. particular models::
To specify some global configuration parameters, you can subclass `ModelView` and use that
subclass when adding your models to the interface::
from flask_admin.contrib.sqla import ModelView from flask_admin.contrib.sqla import ModelView
...@@ -287,6 +284,28 @@ To **enable csv export** of the model view:: ...@@ -287,6 +284,28 @@ To **enable csv export** of the model view::
This will add a button to the model view that exports records, truncating at :attr:`~flask_admin.model.BaseModelView.export_max_rows`. This will add a button to the model view that exports records, truncating at :attr:`~flask_admin.model.BaseModelView.export_max_rows`.
Grouping Views
==============
When adding a view, specify a value for the `category` parameter
to group related views together in the menu::
admin.add_view(UserView(User, db.session, category="Team"))
admin.add_view(ModelView(Role, db.session, category="Team"))
admin.add_view(ModelView(Permission, db.session, category="Team"))
This will create a top-level menu item named 'Team', and a drop-down containing
links to the three views.
To nest related views within these drop-downs, use the `add_sub_category` method::
admin.add_sub_category(name="Links", parent_name="Team")
And to add arbitrary hyperlinks to the menu::
admin.add_link(MenuLink(name='Home Page', url='/', category='Links'))
Adding Your Own Views Adding Your Own Views
===================== =====================
......
This example shows how you can add links to external (non flask-admin) pages to the navbar menu, and how you can hide certain links if a user is not logged-in.
To run this example:
1. Clone the repository::
git clone https://github.com/flask-admin/flask-admin.git
cd flask-admin
2. Create and activate a virtual environment::
virtualenv env
source env/bin/activate
3. Install requirements::
pip install -r 'examples/menu-external-links/requirements.txt'
4. Run the application::
python examples/menu-external-links/app.py
from flask import Flask, redirect, url_for
from flask_login import current_user, UserMixin, login_user, logout_user, LoginManager
from flask_admin.base import MenuLink, Admin, BaseView, expose
# Create fake user class for authentication
class User(UserMixin):
users_id = 0
def __init__(self, id=None):
if not id:
self.users_id += 1
self.id = self.users_id
else:
self.id = id
# Create menu links classes with reloaded accessible
class AuthenticatedMenuLink(MenuLink):
def is_accessible(self):
return current_user.is_authenticated
class NotAuthenticatedMenuLink(MenuLink):
def is_accessible(self):
return not current_user.is_authenticated
# Create custom admin view for authenticated users
class MyAdminView(BaseView):
@expose('/')
def index(self):
return self.render('authenticated-admin.html')
def is_accessible(self):
return current_user.is_authenticated
# Create flask app
app = Flask(__name__, template_folder='templates')
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
@app.route('/login/')
def login_view():
login_user(User())
return redirect(url_for('admin.index'))
@app.route('/logout/')
def logout_view():
logout_user()
return redirect(url_for('admin.index'))
login_manager = LoginManager()
login_manager.init_app(app)
# Create user loader function
@login_manager.user_loader
def load_user(user_id):
return User(user_id)
if __name__ == '__main__':
# Create admin interface
admin = Admin(name='Example: Menu')
admin.add_view(MyAdminView(name='Authenticated'))
# Add home link by url
admin.add_link(MenuLink(name='Back Home', url='/'))
# Add login link by endpoint
admin.add_link(NotAuthenticatedMenuLink(name='Login',
endpoint='login_view'))
# Add links with categories
admin.add_link(MenuLink(name='Google', category='Links', url='http://www.google.com/'))
admin.add_link(MenuLink(name='Mozilla', category='Links', url='http://mozilla.org/'))
# Add logout link by endpoint
admin.add_link(AuthenticatedMenuLink(name='Logout',
endpoint='logout_view'))
admin.init_app(app)
# Start app
app.run(debug=True)
{% extends 'admin/master.html' %}
{% block body %}
Hello World from Authenticated Admin!
{% endblock %}
...@@ -8,6 +8,7 @@ from wtforms import validators ...@@ -8,6 +8,7 @@ from wtforms import validators
import flask_admin as admin import flask_admin as admin
from flask_admin.contrib import sqla from flask_admin.contrib import sqla
from flask_admin.contrib.sqla import filters from flask_admin.contrib.sqla import filters
from flask_admin.base import MenuLink
# Create application # Create application
...@@ -163,7 +164,11 @@ admin = admin.Admin(app, name='Example: SQLAlchemy', template_mode='bootstrap3') ...@@ -163,7 +164,11 @@ admin = admin.Admin(app, name='Example: SQLAlchemy', template_mode='bootstrap3')
admin.add_view(UserAdmin(User, db.session)) admin.add_view(UserAdmin(User, db.session))
admin.add_view(sqla.ModelView(Tag, db.session)) admin.add_view(sqla.ModelView(Tag, db.session))
admin.add_view(PostAdmin(db.session)) admin.add_view(PostAdmin(db.session))
admin.add_view(TreeView(Tree, db.session)) admin.add_view(TreeView(Tree, db.session, category="Other"))
admin.add_sub_category(name="Links", parent_name="Other")
admin.add_link(MenuLink(name='Back Home', url='/', category='Links'))
admin.add_link(MenuLink(name='Google', url='http://www.google.com/', category='Links'))
admin.add_link(MenuLink(name='Mozilla', url='http://mozilla.org/', category='Links'))
def build_sample_db(): def build_sample_db():
......
...@@ -9,7 +9,7 @@ from flask_admin._compat import with_metaclass, as_unicode ...@@ -9,7 +9,7 @@ from flask_admin._compat import with_metaclass, as_unicode
from flask_admin import helpers as h from flask_admin import helpers as h
# For compatibility reasons import MenuLink # For compatibility reasons import MenuLink
from flask_admin.menu import MenuCategory, MenuView, MenuLink # noqa: F401 from flask_admin.menu import MenuCategory, MenuView, MenuLink, SubMenuCategory # noqa: F401
def expose(url='/', methods=('GET',)): def expose(url='/', methods=('GET',)):
...@@ -581,6 +581,27 @@ class Admin(object): ...@@ -581,6 +581,27 @@ class Admin(object):
for view in args: for view in args:
self.add_view(view) self.add_view(view)
def add_sub_category(self, name, parent_name):
"""
Add a category of a given name underneath
the category with parent_name.
:param name:
The name of the new menu category.
:param parent_name:
The name of a parent_name category
"""
name_text = as_unicode(name)
parent_name_text = as_unicode(parent_name)
category = self.get_category_menu_item(name_text)
parent = self.get_category_menu_item(parent_name_text)
if category is None and parent is not None:
category = SubMenuCategory(name)
self._menu_categories[name_text] = category
parent.add_child(category)
def add_link(self, link): def add_link(self, link):
""" """
Add link to menu links collection. Add link to menu links collection.
......
...@@ -7,7 +7,7 @@ class BaseMenu(object): ...@@ -7,7 +7,7 @@ class BaseMenu(object):
""" """
def __init__(self, name, class_name=None, icon_type=None, icon_value=None, target=None): def __init__(self, name, class_name=None, icon_type=None, icon_value=None, target=None):
self.name = name self.name = name
self.class_name = class_name self.class_name = class_name if class_name is not None else ''
self.icon_type = icon_type self.icon_type = icon_type
self.icon_value = icon_value self.icon_value = icon_value
self.target = target self.target = target
...@@ -141,3 +141,9 @@ class MenuLink(BaseMenu): ...@@ -141,3 +141,9 @@ class MenuLink(BaseMenu):
def get_url(self): def get_url(self):
return self.url or url_for(self.endpoint) return self.url or url_for(self.endpoint)
class SubMenuCategory(MenuCategory):
def __init__(self, *args, **kwargs):
super(SubMenuCategory, self).__init__(*args, **kwargs)
self.class_name += ' dropdown-submenu'
...@@ -783,7 +783,7 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -783,7 +783,7 @@ class BaseModelView(BaseView, ActionsMixin):
:param name: :param name:
View name. If not provided, will use the model class name View name. If not provided, will use the model class name
:param category: :param category:
View category Optional category name, for grouping views in the menu
:param endpoint: :param endpoint:
Base endpoint. If not provided, will use the model name. Base endpoint. If not provided, will use the model name.
:param url: :param url:
......
.nav li.dropdown ul.dropdown-menu li:hover ul {
display:block;
position:absolute;
left:100%;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.nav li.dropdown ul.dropdown-menu ul {
display: none;
float:right;
position: relative;
top: auto;
margin-top: -30px;
}
.nav li.dropdown a.dropdown-toggle .glyphicon {
margin: 0 4px;
}
...@@ -20,25 +20,33 @@ ...@@ -20,25 +20,33 @@
{%- if item.is_category() -%} {%- if item.is_category() -%}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{%- if children %} {%- if children %}
{% set class_name = item.get_class_name() %} {% set class_name = item.get_class_name() or '' %}
{%- if item.is_active(admin_view) %} {%- if item.is_active(admin_view) %}
<li class="active dropdown"> <li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else -%} {% else -%}
<li class="dropdown"> <li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{%- endif %} {%- endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)"> <a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
{% if item.class_name %}<i class="{{ item.class_name }}"></i> {% endif %}{{ item.name }}<b class="caret"></b> {% if item.class_name %}<i class="{{ item.class_name }}"></i> {% endif %}
{{ menu_icon(item) }}{{ item.name }}
{%- if 'dropdown-submenu' not in class_name -%}<b class="caret"></b>{%- endif -%}
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{%- for child in children -%} {%- for child in children -%}
{% set class_name = child.get_class_name() %} {%- if child.is_category() -%}
{%- if child.is_active(admin_view) %} {{ menu(menu_root=[child]) }}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %} {% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}> {% set class_name = child.get_class_name() %}
{%- if child.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{%- endif %}
<a href="{{ child.get_url() }}"{% if child.target %}
target="{{ child.target }}"{% endif %}>
{{ menu_icon(child) }}{{ child.name }}</a>
</li>
{%- endif %} {%- endif %}
<a href="{{ child.get_url() }}"{% if child.target %} target="{{ child.target }}"{% endif %}>{{ menu_icon(child) }}{{ child.name }}</a>
</li>
{%- endfor %} {%- endfor %}
</ul> </ul>
</li> </li>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap-theme.min.css', v='3.3.5') }}" rel="stylesheet"> <link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap-theme.min.css', v='3.3.5') }}" rel="stylesheet">
{%endif%} {%endif%}
<link href="{{ admin_static.url(filename='admin/css/bootstrap3/admin.css', v='1.1.1') }}" rel="stylesheet"> <link href="{{ admin_static.url(filename='admin/css/bootstrap3/admin.css', v='1.1.1') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='admin/css/bootstrap3/submenu.css') }}" rel="stylesheet">
{% if admin_view.extra_css %} {% if admin_view.extra_css %}
{% for css_url in admin_view.extra_css %} {% for css_url in admin_view.extra_css %}
<link href="{{ css_url }}" rel="stylesheet"> <link href="{{ css_url }}" rel="stylesheet">
......
...@@ -20,25 +20,37 @@ ...@@ -20,25 +20,37 @@
{%- if item.is_category() -%} {%- if item.is_category() -%}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{%- if children %} {%- if children %}
{% set class_name = item.get_class_name() %} {% set class_name = item.get_class_name() or '' %}
{%- if item.is_active(admin_view) %} {%- if item.is_active(admin_view) %}
<li class="active dropdown"> <li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else -%} {% else -%}
<li class="dropdown"> <li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{%- endif %} {%- endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)"> <a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
{% if item.class_name %}<span class="{{ item.class_name }}"></span> {% endif %}{{ item.name }}<b class="caret"></b> {% if item.class_name %}<span class="{{ item.class_name }}"></span> {% endif %}
{{ menu_icon(item) }}{{ item.name }}
{%- if 'dropdown-submenu' in class_name -%}
<i class="glyphicon glyphicon-chevron-right small"></i>
{%- else -%}
<i class="glyphicon glyphicon-chevron-down small"></i>
{%- endif -%}
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{%- for child in children -%} {%- for child in children -%}
{% set class_name = child.get_class_name() %} {%- if child.is_category() -%}
{%- if child.is_active(admin_view) %} {{ menu(menu_root=[child]) }}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %} {% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}> {% set class_name = child.get_class_name() %}
{%- if child.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{%- endif %}
<a href="{{ child.get_url() }}"{% if child.target %}
target="{{ child.target }}"{% endif %}>
{{ menu_icon(child) }}{{ child.name }}</a>
</li>
{%- endif %} {%- endif %}
<a href="{{ child.get_url() }}"{% if child.target %} target="{{ child.target }}"{% endif %}>{{ menu_icon(child) }}{{ child.name }}</a>
</li>
{%- endfor %} {%- endfor %}
</ul> </ul>
</li> </li>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment