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.ext import extadmin
from flask.ext import adminex
# Create custom admin view
class MyAdminView(extadmin.BaseView):
@extadmin.expose('/')
class MyAdminView(adminex.BaseView):
@adminex.expose('/')
def index(self):
return render_template('myadmin.html', view=self)
class AnotherAdminView(extadmin.BaseView):
@extadmin.expose('/')
class AnotherAdminView(adminex.BaseView):
@adminex.expose('/')
def index(self):
return render_template('anotheradmin.html', view=self)
@adminex.expose('/test/')
def test(self):
return render_template('test.html', view=self)
# Create flask app
app = Flask(__name__, template_folder='templates')
......@@ -28,9 +32,10 @@ def index():
if __name__ == '__main__':
# Create admin interface
admin = extadmin.Admin(app)
admin.add_view(MyAdminView())
admin.add_view(AnotherAdminView())
admin = adminex.Admin()
admin.add_view(MyAdminView(category='Test'))
admin.add_view(AnotherAdminView(category='Test'))
admin.apply(app)
# Start app
app.debug = True
......
{% extends 'admin/master.html' %}
{% 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 %}
{% extends 'admin/master.html' %}
{% block body %}
This is TEST subitem from the AnotherAdminView!
{% endblock %}
from functools import wraps
from re import sub
from flask import Blueprint, render_template, url_for, abort
......@@ -57,8 +58,9 @@ class AdminViewMeta(type):
class BaseView(object):
__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.category = category
self.endpoint = endpoint
self.url = url
self.static_folder = static_folder
......@@ -81,7 +83,7 @@ class BaseView(object):
# If name is not povided, use capitalized endpoint name
if self.name is None:
self.name = self.endpoint.capitalize()
self.name = self._prettify_name(self.__class__.__name__)
# Create blueprint and register rules
self.blueprint = Blueprint(self.endpoint, __name__,
......@@ -95,6 +97,9 @@ class BaseView(object):
getattr(self, name),
methods=methods)
def _prettify_name(self, name):
return sub(r'(?<=.)([A-Z])', r' \1', name)
def is_accessible(self):
return True
......@@ -104,8 +109,8 @@ class BaseView(object):
class AdminIndexView(BaseView):
def __init__(self, name=None, endpoint=None, url=None):
super(AdminIndexView, self).__init__(name or 'Home', endpoint or 'admin', url or '/admin/', 'static')
def __init__(self, name=None, category=None, endpoint=None, url=None):
super(AdminIndexView, self).__init__(name or 'Home', category, endpoint or 'admin', url or '/admin', 'static')
@expose('/')
def index(self):
......@@ -113,9 +118,51 @@ class AdminIndexView(BaseView):
class Admin(object):
def __init__(self, app, index_view=None):
self.app = app
class MenuItem(object):
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._menu = []
if index_view is None:
index_view = AdminIndexView()
......@@ -124,14 +171,37 @@ class Admin(object):
self.add_view(index_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)
self._views.append(view)
def apply(self, app):
self.app = app
for v in self._views:
app.register_blueprint(v.blueprint)
self._refresh_menu()
# Register blueprint
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):
# TODO: Precalculate URL - no need to get URLs for every request
return (('%s.%s' % (v.endpoint, v._default_view), v.url, v.name) for v in self._views if v.is_accessible())
return self._menu
......@@ -8,7 +8,7 @@ from flask import request, render_template, url_for, redirect
from flaskext import wtf
from .base import Admin, BaseView, expose
from flask.ext.adminex import BaseView, expose
class AdminModelConverter(ModelConverter):
......
body
{
padding-top: 50px;
}
......@@ -8,26 +8,45 @@
<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-responsive.css') }}" rel="stylesheet">
<link href="{{ url_for('admin.static', filename='css/admin.css') }}" rel="stylesheet">
{% endblock %}
</head>
<body>
{% block page_body %}
<div class="container-fluid">
<div class="row-fluid">
<div class="span1">
<ul class="nav nav-pills nav-stacked">
{% for url, raw_url, label in view.admin.menu %}
<li{% if raw_url == view.url %} class="active"{% endif %}>
<a href="{{ url_for(url) }}">{{ label }}</a>
</li>
{% endfor %}
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
{% for item in view.admin.menu() %}
{% if item.is_category() %}
{% 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>
{% endfor %}
</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 class="span10">
{% block body %}{% endblock %}
</div>
</div>
</div>
<div class="container">
{% block body %}{% endblock %}
</div>
{% endblock %}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
......
from setuptools import setup
setup(
name='Flask-ExtAdmin',
name='Flask-AdminEx',
version='0.0.1',
url='https://github.com/MrJoes/flask-extadmin/',
license='BSD',
......@@ -9,7 +9,7 @@ setup(
author_email='serge.koval+github@gmail.com',
description='Simple and extensible admin interface framework for Flask',
long_description=__doc__,
packages=['flask_extadmin'],
packages=['flask_adminex'],
include_package_data=True,
zip_safe=False,
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