Vue 2.0 起步(6) 后臺管理Flask-Admin - 微信公眾號RSS

上一篇:Vue 2.0 起步(5) 訂閱列表上傳和下載 - 微信公眾號RSS
本篇關(guān)鍵字:Flask-Admin 權(quán)限管理 定制顯示格式 顯示關(guān)系表外鍵內(nèi)容 顯示多對多(M2M)內(nèi)容

Flask-Admin

Flask-Admin是一個功能齊全赘风、簡單易用的Flask擴展,讓你為Flask應(yīng)用程序增加管理界面纵刘。它受django-admin包的影響邀窃,開箱就有所有管理功能!但開發(fā)者擁有最終應(yīng)用程序的外觀假哎、感覺和功能的全部控制權(quán)瞬捕。
官網(wǎng)Link

Flask-Admin logo.png

本篇完成功能:

  1. 對模型model,有CRUD基本功能:
    CRUD就是Create位谋、Read山析、Update、Delete
  2. 顯示數(shù)據(jù)庫內(nèi)容掏父,包括關(guān)系表Relation對應(yīng)的內(nèi)容
    比如:公眾號表Mp,能即時顯示每個公眾號秆剪,對應(yīng)有哪些Subscriber(訂閱者)赊淑、有哪些Article(文章),有些是通過多對多(M2M)查詢得到的仅讽。
    超過20條記錄陶缺,自動分頁pagination


    Mp.png
  3. 加上權(quán)限保護,只有superuser才能訪問后臺管理
    當(dāng)然洁灵,也可以定制饱岸,讓不同權(quán)限用戶,能查看不同的內(nèi)容
  4. 提供搜索功能
    比如徽千,在用戶表User苫费,我想把某個用戶加入黑名單,設(shè)想一下双抽,如果有成千上萬的用戶百框,不可能一頁頁找吧?所以加入搜索框:


    User-search.png

1. 對模型model牍汹,有CRUD基本功能

我們先看一下Get_started最簡例子:

admin.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView

app = Flask(__name__)
admin = Admin(app, name='MyAdmin', template_mode='bootstrap3')

# Flask-SQLAlchemy initialization here
db = SQLAlchemy(app)

# Create models
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64))
class Role(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))

admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Role, db.session))

app.run(debug=True)

去掉注釋铐维、import柬泽,才短短12行代碼?嫁蛇!
保存為admin.py锨并,運行python admin.py
打開瀏覽器:http://localhost:5000/admin
是不是立馬顯示出Bootstrap風(fēng)格的后臺管理頁面了?
只不過沒有關(guān)聯(lián)真實數(shù)據(jù)庫睬棚,所以內(nèi)容是空的第煮。

Get_start

下面把Flask-Admin整合到我們app應(yīng)用,并且關(guān)聯(lián)微信公眾號RSS的真實數(shù)據(jù)庫:

  • Flask-Admin初始化

/app/_init_.py

# encoding: utf-8
from flask import Flask, abort, redirect, url_for, request
from flask_sqlalchemy import SQLAlchemy
from config import config
from flask_admin.contrib import sqla
from flask_admin import Admin, helpers as admin_helpers

db = SQLAlchemy()

# models引用必須在 db/login_manager之后闸拿,不然會循環(huán)引用
from .models import User, Role

# Create admin
admin = Admin(name=u'簡讀Admin')

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    admin.init_app(app)

    return app
  • 自定義后臺管理頁面的首頁

/app/templates/admin/index.html

{% extends 'admin/master.html' %}
{% block body %}
<div class="container" align="right">
 <h5 align="center">Welcome to 后臺管理空盼!</h5>
    <br>
    <p>管理員<a href="/login">登錄</a></p>
    <br>
    Back to <a href="/">首頁 - 簡讀RSS</a>
<div>
{% endblock %}
  • 在View視圖里,為Admin引入我們應(yīng)用里的數(shù)據(jù)模型

/app/main/views.py

from flask import render_template, redirect, url_for, abort, flash, request,\
    current_app, make_response, jsonify
from .. import db, admin
from flask_admin import Admin, BaseView, expose
from flask_admin.contrib.sqla import ModelView
from ..models import User, Mp, Article, Role, Subscription

# 后臺管理頁面的首頁
class MyView(BaseView):
    @expose('/')
    def index(self):
        return self.render('admin/index.html')

admin.add_view(ModelView(Role, db.session))
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Mp, db.session))
admin.add_view(ModelView(Subscription, db.session))
admin.add_view(ModelView(Article, db.session))

運行應(yīng)用:python manage.py runserver
有數(shù)據(jù)顯示啦新荤!

ModelView-default

等等揽趾!為什么Subscriber、Mp顯示的是<models.XXX object>苛骨?篱瞎?

看一下 /app/models.py里Subscription定義,原來這兩個字段痒芝,是外鍵ForeignKey俐筋,查到當(dāng)然是對象了

# 訂閱公眾號和User是多對多關(guān)系
class Subscription(db.Model):
    __tablename__ = 'subscriptions'
    id = db.Column(db.Integer(), primary_key=True)
    subscriber_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    mp_id = db.Column(db.Integer, db.ForeignKey('mps.id'))
                         #   primary_key=True)

解決:
Python對象有個_repr方法,可以方便地改變打印對象時的輸出
我們來改一下User严衬、Mp對象的_repr
方法:

class User(db.Model):
    ...
    def __repr__(self):
        return '<User-%d %r>' % (self.id, self.email)

class Mp(db.Model):
    ...
    def __repr__(self):
        return '<Mp-%d %s>' % (self.id, self.mpName)

這時澄者,再訪問一下http://localhost:5000/admin/subscription/, 哈哈请琳,清楚地顯示出對象了吧粱挡?不再是一長串內(nèi)存地址了

Class_repr

現(xiàn)在,給其它的模型Role, Subscription, Article都加上__repr__()吧俄精!
加上之后询筏,另一個好處是:使用Create創(chuàng)建/Modify修改數(shù)據(jù)庫記錄時,一目了然竖慧,也不用面對一長串內(nèi)存地址了:

create_user.png

再看一下User頁面:
我的天嫌套,密碼都顯示出來啦!幸好是加密過的圾旨!


User-default

一長串看著有些礙眼踱讨,如何修改?而且萬一有些字段我們想保密碳胳,怎么辦呢勇蝙?

解決:
Flask-Admin提供全方位的定制服務(wù),除了完善的默認顯示,另外你想怎么顯示就怎么顯示味混。

/app/main/views.py

創(chuàng)建一個自定義ModelView产雹,User.password字段,比如翁锡,我只想顯示后6位蔓挖。然后添加給User:

class MyModelViewUser(ModelView):
    # 字段(列)格式化
    # `view` is current administrative view
    # `context` is instance of jinja2.runtime.Context
    # `model` is model instance
    # `name` is property name
    column_formatters = dict(
        password=lambda v, c, m, p: '**'+m.password[-6:],
        )

admin.add_view(MyModelViewUser(User, db.session))

再試試看,這下順眼多了吧馆衔。

User-password customize

2. 顯示數(shù)據(jù)庫內(nèi)容瘟判,包括關(guān)系表Relation對應(yīng)的內(nèi)容

上一節(jié),最后一張圖角溃,缺省情況下不顯示Roles, Mps拷获,因為它們是關(guān)系表Relationship。id减细,默認也是不顯示的匆瓜,因為是主鍵primary_key:

# /app/models.py
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    。未蝌。驮吱。
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users'), lazy='dynamic')
    mps = db.relationship('Subscription',
                               foreign_keys=[Subscription.subscriber_id],
                               backref=db.backref('subscriber', lazy='joined'),
                               lazy='dynamic',      # select dynamic subquery
                               cascade='all, delete-orphan')

那如何顯示呢?
繼續(xù)在/app/main/views.py里配置:

class MyModelViewUser(ModelView):
    column_display_pk = True # optional, but I like to see the IDs in the list
    column_display_all_relations = True

咦萧吠,報錯了:

sqlalchemy.exc.InvalidRequestError
InvalidRequestError: 'User.roles' does not support object population - eager loading cannot be applied.

原來左冬,User模型里,roles relationship沒有定義外鍵纸型,而且是用了lazy='dynamic'查詢方式拇砰,每次動態(tài)查詢,但不會立即返回數(shù)據(jù)狰腌。

解決:
把User--roles字段毕匀,lazy='dynamic'注釋掉!

這下癌别,沒有報錯了,主鍵id蹋笼、關(guān)系Relationship都顯示了展姐。但Mps怎么是SQL語句啊剖毯?圾笨?

Relationship

如果把User--mps字段,lazy='dynamic'注釋掉逊谋,會怎么樣呢擂达?
答案是:Mps會顯示內(nèi)容了!但胶滋,因為mps字段是多對多關(guān)系的外鍵板鬓,這樣Flask后臺查詢語句會報錯悲敷,比如Mp.to_json()!

解決:
User模型保留lazy='dynamic'俭令,添加subscribed_mps_str屬性方法后德,供ModelView里自定義顯示Mps字段使用

# /app/models.py
class User(UserMixin, db.Model):
    ...
    @property
    def subscribed_mps_str(self):
        mplist = [] 
        i = 1
        # SQLAlchemy 過濾器和聯(lián)結(jié)
        mps = Mp.query.join(Subscription, Subscription.mp_id == Mp.id)\
            .filter(Subscription.subscriber_id == self.id)
        for mp in mps:
                mplist.append('<Mp-%d %s_%s>' % (mp.id, mp.weixinhao, mp.mpName) )
                i+=1
        return mplist

Admin ModelView里自定義顯示Mps字段,使用subscribed_mps_str方法抄腔。
順便重構(gòu)一下瓢湃,把公用的ModelView定義到MyModelViewBase

# /app/main/views.py
class MyModelViewBase(ModelView):
   column_display_pk = True # optional, but I like to see the IDs in the list
   column_display_all_relations = True

class MyModelViewUser(MyModelViewBase):
    column_formatters = dict(
        password=lambda v, c, m, p: '**'+m.password[-6:],
        mps=lambda v, c, m, p: (m.subscribed_mps_str),  
        )

admin.add_view(MyModelViewUser(User, db.session))

OK了,按我們的期望顯示了:


User.png

3. 加上權(quán)限保護赫蛇,只有superuser才能訪問后臺管理

這就用到我們上一篇的Flask-Security里绵患,session管理的功能了
在自定義ModelView里,添加權(quán)限檢查就行:

# /app/main/views.py
class MyModelViewBase(ModelView):
    def is_accessible(self):
        if not current_user.is_active or not current_user.is_authenticated:
            return False
        if current_user.has_role('superuser'):
            return True
        return False
        
    def _handle_view(self, name, **kwargs):
        """
        Override builtin _handle_view in order to redirect users when a view is not accessible.
        """
        if not self.is_accessible():
            if current_user.is_authenticated:
                # permission denied
                abort(403)
            else:
                # login
                return redirect(url_for('security.login', next=request.url))

試一下悟耘,是不是沒權(quán)限訪問啦落蝙?乖乖地用admin登錄吧~
記得先創(chuàng)建superuser用戶,用戶名admin作煌,密碼自己定:

python manage.py initrole

4. 提供搜索功能

其實很簡單掘殴,ModelView里添加column_searchable_list就行。
注意,不同的模型狂魔,search字段要分開來抖部。如果放在一起,會查詢錯誤病瞳,因為不同模型(比如User, Article),不一定定義了Relationship關(guān)系悲酷。

# /app/main/views.py
class MyModelViewUser(MyModelViewBase):
    column_formatters = dict(
        password=lambda v, c, m, p: '**'+m.password[-6:],
        mps=lambda v, c, m, p: (m.subscribed_mps_str),  
        )
    column_searchable_list = (User.email, )
    
class MyModelViewMp(MyModelViewBase):
    column_formatters = dict(
        subscribers=lambda v, c, m, p: (m.subscribers_str), # '\n\p'.join
        articles=lambda v, c, m, p: (m.articles_str),
        )
    column_searchable_list = (Mp.weixinhao, Mp.mpName, )

admin.add_view(MyModelViewBase(Role, db.session))
admin.add_view(MyModelViewUser(User, db.session))
admin.add_view(MyModelViewMp(Mp, db.session))
admin.add_view(MyModelViewBase(Subscription, db.session))
admin.add_view(MyModelViewBase(Article, db.session))

Demo:http://vue2.heroku.com
源碼:https://code.csdn.net/Kevin_QQ/vue-tutorial/tree/master

敬請關(guān)注第7篇套菜!
Vue 2.0 起步(7) 大結(jié)局:公眾號文章抓取 - 微信公眾號RSS
Vue 2.0 起步(6) 后臺管理Flask-Admin更新

http://www.reibang.com/p/ab778fde3b99

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市设易,隨后出現(xiàn)的幾起案子逗柴,更是在濱河造成了極大的恐慌,老刑警劉巖顿肺,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戏溺,死亡現(xiàn)場離奇詭異,居然都是意外死亡屠尊,警方通過查閱死者的電腦和手機旷祸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讼昆,“玉大人托享,你說我怎么就攤上這事。” “怎么了闰围?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵赃绊,是天一觀的道長。 經(jīng)常有香客問我辫诅,道長凭戴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任炕矮,我火速辦了婚禮么夫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肤视。我一直安慰自己档痪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布邢滑。 她就那樣靜靜地躺著腐螟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪困后。 梳的紋絲不亂的頭發(fā)上乐纸,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音摇予,去河邊找鬼汽绢。 笑死,一個胖子當(dāng)著我的面吹牛侧戴,可吹牛的內(nèi)容都是我干的宁昭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼酗宋,長吁一口氣:“原來是場噩夢啊……” “哼积仗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜕猫,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤寂曹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后回右,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稀颁,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年楣黍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棱烂。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡租漂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哩治,我是刑警寧澤秃踩,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站业筏,受9級特大地震影響憔杨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蒜胖,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一消别、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧台谢,春花似錦寻狂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樊拓,卻和暖如春纠亚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筋夏。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工蒂胞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叁丧。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓啤誊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拥娄。 傳聞我的和親對象是個殘疾皇子蚊锹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容