Flask-admin is an extension of the Flask framework, it can quickly create a Web management interface, it realizes such as users, files, additions and deletions and other commonly used management functions, if the default interface does not like, you can modify the template file to customize;
Flask-admin each menu (hyperlink) as a view, registered before it can be displayed, the view itself has properties to control whether it is visible, so the mechanism can be used to customize their own modular interface, such as to allow different users to login after the user to see the same menu;
Project Address: https://flask-admin.readthedocs.io/en/latest/
Example/simple
This is the simplest example that can help us quickly and intuitively understand the basic concepts and learn to customize the Flask-admin interface
simple.py:
From flask import flaskfrom flask.ext import admin# Create Custom admin viewclass myadminview (admin. Baseview): @admin. Expose ('/') def index (self): return Self.render (' myadmin.html ') class Anotheradminview (admin. Baseview): @admin. Expose ('/') def index (self): return Self.render (' anotheradmin.html ') @ Admin.expose ('/test/') def Test (self): return Self.render (' test.html ') # Create flask Appapp = Flask (__name__, template_folder= ' templates ') App.debug = true# Flask views@app.route ('/') def index (): return ' Click me to get to Admin !' # Create Admin interfaceadmin = admin. Admin () Admin.add_view (Myadminview (category= ' test ')) Admin.add_view (Anotheradminview (category= ' test ')) Admin.init _app (APP) If __name__ = = ' __main__ ': # Start app App.run ()
Here you can see the effect of running
Baseview
All view must inherit from Baseview:
Copy the Code code as follows:
Class Baseview (Name=none, Category=none, Endpoint=none, Url=none, Static_folder=none, Static_url_path=none)
Name:view is displayed on the page as a menu (hyperlink), menu name = = ' Name ', the default is lowercase class name
Category: If multiple view has the same category, put it all into a dropdown (dropdown name== ' category ')
Endpoint: Assume endpoint= ' xxx ', you can use Url_for (Xxx.index), can also change the page URL (/admin/xxx)
URL: page URL, priority URL > Endpoint > class name
Path to the Static_folder:static directory
URL of the Static_url_path:static directory
Anotheradmin.html:
{% extends ' admin/master.html '%} {% block body%} Hello World from anothermyadmin!
Click me to go to test view{% endblock%}
If Anotheradminview adds the parameter endpoint= ' xxx ', then this can be written url_for (' Xxx.text '), then the page URL will be/admin/anotheradminview/into/admin/xxx
If the parameter url= ' AAA ' is specified at the same time, the page URL becomes/admin/aaa,url priority higher than endpoint
Admin
Copy the Code code as follows:
Class Admin (App=none, Name=none, Url=none, Subdomain=none, Index_view=none, Translations_path=none, Endpoint=None, Static_url_path=none, Base_template=none)
App:flask application Object; In this case, you can not write Admin.init_app (app), directly with admin = admin. Admin (App=app) is the same
Name:application name, default ' admin ', will be displayed as main Menu name (' admin ' on left side of ' Home ') and page title
Subdomain:???
Index_view: The "Home" menu corresponds to the index view, the default Adminindexview
Base_template: base template, Default admin/base.html, the template is in the Flask-admin source directory
Some of the admin codes are as follows:
Class MenuItem (object): "" "simple menu tree hierarchy. "" "Def __init__ (self, Name, view=none): self.name = name Self._view = View Self._children = [] Self._childre N_urls = set () Self._cached_url = None Self.url = None if view is not None:self.url = View.url def add_chi LD (self, view): Self._children.append (view) Self._children_urls.add (View.url) class Admin (object): Def __init__ (self , App=none, Name=none, Url=none, Subdomain=none, Index_view=none, Translations_path=none, Endpoint=none, Static_url_path=none, base_template=none): Self.app = App Self.translations_path = tr Anslations_path self._views = [] Self._menu = [] self._menu_categories = Dict () self._menu_links = [] If NA Me is none:name = ' Admin ' self.name = name Self.index_view = Index_view or Adminindexview (endpoint=endpoint, U Rl=url) Self.endpoint = endpoint or self.index_view.endpoint self.url = URL orSelf.index_view.url Self.static_url_path = Static_url_path Self.subdomain = Subdomain self.base_template = base_t Emplate or ' admin/base.html ' # ADD predefined index view Self.add_view (Self.index_view) # Register with Applicati On if app was not none:self._init_extension () def add_view (self, view): # Add to Views Self._views.append (V Iew) # If app was provided in constructor, register view with Flask app If Self.app are not None:self.app.regis Ter_blueprint (View.create_blueprint (self)) Self._add_view_to_menu (view) def _add_view_to_menu (self, view): If Vie W.category:category = Self._menu_categories.get (view.category) if category is None:category = MenuItem ( view.category) Self._menu_categories[view.category] = category Self._menu.append (category) Category.add _child (MenuItem (view.name, view)) Else:self._menu.append (MenuItem (view.name, view)) def Init_app (self, app): Self.app = App Self._Init_extension () # Register views for view in Self._views:app.register_blueprint (View.create_blueprint (self)) Self._add_view_to_menu (view)
From the code above you can see that Init_app (APP) and admin (App=app) are the same:
Registering each view as a blueprint (flask concept can be easily understood as a module)
Record all view, and the category and URL to which it belongs
Adminindexview
Copy the Code code as follows:
Class Adminindexview (Name=none, Category=none, Endpoint=none, Url=none, template= ' admin/index.html ')
Name: Default ' Home '
Endpoint: Default ' admin '
URL: Default '/admin '
If you want to encapsulate your view, you can refer to Adminindexview's notation:
Class Adminindexview (Baseview): def __init__ (self, name=none, Category=none, Endpoint=none, Url=none , Template= ' admin/index.html '): super (Adminindexview, self). __init__ (name or Babel.lazy_gettext (' Home '), Category, endpoint or ' admin ', url or '/admin ', ' static ') self._template = template @expose () def index (self): return Self.render (self._template) base_template
Base_template default is/admin/base.html, is the main code of the page (based on Bootstrap), it is also import admin/layout.html;
Layout is a number of macros, mainly used to expand, display menu;
Use some variables in the template to remove information that was saved before registering the view (for example, menu name and URL):
# admin/layout.html (part)
{% Macro menu ()%} {% for item in Admin_view.admin.menu ()%} {% if item.is_category ()%} {% Set children = Item.get_children ()%} {% if children%} {% if item.is_active (admin_view)%}
{% Else%}{% ENDIF%} {{Item.name}} {% for child in
children%} {% if child.is_active (admin_view)%}
- {% Else%}
- {% ENDIF%} {{Child.name}}
{% ENDFOR%}
{% ENDIF%} {% Else%} {% if item.is_accessible () and item.is_visible ()%} {% if item.is_active (admin_view)%}
{% Else%}{% ENDIF%} {{Item.name}} {% ENDIF%} {% ENDIF%} {% ENDFOR%} {% ENDMACRO%}
Example/file
This example can help us quickly build a file management interface, but our focus is on learning to use the Actionsmixin module
file.py:
Import Osimport Os.path as Opfrom flask import flaskfrom flask.ext import adminfrom flask.ext.admin.contrib import Fileadm in# Create flask Appapp = Flask (__name__, template_folder= ' templates ', static_folder= ' files ') # Create dummy Secrey key so We can use flashapp.config[' secret_key ' "= ' 123456790 ' # Flask Views@app.route ('/') def index (): return ' Click me to get To admin! ' if __name__ = = ' __main__ ': # Create Directory path = Op.join (Op.dirname (__file__), ' files ') try: Os.mkdir (path) except OSError: pass # Create Admin interface admin = admin. Admin (APP) Admin.add_view (fileadmin. Fileadmin (Path, '/files/', name= ' files ') # Start app App.run (debug=true)
Fileadmin is a view that has been written and can be used directly:
Copy the Code code as follows:
Class Fileadmin (Base_path, Base_url, Name=none, Category=none, Endpoint=none, Url=none, Verify_path=true)
Base_path: Relative path to file storage
Base_url: URL of the file directory
Fileadmin and Actionsmixin related codes are as follows:
Class Fileadmin (Baseview, actionsmixin):
def __init__ (self, Base_path, Base_url, Name=none, Category=none, Endpoint=none, Url=none, verify_path=t Rue): Self.init_actions () @expose ('/action/', methods= (' POST ',)) def action_view (self): return self.handle_action () # Actions@action (' delete ', lazy_gettext (' delete '), Lazy_gettext (' Is you sure do want to delete these files? ')) def action_delete (self, items): If isn't Self.can_delete:flash (GetText (' File deletion is disabled. '), ' error ') retur N for the path in Items:base_path, Full_path, Path = Self._normalize_path (path) if Self.is_accessible_path (path): Try:os.remove (Full_path) Flash (gettext (' File '% (name) s "was successfully deleted. ', Name=path)) excep T Exception as Ex:flash (GetText (' Failed to delete file:% (name) s ', Name=ex), ' Error ') @action (' Edit ', Lazy_gettext ( ' Edit ') def action_edit (self, items): Return Redirect (Url_for ('. Edit ', Path=items)) @action () for wrap followed by the function, The function here is to save the parameters: Def action (name, text, ConfirmatiOn=none) def wrap (f): F._action = (name, text, confirmation) return F return wrap
Name:action Name
Text: Available for button names
Confirmation: Frame Confirmation Information
Init_actions () Saves all action information to the Actionsmixin:
# debug Information _actions = [(' delete ', Lu ' delete '), (' Edit ', Lu ' edit ')]_actions_data = {' edit ': (
; Lu ' Edit ', None), ' delete ': (
sure;, Lu ' delete ', Lu ' is you-want to delete these files? ')}
Action_view () is used to process the post-to-/action/request, and then calls Handle_action (), which then invokes a different action handler, and finally returns the current page:
# Omit extraneous code def handle_action (self, return_view=none): action = request.form.get (' action ') ids = Request.form.getlist (' rowID ') handler = Self._actions_data.get (action) if handler and Self.is_action_ Allowed (action): response = handler[0] (IDS) If response is not None: return response if not Return_view : url = url_for ('. ' + Self._default_view) else: url = url_for ('. ' + Return_view) return redirect (URL)
IDS is a list of files that are passed as parameters to the action handler function (parameters items):
# Debug Information IDs: [u ' 1.png ', U ' 2.png ']
Re-analysis page code, files page corresponding file for admin/file/list.html, focus on with selected drop-down menu related code:
{% import ' admin/actions.html ' as actionslib with context%}
{% if actions%} {{Actionslib.dropdown (actions, ' dropdown-toggle btn Btn-large ')}} {% ENDIF%} {% block actions%} {{Actionslib.form (Actions, Url_for ('. Action_view ')}}} {% Endblock%} {% block tail%} {{Actionslib.script (_gettext (' Please select at least one file. '), actions, actions_confirmation)}}{% Endblock%}
The three macros used above are actions.html:
{% Macro dropdown (Actions, btn_class= ' Dropdown-toggle ')-%} {{_gettext (' with Selected ')}}
{% for P in actions%}
- {{_gettext (p[1])}}
{% endfor%}
{% ENDMACRO%} {% macro form (actions, URL)%} {% if actions%} {% ENDIF%} {% ENDMACRO%} {% macro script (message, actions, actions_confirmation)%} {% if actions%} {% ENDIF%} {% ENDMACRO%}
Final generated page (partial):
With selected
The processing method after selecting the menu in Actions.js:
var adminmodelactions = function (Actionerrormessage, actionconfirmations) { //Actions helpers. Todo:move to separate file This.execute = function (name) { var selected = $ (' input.action-checkbox:checked '). Size (); if (selected = = = 0) { alert (actionerrormessage); return false; } var msg = Actionconfirmations[name]; if (!! msg) if (!confirm (msg)) return false; Update hidden form and submit it var form = $ (' #action_form '); $ (' #action ', form). val (name); $ (' Input.action-checkbox ', form). Remove (); $ (' input.action-checkbox:checked '). each (function () { Form.append ($ (this). Clone ()); }); Form.submit (); return false; }; $ (function () { $ ('. Action-rowtoggle '). Change (function () { $ (' Input.action-checkbox '). attr (' checked '), (this.checked);});};
Compare the changes before and after the form:
# initialize # ' Delete ' selected three files # ' Edit ' selected one of the files
To summarize, when we click on the menu item (delete,edit) in the drop-down menu, the local JavaScript code pops up the confirmation box (assuming there is a confirmation message), then submits a form to/admin/fileadmin/action/, request handler function Action_ View () calls different action handlers based on the form type and returns a page.
Flask-admin field (column) formatting
In some cases, we need to format a property of the model. For example, by default, the date time is shown to be longer, and it may be necessary to display only the month and day, when the column formatting comes in handy.
For example, if you want to show double the price, you can do this:
Class Mymodelview (Basemodelview): column_formatters = dict (Price=lambda V, c, M, p:m.price*2)
Or use a macro in the JINJA2 template:
From flask.ext.admin.model.template import Macroclass Mymodelview (basemodelview): column_formatters = dict (Price =macro (' Render_price ')) # in template{% macro Render_price (model, column)%} {{Model.price * 2}}{% endmacro%}
callback function Model:
Def formatter (view, context, model, name): # ' View ' is the current administrative View # ' context ' is instance of Jinj A2.runtime.Context # ' model ' is model instance # ' name ' was property name Pass
Corresponds exactly to the above V, C, M, p.