Commit 24115621 authored by Serge S. Koval's avatar Serge S. Koval

Improvements and fixes.

parent a5e419ce
from flask import Flask, render_template from flask import Flask, render_template
from flask.ext import extadmin from flask.ext import adminex
# Create custom admin view # Create custom admin view
class MyAdminView(extadmin.BaseView): class MyAdminView(adminex.BaseView):
@extadmin.expose('/') @adminex.expose('/')
def index(self): def index(self):
return render_template('myadmin.html', view=self) return render_template('myadmin.html', view=self)
class AnotherAdminView(extadmin.BaseView): class AnotherAdminView(adminex.BaseView):
@extadmin.expose('/') @adminex.expose('/')
def index(self): def index(self):
return render_template('anotheradmin.html', view=self) return render_template('anotheradmin.html', view=self)
@adminex.expose('/test/')
def test(self):
return render_template('test.html', view=self)
# Create flask app # Create flask app
app = Flask(__name__, template_folder='templates') app = Flask(__name__, template_folder='templates')
...@@ -28,9 +32,10 @@ def index(): ...@@ -28,9 +32,10 @@ def index():
if __name__ == '__main__': if __name__ == '__main__':
# Create admin interface # Create admin interface
admin = extadmin.Admin(app) admin = adminex.Admin()
admin.add_view(MyAdminView()) admin.add_view(MyAdminView(category='Test'))
admin.add_view(AnotherAdminView()) admin.add_view(AnotherAdminView(category='Test'))
admin.apply(app)
# Start app # Start app
app.debug = True app.debug = True
......
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% block body %} {% block body %}
Hello World from AnotherMyAdmin! Hello World from AnotherMyAdmin!<br/>
<a href="{{ url_for('.test') }}">Click me to go to test view</a>
{% endblock %} {% endblock %}
{% extends 'admin/master.html' %}
{% block body %}
This is TEST subitem from the AnotherAdminView!
{% endblock %}
from functools import wraps from functools import wraps
from re import sub
from flask import Blueprint, render_template, url_for, abort from flask import Blueprint, render_template, url_for, abort
...@@ -57,8 +58,9 @@ class AdminViewMeta(type): ...@@ -57,8 +58,9 @@ class AdminViewMeta(type):
class BaseView(object): class BaseView(object):
__metaclass__ = AdminViewMeta __metaclass__ = AdminViewMeta
def __init__(self, name=None, endpoint=None, url=None, static_folder=None): def __init__(self, name=None, category=None, endpoint=None, url=None, static_folder=None):
self.name = name self.name = name
self.category = category
self.endpoint = endpoint self.endpoint = endpoint
self.url = url self.url = url
self.static_folder = static_folder self.static_folder = static_folder
...@@ -81,7 +83,7 @@ class BaseView(object): ...@@ -81,7 +83,7 @@ class BaseView(object):
# If name is not povided, use capitalized endpoint name # If name is not povided, use capitalized endpoint name
if self.name is None: if self.name is None:
self.name = self.endpoint.capitalize() self.name = self._prettify_name(self.__class__.__name__)
# Create blueprint and register rules # Create blueprint and register rules
self.blueprint = Blueprint(self.endpoint, __name__, self.blueprint = Blueprint(self.endpoint, __name__,
...@@ -95,6 +97,9 @@ class BaseView(object): ...@@ -95,6 +97,9 @@ class BaseView(object):
getattr(self, name), getattr(self, name),
methods=methods) methods=methods)
def _prettify_name(self, name):
return sub(r'(?<=.)([A-Z])', r' \1', name)
def is_accessible(self): def is_accessible(self):
return True return True
...@@ -104,8 +109,8 @@ class BaseView(object): ...@@ -104,8 +109,8 @@ class BaseView(object):
class AdminIndexView(BaseView): class AdminIndexView(BaseView):
def __init__(self, name=None, endpoint=None, url=None): def __init__(self, name=None, category=None, endpoint=None, url=None):
super(AdminIndexView, self).__init__(name or 'Home', endpoint or 'admin', url or '/admin/', 'static') super(AdminIndexView, self).__init__(name or 'Home', category, endpoint or 'admin', url or '/admin', 'static')
@expose('/') @expose('/')
def index(self): def index(self):
...@@ -113,9 +118,51 @@ class AdminIndexView(BaseView): ...@@ -113,9 +118,51 @@ class AdminIndexView(BaseView):
class Admin(object): class Admin(object):
def __init__(self, app, index_view=None): class MenuItem(object):
self.app = app def __init__(self, name, view=None):
self.name = name
self._view = view
self._children = []
self._children_urls = set()
self.url = None
if view is not None:
self.url = view.url
def add_child(self, view):
self._children.append(view)
self._children_urls.add(view.url)
def get_url(self):
if self._view is None:
return None
return url_for('%s.%s' % (self._view.endpoint, self._view._default_view))
def is_active(self, view):
if view == self._view:
return True
return view.url in self._children_urls
def is_accessible(self):
if self._view is None:
return False
return self._view.is_accessible()
def is_category(self):
return self._view is None
def get_children(self):
return [c for c in self._children if c.is_accessible()]
def __repr__(self):
return 'MenuItem %s (%s)' % (self.name, repr(self._children))
def __init__(self, index_view=None):
self._views = [] self._views = []
self._menu = []
if index_view is None: if index_view is None:
index_view = AdminIndexView() index_view = AdminIndexView()
...@@ -124,14 +171,37 @@ class Admin(object): ...@@ -124,14 +171,37 @@ class Admin(object):
self.add_view(index_view) self.add_view(index_view)
def add_view(self, view): def add_view(self, view):
# Store in list of views and associate view with admin instance
self._views.append(view)
view._set_admin(self) view._set_admin(self)
self._views.append(view)
def apply(self, app):
self.app = app
for v in self._views:
app.register_blueprint(v.blueprint)
# Register blueprint self._refresh_menu()
self.app.register_blueprint(view.blueprint)
def _refresh_menu(self):
categories = dict()
self._menu = []
for v in self._views:
if v.category is None:
self._menu.append(self.MenuItem(v.name, v))
else:
category = categories.get(v.category)
if category is None:
category = self.MenuItem(v.category)
categories[v.category] = category
self._menu.append(category)
category.add_child(self.MenuItem(v.name, v))
print repr(self._menu)
@property
def menu(self): def menu(self):
# TODO: Precalculate URL - no need to get URLs for every request return self._menu
return (('%s.%s' % (v.endpoint, v._default_view), v.url, v.name) for v in self._views if v.is_accessible())
...@@ -8,7 +8,7 @@ from flask import request, render_template, url_for, redirect ...@@ -8,7 +8,7 @@ from flask import request, render_template, url_for, redirect
from flaskext import wtf from flaskext import wtf
from .base import Admin, BaseView, expose from flask.ext.adminex import BaseView, expose
class AdminModelConverter(ModelConverter): class AdminModelConverter(ModelConverter):
......
body
{
padding-top: 50px;
}
...@@ -8,25 +8,44 @@ ...@@ -8,25 +8,44 @@
<meta name="author" content=""> <meta name="author" content="">
<link href="{{ url_for('admin.static', filename='bootstrap/css/bootstrap.css') }}" rel="stylesheet"> <link href="{{ url_for('admin.static', filename='bootstrap/css/bootstrap.css') }}" rel="stylesheet">
<link href="{{ url_for('admin.static', filename='bootstrap/css/bootstrap-responsive.css') }}" rel="stylesheet"> <link href="{{ url_for('admin.static', filename='bootstrap/css/bootstrap-responsive.css') }}" rel="stylesheet">
<link href="{{ url_for('admin.static', filename='css/admin.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
{% block page_body %} {% block page_body %}
<div class="container-fluid"> <div class="navbar navbar-fixed-top">
<div class="row-fluid"> <div class="navbar-inner">
<div class="span1"> <div class="container">
<ul class="nav nav-pills nav-stacked"> <ul class="nav">
{% for url, raw_url, label in view.admin.menu %} {% for item in view.admin.menu() %}
<li{% if raw_url == view.url %} class="active"{% endif %}> {% if item.is_category() %}
<a href="{{ url_for(url) }}">{{ label }}</a> {% set children = item.get_children() %}
{% if children %}
{% if item.is_active(view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="#">{{ item.name }}<b class="caret"></b></a>
<ul class="dropdown-menu">
{% for child in children %}
{% if child.is_active(view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ child.get_url() }}">{{ child.name }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</li>
{% endif %}
{% else %}
{% if item.is_accessible() %}
{% if item.is_active(view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ item.get_url() }}">{{ item.name }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</div> </div>
<div class="span10">
{% block body %}{% endblock %}
</div> </div>
</div> </div>
<div class="container">
{% block body %}{% endblock %}
</div> </div>
{% endblock %} {% endblock %}
......
from setuptools import setup from setuptools import setup
setup( setup(
name='Flask-ExtAdmin', name='Flask-AdminEx',
version='0.0.1', version='0.0.1',
url='https://github.com/MrJoes/flask-extadmin/', url='https://github.com/MrJoes/flask-extadmin/',
license='BSD', license='BSD',
...@@ -9,7 +9,7 @@ setup( ...@@ -9,7 +9,7 @@ setup(
author_email='serge.koval+github@gmail.com', author_email='serge.koval+github@gmail.com',
description='Simple and extensible admin interface framework for Flask', description='Simple and extensible admin interface framework for Flask',
long_description=__doc__, long_description=__doc__,
packages=['flask_extadmin'], packages=['flask_adminex'],
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
platforms='any', platforms='any',
......
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