flask-admin记录(参照官网)

搜下没有很全的中文官方文档,都是其他个人的笔记很零散的功能。只好看着官方教程自己记录

2017-08月 温习一遍 发现插件升级了有些地方都不对了 懒得更新代码了

初始化:

from flask import Flask
from flask_admin import Admin

app = Flask(__name__)

admin = Admin(app, name='microblog', template_mode='bootstrap3')
# Add administrative views here
app.run()

添加模型动态视图:

from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
admin = Admin(app, name='microblog', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

添加admin/index.html

{% extends 'admin/master.html' %}

{% block body %}
  <p>Hello world</p>
{% endblock %}

用户认证及其他一些功能列在下面代码注释里:

#coding=utf-8
"""filename:app/admin/views.py
Created 2017-06-01
Author: by anaf
note:admin视图函数
"""


from flask import Flask,request,redirect,url_for
from flask.ext.admin import Admin, BaseView, expose
from app import admin_app,db
from app.models import Article,Category,User,User_msg,Category_attribute,Comment
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.login import current_user,login_required
from wtforms.validators import Required

# class MyView(BaseView):
#   @expose('/')
#   def index(self):
#       return self.render('admin/index.html')


#增加一个导航
# admin_app.add_view(MyView(name=u'文章'))

# admin_app.add_view(ModelView(User,db.session,name=u'用户管理'))


class Admin_v(ModelView):
    #认证
    def is_accessible(self):
        return current_user.is_authenticated
    #不知道干什么用
    def inaccessible_callback(self, name, **kwargs):
        return redirect(url_for('login', next=request.url))

    #是否允许创建、删除、编辑、只读
    can_create = True
    # can_delete = False
    # can_edit = False
    # can_view_details = True
    #每页记录显示行数
    page_size = 50

    #删除行
    # column_exclude_list = ['password', ]

    #搜索列表
    # column_searchable_list = ['username', 'name']
    #筛选列表
    # column_filters = ['location']
    #直接在视图中启用内联编辑,快速编辑行
    column_editable_list = ['name', 'username']
    #直接在当前页弹框 进行 编辑或者添加,  不是很大用
    # create_modal = True
    # edit_modal = True
    #移除创建的字段
    form_excluded_columns = ['comments','last_seen', 'avatar_hash','article_id','followed','followers']
    #form  WTForms 表单验证,详细验证规则 看WTForms 
    form_args={'name':{'label':u'名字','validators':[Required()]}}
    #制定form渲染参数
    form_widget_args = {
                    'about_me': {
                    'rows': 10,
                    'style': 'color: black'
                        }
                    }
    #当表单包含外键时,使用Ajax加载那些相关的模型(没会用)
    # form_ajax_refs = {
    #   'role_id': {
    #       'fields': ['first_name', 'last_name', 'email'],
    #       'page_size': 10
    #       }
    #   }
    #过滤ajax加载的结果 没会用
    # form_ajax_refs = {'active_user': QueryAjaxModelLoader('user', db.session, User,filters=["is_active=True", "id>1000"])}


    # select choices   没有select选择器  不知道效果
    # form_choices = {
    #   'title': [
 #          ('MR', 'Mr'),
 #          ('MRS', 'Mrs'),
 #          ('MS', 'Ms'),
 #          ('DR', 'Dr'),
 #          ('PROF', 'Prof.')
 #          ]
 #        }
    
    
    #列表行重写
    column_labels = {
        'id':u'序号',
        'username' : u'用户账号',
        # 'password_hash':u'密码加密值',
        'name':u'真实姓名',
        'location':u'地址',
        'about_me':u'自我简介',
        'avatar_hash':u'头像加密值',
    }
    column_list = ('id', 'username','name',
                'location','about_me','avatar_hash')

    def __init__(self, session, **kwargs):
        super(Admin_v, self).__init__(User, session, **kwargs)


admin_app.add_view(Admin_v(db.session,name=u'用户管理'))
admin_app.add_view(ModelView(Article,db.session,name=u'文章管理'))
admin_app.add_view(ModelView(Category,db.session,name=u'栏目管理'))
admin_app.add_view(ModelView(User_msg,db.session,name=u'留言管理'))
admin_app.add_view(ModelView(Category_attribute,db.session,name=u'栏目属性表(不要随意更改)'))
admin_app.add_view(ModelView(Comment,db.session,name=u'评论管理'))  


#文件管理
from flask.ext.admin.contrib.fileadmin import FileAdmin
import os.path as op
path = op.join(op.dirname(__file__), 'static/uploads')
admin_app.add_view(FileAdmin(path,'/static', name=u'静态文件'))

添加 自定义视图

from flask_admin import BaseView, expose

class AnalyticsView(BaseView):
    @expose('/')
    def index(self):
        return self.render('analytics_index.html')

admin.add_view(AnalyticsView(name='Analytics', endpoint='analytics'))
{% extends 'admin/master.html' %}
{% block body %}
  <p>Here I'm going to display some data.</p>
{% endblock %}

重写内置 视图

from flask_admin.contrib.sqla import ModelView

# Flask and Flask-SQLAlchemy initialization here

class UserView(ModelView):
    @expose('/new/', methods=('GET', 'POST'))
    def create_view(self):
    """
        Custom create view.
    """

    return self.render('create_user.html')

扩展内置模板
可扩展的模板:对应继承 列表、创建、编辑页
admin/model/list.html
admin/model/create.html
admin/model/edit.html
例如:
{% extends 'admin/model/edit.html' %}

{% block body %}
MicroBlog Edit View
{{ super() }}
{% endblock %}

使视图使用模板

class MicroBlogModelView(ModelView):
    edit_template = 'microblog_edit.html'
    # create_template = 'microblog_create.html'
    # list_template = 'microblog_list.html'

如果使用基础模板,则在基础函数中添加

admin = Admin(app, base_template='microblog_master.html')

重写内置模板
可用的模板块,首先要定义一个基础模板快:admin/base.html.里面可以定义如下图内容:

image.png

admin/model/list.html包含了如下模块:

image.png

可参照这里的github例子

环境变量,在任何模板下可扩展admin/master.html模板。可用的环境变量有:

image.png

为特定的视图生成url

from flask import url_for

class MyView(BaseView):
    @expose('/')
    def index(self):
        # Get URL for the test view method
        user_list_url = url_for('user.index_view')
        return self.render('index.html', user_list_url=user_list_url)
url_for('user.edit_view', id=1, url=url_for('user.index_view'))

url_for('analytics.index')
admin.add_view(CustomView(name='Analytics', endpoint='analytics'))


高级 功能

防止csrf攻击保护

指定form_base_class 参数即可

from flask_admin.form import SecureForm
from flask_admin.contrib.sqla import ModelView

class CarAdmin(ModelView):
    form_base_class = SecureForm

格式化语言,显示中文字段
pip install flask-babelex #当时下载好多次才成功。6.8m太大了
初始化
from flask import app
from flask_babelex import Babel
app = Flask(name)
babel = Babel(app)
app.config['BABEL_DEFAULT_LOCALE'] = 'zh_CN'

管理文件和文件夹。上面代码已经集成了

添加一个Redis控制器(没有测试)

from redis import Redis
from flask_admin.contrib import rediscli

# Flask setup here

admin = Admin(app, name='microblog', template_mode='bootstrap3')

admin.add_view(rediscli.RedisCli(Redis()))

替换表单字段

from wtforms import TextAreaField
from wtforms.widgets import TextArea

class CKTextAreaWidget(TextArea):
    def __call__(self, field, **kwargs):
        if kwargs.get('class'):
            kwargs['class'] += ' ckeditor'
        else:
            kwargs.setdefault('class', 'ckeditor')
        return super(CKTextAreaWidget, self).__call__(field, **kwargs)

class CKTextAreaField(TextAreaField):
    widget = CKTextAreaWidget()

class MessageAdmin(ModelView):
    extra_js = ['//cdn.ckeditor.com/4.6.0/standard/ckeditor.js']

    form_overrides = {
        'body': CKTextAreaField
    

**↑上面的代码 CKTextAreaField这个放最上面不然提示找不到这个类 **
效果如下图:

image.png

亲测样子是实现了。但是没法上传图片
检查了下,发现问题。第一在编辑器的config.js配置上传处理函数:
config.filebrowserImageUploadUrl= "/main/upload";
细节配置参考编辑器的文档
第二编写上传处理函数:

#ckeditor图片上传
def gen_rnd_filename():
    filename_prefix = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
    return '%s%s' % (filename_prefix, str(random.randrange(1000, 10000)))

@main.route('/main/upload', methods=['GET','POST'])
@login_required
def UploadFileImage():
    """CKEditor file upload"""
    error = ''
    url = ''
    callback = request.args.get("CKEditorFuncNum")
    if request.method == 'POST' and 'upload' in request.files:
        fileobj = request.files['upload']
        fname, fext = os.path.splitext(fileobj.filename)
        rnd_name = '%s%s' % (gen_rnd_filename(), fext)
        filepath = os.path.join(current_app.static_folder, 'uploads/main', rnd_name)
        
        dirname = os.path.dirname(filepath)
        if not os.path.exists(dirname):
            try:
                os.makedirs(dirname)
            except:
                error = 'ERROR_CREATE_DIR'
        elif not os.access(dirname, os.W_OK):
            error = 'ERROR_DIR_NOT_WRITEABLE'
        if not error:
            fileobj.save(filepath)
            url = url_for('static', filename='%s/%s' % ('uploads/main', rnd_name))
    else:
        error = 'post error'
    res = """

        <script type="text/javascript">
          window.parent.CKEDITOR.tools.callFunction(%s, '%s', '%s');
        </script>

 """ % (callback, url, error)
    response = make_response(res)
    response.headers["Content-Type"] = "text/html"
    return response

这时候就可以上传图片了

头像

from jinja2 import Markup
#...
    #头像
    def _list_thumbnail(view, context, model, name):
        if not model.avatar_hash:
            return ''
        return Markup('<img src="http://www.gravatar.com/avatar/%s?d=identicon&s=32"' % model.avatar_hash)

    column_formatters = {'avatar_hash': _list_thumbnail}
#...

效果图,看最后的小图像:

image.png

可以看到自我简介就上传了一张 图片。但是里面的内容非常的长。
想不到什么办法只好这样处理的,虽然不是很妥当:

def _list_about_me_sub(view, context, model, name):
        return model.about_me[0:50]#截取50个字符

    column_formatters = {'avatar_hash': _list_thumbnail,'about_me':_list_about_me_sub}

看起来是这样:

image.png

下面的的教程就是地理位置了 。这个不需要。有需要的自己看下文档吧github上的flask-admin的地理位置文档

通过渲染规则自定义内置表单

from flask_admin.form import rules
form_create_rules = ('location', rules.HTML('<input type="text" />'), 'username', 'about_me')

效果图,看到左边多了一个文本框就是自定义添加的:

image.png

rules支持的类型如下图:

image.png

重写表格

class MyView(ModelView):
    def scaffold_form(self):
        form_class = super(UserView, self).scaffold_form()
        form_class.extra = StringField('Extra')
        return form_class

反正我没有重写

自定义批量操作

from flask_admin.actions import action

class UserView(ModelView):
    @action('approve', 'Approve', 'Are you sure you want to approve selected users?')
    def action_approve(self, ids):
        try:
            query = User.query.filter(User.id.in_(ids))

            count = 0
            for user in query.all():
                if user.approve():
                    count += 1

            flash(ngettext('User was successfully approved.',
                           '%(count)s users were successfully approved.',
                           count,
                           count=count))
        except Exception as ex:
            if not self.handle_view_exception(ex):
                raise

            flash(gettext('Failed to approve users. %(error)s', error=str(ex)), 'error')

还没有去尝试添加自定义操作,就是除了系统自带的增删改查基础上增加自己的功能操作
基本以上的操作都可以满足大部分功能了。教程也就到这里吧,应该是目前最全的了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容