上一篇: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
本篇完成功能:
- 對模型model,有CRUD基本功能:
CRUD就是Create位谋、Read山析、Update、Delete
-
顯示數(shù)據(jù)庫內(nèi)容掏父,包括關(guān)系表Relation對應(yīng)的內(nèi)容
比如:公眾號表Mp,能即時顯示每個公眾號秆剪,對應(yīng)有哪些Subscriber(訂閱者)赊淑、有哪些Article(文章),有些是通過多對多(M2M)查詢得到的仅讽。
超過20條記錄陶缺,自動分頁pagination
- 加上權(quán)限保護,只有superuser才能訪問后臺管理
當(dāng)然洁灵,也可以定制饱岸,讓不同權(quán)限用戶,能查看不同的內(nèi)容 -
提供搜索功能
比如徽千,在用戶表User苫费,我想把某個用戶加入黑名單,設(shè)想一下双抽,如果有成千上萬的用戶百框,不可能一頁頁找吧?所以加入搜索框:
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)容是空的第煮。
下面把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ù)顯示啦新荤!
等等揽趾!為什么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)存地址了
現(xiàn)在,給其它的模型Role, Subscription, Article都加上__repr__()
吧俄精!
加上之后询筏,另一個好處是:使用Create創(chuàng)建/Modify修改數(shù)據(jù)庫記錄時,一目了然竖慧,也不用面對一長串內(nèi)存地址了:
再看一下User頁面:
我的天嫌套,密碼都顯示出來啦!幸好是加密過的圾旨!
一長串看著有些礙眼踱讨,如何修改?而且萬一有些字段我們想保密碳胳,怎么辦呢勇蝙?
解決:
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))
再試試看,這下順眼多了吧馆衔。
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語句啊剖毯?圾笨?
如果把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了,按我們的期望顯示了:
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更新