下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

python教程的Flask扫盲系列

作者:千锋python学院     来源: 今日头条点击数:937发布时间: 2019-12-10 11:44:04

标签: PythonPython教程编程语言

大神带你学编程,欢迎选课

Python学习教程:Flask扫盲系列——权限设置。虽然大多数的语言可以既可被编译又可被解译,但大多数仅在一种情况下能够良好运行。

在一些编程系统中,程序要经过几个阶段的编译,一般而言,后阶段的编译往往更接近机器语言。这种常用的使用技巧最早在1960年代末用于BCPL,编译程序先编译一个叫做“0代码”的转换程序(representation),然后再使用虚拟器转换到可以运行于机器上的真实代码。

在前面的Python学习教程中,我们设置了系统的注册和登陆功能,已经基本满足了一个小型 Web 应用的需求。那么如果我们想通过这个网站来赚些小钱呢,就需要提供更高级的功能,当然这些高级功能不是免费开放的,设计一个权限系统,来控制高级应用的使用。

撰写高级功能

所谓的高级功能就是用户舍得花钱去购买的功能,像我这种喜欢薅羊毛的主,只配用用基础功能了。

我这里设计的高级功能,就是丰富 K 线图,在我们原来 K 线图的基础上添加移动平均线和成交量。

移动平均线

移动平均线是技术分析中非常普遍的一项指标,“平均”是指单位周期内的平均收盘价格,“移动”则是指将新的交易日收盘价纳入计算周期的同时,剔除最早的交易收盘价。

我们先来观察下通过 tushare 获取到的数据

python教程的Flask扫盲系列_Python_Python教程_编程语言_课课家 


可以看到,数据中的 Ma5、Ma10 和 Ma20 值可以用来制作移动平均线,可以通过折线图的方式来展现。

import pyecharts.options as opts
from pyecharts.charts import Line


def moving_average() -> Line:
c = (
Line()
.add_xaxis(df.index.tolist())
.add_yaxis("Ma5", df['ma5'].values.tolist(), is_smooth=True)
.add_yaxis("Ma10", df['ma10'].values.tolist(), is_smooth=True)
.add_yaxis("Ma20", df['ma20'].values.tolist(), is_smooth=True)
.set_global_opts(title_opts=opts.TitleOpts(title="移动平均线"))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)
return c

moving_average().render_notebook()

 

成交量

对于成交量,可以通过柱状图来展示,柱状图的高度,就是成交量的大小。把上涨时的成交量显示成红色,下跌时的成交量显示成绿色。

import pyecharts.options as opts
from pyecharts.charts import Line, Bar


volume_rise=[df.volume[x] if df.close[x] > df.open[x] else "0" for x in range(0, len(df.index))]
volume_drop=[df.volume[x] if df.close[x] <= df.open[x] else "0" for x in range(0, len(df.index))]


def volume() -> Bar:
c = (
Bar()
.add_xaxis(df.index.tolist())
.add_yaxis("volume_rise", volume_rise, stack=True, color=["#ec0000"])
.add_yaxis("volume_drop", volume_drop, stack=True, color=["#00da3c"])
.set_global_opts(title_opts=opts.TitleOpts(title="成交量"),
datazoom_opts=[opts.DataZoomOpts()],)
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)
return c


volume().render_notebook()

 

集成三个图表

下面我们就把三个图标,K 线图,移动平均线图和成交量图合成到一起
首先把 K 线图和移动平均线图层叠到一起

def kline_base() -> Kline:
kline = (
Kline()
.add_xaxis(df.index.tolist())
.add_yaxis("日K图", df[['open', 'close', 'low', 'high']].values.tolist(), markpoint_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max", value_dim="close")]
), markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max", value_dim="close")]
),
itemstyle_opts=opts.ItemStyleOpts(
color="#ec0000",
color0="#00da3c",
border_color="#8A0000",
border_color0="#008F28",
),
)
.set_global_opts(
yaxis_opts=opts.AxisOpts(is_scale=True,
splitarea_opts=opts.SplitAreaOpts(
is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)
),
),
xaxis_opts=opts.AxisOpts(is_scale=True,
axislabel_opts=opts.LabelOpts(rotate=-30)),
title_opts=opts.TitleOpts(title="股票走势"),
datazoom_opts=[opts.DataZoomOpts()],
toolbox_opts=opts.ToolboxOpts(is_show=True),
)
)
line = (
Line()
.add_xaxis(df.index.tolist())
.add_yaxis("Ma5", df['ma5'].values.tolist(), is_smooth=True)
.add_yaxis("Ma10", df['ma10'].values.tolist(), is_smooth=True)
.add_yaxis("Ma20", df['ma20'].values.tolist(), is_smooth=True)
.set_global_opts(title_opts=opts.TitleOpts(title="移动平均线"))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)
kline.overlap(line)
return kline

 


接下来再通过 grid 把成交量图添加到主图表中

...
bar = (
Bar()
.add_xaxis(df.index.tolist())
.add_yaxis("volume_rise", volume_rise, stack=True, color=["#ec0000"], )
.add_yaxis("volume_drop", volume_drop, stack=True, color=["#00da3c"], )
.set_global_opts(title_opts=opts.TitleOpts(),
legend_opts=opts.LegendOpts(pos_right="20%"))
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)

overlap_kline_line = kline.overlap(line)
grid = Grid()
grid.add(
overlap_kline_line,
grid_opts=opts.GridOpts(pos_left="10%", pos_right="8%", height="50%"),
)
grid.add(
bar,
grid_opts=opts.GridOpts(
pos_left="10%", pos_right="8%", pos_top="70%", height="16%"
),
)
...

 


至此,我们所谓的“高级”图表就完成了,下面就开始结合 Flask,嵌入我们产生的图表

编写各个图表页面

首先我们先把新产生的两个图表嵌入到 Web 应用中,每个图表都是一个独立的页面

后台函数

先来创建生成移动平均线和成交量图表的函数

# 移动平均线
def moving_average_chart(mydate, data_5, data_10, data_20, name) -> Line:
moving_average = (
Line()
.add_xaxis(mydate)
.add_yaxis("ma5", data_5, is_smooth=True)
.add_yaxis("ma10", data_10, is_smooth=True)
.add_yaxis("ma20", data_20, is_smooth=True)
.set_global_opts(title_opts=opts.TitleOpts(title="%s-移动平均线" % name),
datazoom_opts=[opts.DataZoomOpts()],
)
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)
return moving_average


# 成交量
def volume_chart(mydate, volume_rise, volume_drop, name) -> Bar:
bar = (
Bar()
.add_xaxis(mydate)
.add_yaxis("volume_rise", volume_rise, stack=True, color=["#ec0000"])
.add_yaxis("volume_drop", volume_drop, stack=True, color=["#00da3c"])
.set_global_opts(title_opts=opts.TitleOpts(title="%s-成交量" % name),
datazoom_opts=[opts.DataZoomOpts()],)
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
)
)
return bar

然后再修改 get_stock_data 函数,返回我们需要的数据

def get_stock_data(code, ctime):
df = ts.get_hist_data(code)
df_time = df[:ctime]
mydate = df_time.index.tolist()
kdata = df_time[['open', 'close', 'low', 'high']].values.tolist()
madata_5 = df_time['ma5'].values.tolist()
madata_10 = df_time['ma10'].values.tolist()
madata_20 = df_time['ma20'].values.tolist()
volume_rise = [df_time.volume[x] if df_time.close[x] > df_time.open[x] else "0" for x in range(0, len(df_time.index))]
volume_drop = [df_time.volume[x] if df_time.close[x] <= df_time.open[x] else "0" for x in range(0, len(df_time.index))]
return [mydate, kdata, madata_5, madata_10, madata_20, volume_rise, volume_drop]

接着再增加生成两个图表所对应的视图函数

@app.route("/Line", methods=['GET', 'POST'])
def get_moving_average():
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
if not stock_name:
stock_name = '平安银行'
if not query_time:
query_time = 30
if int(query_time) > 30:
if current_user.is_authenticated:
pass
else:
abort(403)

status, stock_code = check_stock(stock_name)
if status == 0:
return 'error stock code or name'
mydate, kdata, madata_5, madata_10, madata_20, volume_rise, volume_drop = get_stock_data(stock_code[0], int(query_time))
c = moving_average_chart(mydate, madata_5, madata_10, madata_20, stock_code[1])
return c.dump_options()


@app.route("/Bar", methods=['GET', 'POST'])
def get_volume():
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
if not stock_name:
stock_name = '平安银行'
if not query_time:
query_time = 30
if int(query_time) > 30:
if current_user.is_authenticated:
pass
else:
abort(403)

status, stock_code = check_stock(stock_name)
if status == 0:
return 'error stock code or name'
mydate, kdata, madata_5, madata_10, madata_20, volume_rise, volume_drop = get_stock_data(stock_code[0], int(query_time))
c = volume_chart(mydate, volume_rise, volume_drop, stock_code[1])
return c.dump_options()

然后还要添加对应的前端页面

@app.route("/mavg", methods=['GET', 'POST'])
def moving_average():
return render_template("mavg.html")


@app.route("/volume", methods=['GET', 'POST'])
def volume():
return render_template("volume.html")

最后创建上面的两个 html 文件,并修改

{% extends "base.html" %}

{% block title %}我的股票走势图{% endblock %}


{% block page_content %}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
<body>
<div id="form-div">
<form id="form1" onsubmit="return false" action="#" method="post">
<p id="p1">股票名称:
<input name="stockName" type="text" id="stockName" tabindex="1" size="16" value="" placeholder="股票名称"/>
<!-- <input type="button" onclick="add1();" value="添加" />-->
</p>
<p id="p2">查询时间:
<input name="queryTime" type="text" id="queryTime" tabindex="2" size="16" value="" placeholder="输入30查询近30天数据"/>
</p>
<p><input type="submit" value="查询" onclick="getData()"></p>
</form>
</div>
<div id="Bar"></div>
<script>
$(
function () {
var chart = echarts.init(document.getElementById('Bar'), 'white', {renderer: 'canvas'});
$.ajax({
type: "GET",
url: "http://127.0.0.1:5000/Bar",
dataType: 'json',
success: function (result) {
chart.setOption(result);
}
});
}
);
function getData() {
var chart = echarts.init(document.getElementById('Bar'), 'white', {renderer: 'canvas'});
$.ajax({
type: "POST",//方法类型
dataType: "json",//预期服务器返回的数据类型
url: "/Bar" ,//url
data: $('#form1').serialize(),
success: function (result) {
chart.setOption(result);
},
error: function(err) {
if (err.status === 403) {
alert("请先登陆系统!");
}
else {
alert("错误的股票代码!");
}
}
});
}
function add1(){
var input1 = document.createElement('input');
input1.setAttribute('type', 'text');
input1.setAttribute('name', 'organizers[]');

var btn1 = document.getElementById("p1");
//btn1.insertBefore(input1,null);
btn1.appendChild(input1);
}
</script>
</body>
{% endblock %}

{% block scripts %}
{{ super() }}
<script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
{% endblock %}

同时在 base.html 中添加入口地址

...
<ul class="nav navbar-nav">
<li><a href="{{ url_for('moving_average')}}">Moving Average</a></li>
</ul>
<ul class="nav navbar-nav">
<li><a href="{{ url_for('volume')}}">Volume</a></li>
</ul>
...

现在我们的 Web 应用就是下图的样子了


下面我们就可以进入今天的正题了,设置权限。

权限设计

定义表结构

首先定义权限表结构

class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('WebUser', backref='role')

@staticmethod
def init_roles():
roles = ['User', 'Admin']
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
db.session.add(role)
db.session.commit()

我们定义了两种权限,User 和 Admin,那么只有拥有 Admin 权限的用户才可以访问高级功能。

这里还使用了外键关联到了 WebUser 表上,所以需要同步修改 WebUser 表

# 用户表结构
class WebUser(UserMixin, db.Model):
__tablename__ = 'webuser'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), default=1)
...

因为我们修改了原始表的表结构,所以需要进行表结构的迁移操作,这里可以使用插件 flask-migrate 来帮助我们实现

表结构迁移

先安装 flask-migrate 插件

pip install flask-migrate

然后在程序中配置 flask_migrate

from flask_migrate import Migrate
...
migrate = Migrate(app, db, render_as_batch=True)
...

创建迁移仓库

flask db init

该命令会在当前目录下生成迁移文件夹,所有的迁移脚本都会保存在其中。

创建迁移脚本

flask db migrate

最后就是更新数据库,如果你和我一样是使用的 sqllite 数据库的话,那么需要对迁移脚本做些修改

打开 migrations 下 versions 里的 py 文件,找到语句 “batch_op.create_foreign_key”,修改如下

batch_op.create_foreign_key('role_key', 'roles', ['role_id'], ['id'])

然后再执行下面的命令

flask db upgrade

最后我们初始化角色
进入 flask shell,执行如下操作完成角色表的初始化

flask shell
from app import Role
Role.init_roles()

这样就完成了数据库的迁移和初始化。

权限校验

下面我们就可以开始编写权限校验部分了

校验函数

对于校验函数,我们可以写在 WebUser 类中,这样就可以通过 current_user 来调用

...
def is_admin(self):
if self.role_id is 2:
return True
else:
return False
...

再创建一个必须是 admin role 的用户才能访问的视图

@app.route('/fullchart/', methods=['GET', 'POST'])
@login_required
def fullchart():
if current_user.is_admin():
return "OK"
flash('You have not permission to access this page')
return redirect(url_for('index'))

整合前后端

把页面入口添加到 base.html 页面上

<ul class="nav navbar-nav">
<li><a href="{{ url_for('fullchart')}}">Full Chart</a></li>
</ul>

然后新建一个 full chart 函数,用于产生高级图表

# full chart
def full_chart(mydate, kdata, data_5, data_10, data_20, volume_rise, volume_drop, name):
kline = (
Kline()
...

同样的,编写为前端提供的接口函数

@app.route("/FullChart", methods=['GET', 'POST'])
def get_fullcharte():
stock_name = request.form.get('stockName')
query_time = request.form.get('queryTime')
...

最后创建 fullchart.html 并做响应修改,同时把 fullchart 视图函数指向该模板

@app.route('/fullchart/', methods=['GET', 'POST'])
@login_required
def fullchart():
if current_user.is_admin():
return render_template('fullchart.html')
flash('You have not permission to access this page')
return redirect(url_for('index'))

至此,我们的高级图表功能也完成了

 


只有拥有 admin 权限的用户才能访问哦!伙伴们有不清楚的地方可以留言哈,更多的Python学习教程也会继续为大家更新!

编程语言(programming language)是一种被标准化的交流技巧,用来向计算机发出指令,定义计算机程序,让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动的一种计算机语言。

赞(11)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程