YFCMF-TP6

YFCMF-TP6 基于ThinkPHP6+FastAdmin的快速开发框架

主要特性

  • 基于Auth验证的权限管理系统
  • 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
  • 支持单管理员多角色
  • 支持管理子级数据或个人数据
  • 强大的一键生成功能
  • 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等
  • 一键压缩打包JS和CSS文件,一键CDN静态资源部署
  • 一键生成控制器菜单和规则
  • 一键生成API接口文档
  • 完善的前端功能组件开发
  • 基于AdminLTE二次开发
  • 基于Bootstrap开发,自适应手机、平板、PC
  • 基于RequireJS进行JS模块管理,按需加载
  • 基于Less进行样式开发
  • 基于Bower进行前端组件包管理
  • 强大的插件扩展功能,在线安装卸载升级插件
  • 通用的会员模块和API模块
  • 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
  • 二级域名部署支持,同时域名支持绑定到插件
  • 多语言支持,服务端及客户端支持
  • 整合第三方短信接口(阿里云、腾讯云短信)
  • 无缝整合第三方云存储(七牛、阿里云OSS、又拍云)功能
  • 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器)
  • 第三方登录(QQ、微信、微博)整合
  • 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付
  • 丰富的插件应用市场

各种设备自适应

响应式的网站设计能够对用户产生友好度,并且对于不同的分辨率能够灵活的进行操作应用。 简洁通俗表达就是页面宽度可以自适应屏幕大小,一个网站 PC、手机、PAD 通吃,页面地址一致。
一个字 “酷 “,可以用 PC 浏览器拉动窗口大小,网站内容显示依旧在设计之内,用户体验非常不错。 一个字 “省”,一个网站 PC、手机、PAD 通吃,这样就不用花那么多心思去维护多个网站,无论是制作还是数据内容。
基于 HTML5 技术
HTML5 对于用户来说,提高了用户体验,加强了视觉感受。HTML5 技术在移动端,能够让应用程序回归到网页,并对网页的功能进行扩展,操作更加简单,用户体验更好。
HTML5 技术跨平台,适配多终端。对于搜索引擎来说,HTML5 新增的标签,使搜索引擎更加容易抓取和索引网页,从而驱动网站获得更多的点击流量。
人性化的后台管理
传统的企业网站管理系统是以技术人员的角度出发,设计了很多复杂的功能,并且操作流程上也很复杂,对于最终要操控这个系统的管理员来说并不是很人性化,YFCMF 所做的只是简化不必要的功能,从操作习惯下合理地布局和设计界面,让最普通的用户,即使没有网站管理的经营,也能很容易上手我们的系统。

问题反馈

在使用中有任何问题,请使用以下联系方式联系我们

交流社区: https://bbs.iuok.cn

QQ群: 345183861

Email: (ice#sbing.vip, 把#换成@)

Github: https://github.com/0377/yfcmf-tp6

Gitee: https://gitee.com/nymondo/yfcmf-tp6

特别鸣谢

感谢以下的项目,排名不分先后

Fastadmin:https://www.fastadmin.net

ThinkPHP:http://www.thinkphp.cn

AdminLTE:https://adminlte.io

Bootstrap:http://getbootstrap.com

jQuery:http://jquery.com

Bootstrap-table:https://github.com/wenzhixin/bootstrap-table

Nice-validator: https://validator.niceue.com

SelectPage: https://github.com/TerryZ/SelectPage

Flask 微型 Python 框架

Flask是一个轻量级的基于Python的web框架。

一.Flask框架的诞生:

Flask诞生于2010年, Armin Ronacher的一个愚人节玩笑。不过现在已经是一个用python语言基于Werkzeug工具箱编写的轻量级web开发框架,它主要面向需求简单,项目周期短的小应用。

二.安装

通过pip3安装Flask即可:

$ sudo pip3 install Flask

进入python交互模式看下Flask的介绍和版本:

$ python3

>>> import flask
>>> print(flask.__doc__)

flask
~~~~~
A microframework based on Werkzeug. It's extensively documented
and follows best practice patterns.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
>>> print(flask.__version__)
1.0.2

3.从 Hello World 开始

本节主要内容:使用Flask写一个显示”Hello World!”的web程序,如何配置、调试Flask。

1 Hello World

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

static和templates目录是默认配置,其中static用来存放静态资源,例如图片、js、css文件等。templates存放模板文件。
我们的网站逻辑基本在server.py文件中,当然,也可以给这个文件起其他的名字。

在server.py中加入以下内容:

from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    return 'Hello World!'
 
if __name__ == '__main__':
    app.run()

运行server.py:

$ python3 server.py 
 * Running on http://127.0.0.1:5000/

打开浏览器访问http://127.0.0.1:5000/,浏览页面上将出现Hello World!。
终端里会显示下面的信息:

127.0.0.1 - - [16/May/2014 10:29:08] "GET / HTTP/1.1" 200 -

变量app是一个Flask实例,通过下面的方式:

@app.route('/')
def hello_world():
return 'Hello World!'

当客户端访问/时,将响应hello_world()函数返回的内容。注意,这不是返回Hello World!这么简单,Hello World!只是HTTP响应报文的实体部分,状态码等信息既可以由Flask自动处理,也可以通过编程来制定。

2 修改Flask的配置

app = Flask(__name__)

上面的代码中,python内置变量__name__的值是字符串__main__ 。Flask类将这个参数作为程序名称。当然这个是可以自定义的,比如app = Flask(“my-app”)。
Flask默认使用static目录存放静态资源,templates目录存放模板,这是可以通过设置参数更改的:

app = Flask("my-app", static_folder="path1", template_folder="path2")

更多参数请参考__doc__:

from flask import Flask
print(Flask.__doc__)

3 调试模式

上面的server.py中以app.run()方式运行,这种方式下,如果服务器端出现错误是不会在客户端显示的。但是在开发环境中,显示错误信息是很有必要的,要显示错误信息,应该以下面的方式运行Flask:

app.run(debug=True)

将debug设置为True的另一个好处是,程序启动后,会自动检测源码是否发生变化,若有变化则自动重启程序。这可以帮我们省下很多时间。

4 绑定IP和端口

默认情况下,Flask绑定IP为127.0.0.1,端口为5000。我们也可以通过下面的方式自定义:

app.run(host='0.0.0.0', port=80, debug=True)

0.0.0.0代表电脑所有的IP。80是HTTP网站服务的默认端口。什么是默认?比如,我们访问网站http://www.example.com,其实是访问的http://www.example.com:80,只不过:80可以省略不写。

由于绑定了80端口,需要使用root权限运行server.py。也就是:

$ sudo python3 server.py

5 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-001

 

4.获取 URL 参数

URL参数是出现在url中的键值对,例如http://127.0.0.1:5000/?disp=3中的url参数是{'disp':3}

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 列出所有的url参数

在server.py中添加以下内容:

from flask import Flask, request
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return request.args.__str__()
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

在浏览器中访问http://127.0.0.1:5000/?user=Flask&time&p=7&p=8,将显示:

ImmutableMultiDict([('user', 'Flask'), ('time', ''), ('p', '7'), ('p', '8')])

较新的浏览器也支持直接在url中输入中文(最新的火狐浏览器内部会帮忙将中文转换成符合URL规范的数据),在浏览器中访问http://127.0.0.1:5000/?info=这是爱,,将显示:

ImmutableMultiDict([('info', '这是爱,')])

浏览器传给我们的Flask服务的数据长什么样子呢?可以通过request.full_path和request.path来看一下:

from flask import Flask, request
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    print(request.path)
    print(request.full_path)
    return request.args.__str__()
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

浏览器访问http://127.0.0.1:5000/?info=这是爱,,运行server.py的终端会输出:

/
/?info=%E8%BF%99%E6%98%AF%E7%88%B1%EF%BC%8C

3 获取某个指定的参数

例如,要获取键info对应的值,如下修改server.py:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    return request.args.get('info')
 
if __name__ == '__main__':
    app.run(port=5000)

运行server.py,在浏览器中访问http://127.0.0.1:5000/?info=hello,浏览器将显示:

hello

不过,当我们访问http://127.0.0.1:5000/时候却出现了500错误,浏览器显示:

如果开启了Debug模式,会显示:

为什么为这样?

这是因为没有在URL参数中找到info。所以request.args.get(‘info’)返回Python内置的None,而Flask不允许返回None。

解决方法很简单,我们先判断下它是不是None:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    r = request.args.get('info')
    if r==None:
        # do something
        return ''
    return r
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

另外一个方法是,设置默认值,也就是取不到数据时用这个值:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    r = request.args.get('info', 'hi')
    return r
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

函数request.args.get的第二个参数用来设置默认值。此时在浏览器访问http://127.0.0.1:5000/,将显示:

hi

4 如何处理多值

还记得上面有一次请求是这样的吗? http://127.0.0.1:5000/?user=Flask&time&p=7&p=8,仔细看下,p有两个值。

如果我们的代码是:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    r = request.args.get('p')
    return r
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

在浏览器中请求时,我们只会看到7。如果我们需要把p的所有值都获取到,该怎么办?

不用get,用getlist:

from flask import Flask, request
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    r = request.args.getlist('p')  # 返回一个list
    return str(r)
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

浏览器输入 http://127.0.0.1:5000/?user=Flask&time&p=7&p=8,我们会看到[‘7’, ‘8’]。

5 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-002

五.获取POST方法传送的数据

作为一种HTTP请求方法,POST用于向指定的资源提交要被处理的数据。我们在某网站注册用户、写文章等时候,需要将数据传递到网站服务器中。并不适合将数据放到URL参数中,密码放到URL参数中容易被看到,文章数据又太多,浏览器不一定支持太长长度的URL。这时,一般使用POST方法。

本文使用python的requests库模拟浏览器。
安装方法:

$ sudo pip3 install requests

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

<h3″>2 查看POST数据内容

以用户注册为例子,我们需要向服务器/register传送用户名name和密码password。如下编写HelloWorld/server.py。

from flask import Flask, request
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/register', methods=['POST'])
def register():
    print(request.headers)
    print(request.stream.read())
    return 'welcome'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

`@app.route(‘/register’, methods=[‘POST’])是指url/register只接受POST方法。可以根据需要修改methods`参数,例如如果想要让它同时支持GET和POST,这样写:

@app.route('/register', methods=['GET', 'POST']) 

浏览器模拟工具client.py内容如下:

import requests

user_info = {'name': 'letian', 'password': '123'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)

print(r.text)

运行HelloWorld/server.py,然后运行client.py。client.py将输出:

welcome

而HelloWorld/server.py在终端中输出以下调试信息(通过print输出):

Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 24
Content-Type: application/x-www-form-urlencoded
 
 
b'name=letian&password=123'

前6行是client.py生成的HTTP请求头,由print(request.headers)输出。

请求体的数据,我们通过print(request.stream.read())输出,结果是:

b'name=letian&password=123'

3 解析POST数据

上面,我们看到post的数据内容是:

b'name=letian&password=123'

我们要想办法把我们要的name、password提取出来,怎么做呢?自己写?不用,Flask已经内置了解析器request.form。

我们将服务代码改成:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/register', methods=['POST'])
def register():
    print(request.headers)
    # print(request.stream.read()) # 不要用,否则下面的form取不到数据
    print(request.form)
    print(request.form['name'])
    print(request.form.get('name'))
    print(request.form.getlist('name'))
    print(request.form.get('nickname', default='little apple'))
    return 'welcome'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

执行client.py请求数据,服务器代码会在终端输出:

Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 24
Content-Type: application/x-www-form-urlencoded
 
 
ImmutableMultiDict([('name', 'letian'), ('password', '123')])
letian
letian
['letian']
little apple

request.form会自动解析数据。

request.form[‘name’]和request.form.get(‘name’)都可以获取name对应的值。对于request.form.get()可以为参数default指定值以作为默认值。所以:

print(request.form.get('nickname', default='little apple'))

输出的是默认值

little apple

如果name有多个值,可以使用request.form.getlist(‘name’),该方法将返回一个列表。我们将client.py改一下:

import requests
 
user_info = {'name': ['letian', 'letian2'], 'password': '123'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)
 
print(r.text)

此时运行client.py,print(request.form.getlist(‘name’))将输出:

[u'letian', u'letian2']

4 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-003

六. 处理和响应JSON数据

使用 HTTP POST 方法传到网站服务器的数据格式可以有很多种,比如「5. 获取POST方法传送的数据」讲到的name=letian&password=123这种用过&符号分割的key-value键值对格式。我们也可以用JSON格式、XML格式。相比XML的重量、规范繁琐,JSON显得非常小巧和易用。

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 处理JSON格式的请求数据

如果POST的数据是JSON格式,request.json会自动将json数据转换成Python类型(字典或者列表)。

编写server.py:

from flask import Flask, request
 
app = Flask("my-app")
 
 
@app.route('/')
def hello_world():
    return 'Hello World!'
 
 
@app.route('/add', methods=['POST'])
def add():
    print(request.headers)
    print(type(request.json))
    print(request.json)
    result = request.json['a'] + request.json['b']
    return str(result)
 
 
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=True)

编写client.py模拟浏览器请求:

import requests
 
json_data = {'a': 1, 'b': 2}
 
r = requests.post("http://127.0.0.1:5000/add", json=json_data)
 
print(r.text)

运行server.py,然后运行client.py,client.py 会在终端输出:

3

server.py 会在终端输出:

Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 16
Content-Type: application/json
 
 
<class 'dict'>
{'a': 1, 'b': 2}

注意,请求头中Content-Type的值是application/json。

3 响应JSON-方案1

响应JSON时,除了要把响应体改成JSON格式,响应头的Content-Type也要设置为application/json。
编写server2.py:

from flask import Flask, request, Response
import json
 
app = Flask("my-app")
 
 
@app.route('/')
def hello_world():
    return 'Hello World!'
 
 
@app.route('/add', methods=['POST'])
def add():
    result = {'sum': request.json['a'] + request.json['b']}
    return Response(json.dumps(result),  mimetype='application/json')
 
 
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=True)

修改后运行。

编写client2.py:

import requests
 
json_data = {'a': 1, 'b': 2}
 
r = requests.post("http://127.0.0.1:5000/add", json=json_data)
 
print(r.headers)
print(r.text)

运行client.py,将显示:

{'Content-Type': 'application/json', 'Content-Length': '10', 'Server': 'Werkzeug/0.14.1 Python/3.6.4', 'Date': 'Sat, 07 Jul 2018 05:23:00 GMT'}
{"sum": 3}

上面第一段内容是服务器的响应头,第二段内容是响应体,也就是服务器返回的JSON格式数据。

另外,如果需要服务器的HTTP响应头具有更好的可定制性,比如自定义Server,可以如下修改add()函数:

@app.route('/add', methods=['POST'])
def add():
    result = {'sum': request.json['a'] + request.json['b']}
    resp = Response(json.dumps(result),  mimetype='application/json')
    resp.headers.add('Server', 'python flask')
    return resp

client2.py运行后会输出:

{'Content-Type': 'application/json', 'Content-Length': '10', 'Server': 'python flask', 'Date': 'Sat, 07 Jul 2018 05:26:40 GMT'}
{"sum": 3}

4 响应JSON-方案2

使用 jsonify 工具函数即可。

from flask import Flask, request, jsonify
 
app = Flask("my-app")
 
 
@app.route('/')
def hello_world():
    return 'Hello World!'
 
 
@app.route('/add', methods=['POST'])
def add():
    result = {'sum': request.json['a'] + request.json['b']}
    return jsonify(result)
 
 
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=True)

5 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-004

七. 上传文件

上传文件,一般也是用POST方法。

7.1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 上传文件

这一部分的代码参考自How to upload a file to the server in Flask。

我们以上传图片为例:
假设将上传的图片只允许’png’、’jpg’、’jpeg’、’gif’这四种格式,通过url/upload使用POST上传,上传的图片存放在服务器端的static/uploads目录下。

首先在项目HelloWorld中创建目录static/uploads:

mkdir HelloWorld/static/uploads

werkzeug库可以判断文件名是否安全,例如防止文件名是../../../a.png,安装这个库:

$ sudo pip3 install werkzeug

server.py代码:

from flask import Flask, request
 
from werkzeug.utils import secure_filename
import os
 
app = Flask(__name__)
 
# 文件上传目录
app.config['UPLOAD_FOLDER'] = 'static/uploads/'
# 支持的文件格式
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}  # 集合类型
 
 
# 判断文件名是否是我们支持的格式
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/upload', methods=['POST'])
def upload():
    upload_file = request.files['image']
    if upload_file and allowed_file(upload_file.filename):
        filename = secure_filename(upload_file.filename)
        # 将文件保存到 static/uploads 目录,文件名同上传时使用的文件名
        upload_file.save(os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], filename))
        return 'info is '+request.form.get('info', '')+'. success'
    else:
        return 'failed'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

app.config中的config是字典的子类,可以用来设置自有的配置信息,也可以设置自己的配置信息。函数allowed_file(filename)用来判断filename是否有后缀以及后缀是否在app.config[‘ALLOWED_EXTENSIONS’]中。

客户端上传的图片必须以image01标识。upload_file是上传文件对应的对象。app.root_path获取server.py所在目录在文件系统中的绝对路径。upload_file.save(path)用来将upload_file保存在服务器的文件系统中,参数最好是绝对路径,否则会报错(网上很多代码都是使用相对路径,但是笔者在使用相对路径时总是报错,说找不到路径)。函数os.path.join()用来将使用合适的路径分隔符将路径组合起来。

好了,定制客户端client.py:

import requests
 
file_data = {'image': open('Lenna.jpg', 'rb')}
 
user_info = {'info': 'Lenna'}
 
r = requests.post("http://127.0.0.1:5000/upload", data=user_info, files=file_data)
 
print(r.text)

运行client.py,当前目录下的Lenna.jpg将上传到服务器。

然后,我们可以在static/uploads中看到文件Lenna.jpg。

要控制上产文件的大小,可以设置请求实体的大小,例如:

app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 #16MB

不过,在处理上传文件时候,需要使用try:…except:…。

如果要获取上传文件的内容可以:

file_content = request.files['image'].stream.read()

3 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-005

八. Restful URL

简单来说,Restful URL可以看做是对 URL 参数的替代。

8.1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 编写代码

编辑server.py:

from flask import Flask
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/user/')
def user(username):
    print(username)
    print(type(username))
    return 'hello ' + username
 
 
@app.route('/user//friends')
def user_friends(username):
    print(username)
    print(type(username))
    return 'hello ' + username
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

运行HelloWorld/server.py。使用浏览器访问http://127.0.0.1:5000/user/letian,HelloWorld/server.py将输出:

letian
<class 'str'>

而访问http://127.0.0.1:5000/user/letian/,响应为404 Not Found。

浏览器访问http://127.0.0.1:5000/user/letian/friends,可以看到:

Hello letian. They are your friends.

HelloWorld/server.py输出:

letian
<class 'str'>

3 转换类型

由上面的示例可以看出,使用 Restful URL 得到的变量默认为str对象。如果我们需要通过分页显示查询结果,那么需要在url中有数字来指定页数。按照上面方法,可以在获取str类型页数变量后,将其转换为int类型。不过,还有更方便的方法,就是用flask内置的转换机制,即在route中指定该如何转换。

新的服务器代码:

from flask import Flask
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/page/')
def page(num):
    print(num)
    print(type(num))
    return 'hello world'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

`@app.route(‘/page/int:num‘)`会将num变量自动转换成int类型。

运行上面的程序,在浏览器中访问http://127.0.0.1:5000/page/1,HelloWorld/server.py将输出如下内容:

1
<class 'int'>

如果访问的是http://127.0.0.1:5000/page/asd,我们会得到404响应。

在官方资料中,说是有3个默认的转换器:

int     accepts integers
float     like int but for floating point values
path     like the default but also accepts slashes

看起来够用了。

4 一个有趣的用法

如下编写服务器代码:

from flask import Flask
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/page/-')
def page(num1, num2):
    print(num1)
    print(num2)
    return 'hello world'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

在浏览器中访问http://127.0.0.1:5000/page/11-22,HelloWorld/server.py会输出:

11
22

5 编写转换器

自定义的转换器是一个继承werkzeug.routing.BaseConverter的类,修改to_python和to_url方法即可。to_python方法用于将url中的变量转换后供被`@app.route包装的函数使用,to_url方法用于flask.url_for`中的参数转换。

下面是一个示例,将HelloWorld/server.py修改如下:

from flask import Flask, url_for
 
from werkzeug.routing import BaseConverter
 
 
class MyIntConverter(BaseConverter):
 
    def __init__(self, url_map):
        super(MyIntConverter, self).__init__(url_map)
 
    def to_python(self, value):
        return int(value)
 
    def to_url(self, value):
        return value * 2
 
 
app = Flask(__name__)
app.url_map.converters['my_int'] = MyIntConverter
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/page/')
def page(num):
    print(num)
    print(url_for('page', num=123))   # page 对应的是 page函数 ,num 对应对应`/page/`中的num,必须是str
    return 'hello world'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

浏览器访问http://127.0.0.1:5000/page/123后,HelloWorld/server.py的输出信息是:

123
/page/123123

6 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-006

7 值得读

理解RESTful架构

9. 使用url_for生成链接

工具函数url_for可以让你以软编码的形式生成url,提供开发效率。

9.1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 编写代码

编辑HelloWorld/server.py:

from flask import Flask, url_for
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    pass
 
@app.route('/user/')
def user(name):
    pass
 
@app.route('/page/')
def page(num):
    pass
 
@app.route('/test')
def test():
    print(url_for('hello_world'))
    print(url_for('user', name='letian'))
    print(url_for('page', num=1, q='hadoop mapreduce 10%3'))
    print(url_for('static', filename='uploads/01.jpg'))
    return 'Hello'
 
if __name__ == '__main__':
    app.run(debug=True)

运行HelloWorld/server.py。然后在浏览器中访问http://127.0.0.1:5000/test,HelloWorld/server.py将输出以下信息:

/
/user/letian
/page/1?q=hadoop+mapreduce+10%253
/static/uploads/01.jpg

3 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-007

十. 使用redirect重定向网址

redirect函数用于重定向,实现机制很简单,就是向客户端(浏览器)发送一个重定向的HTTP报文,浏览器会去访问报文中指定的url。

10.1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 编写代码

使用redirect时,给它一个字符串类型的参数就行了。
编辑HelloWorld/server.py:

from flask import Flask, url_for, redirect
 
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    return 'hello world'
 
@app.route('/test1')
def test1():
    print('this is test1')
    return redirect(url_for('test2'))
 
@app.route('/test2')
def test2():
    print('this is test2')
    return 'this is test2'
 
if __name__ == '__main__':
    app.run(debug=True)

运行HelloWorld/server.py,在浏览器中访问http://127.0.0.1:5000/test1,浏览器的url会变成http://127.0.0.1:5000/test2,并显示:

this is test2

3 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-008

十一. 使用Jinja2模板引擎

模板引擎负责MVC中的V(view,视图)这一部分。Flask默认使用Jinja2模板引擎。

Flask与模板相关的函数有:

  • flask.render_template(template_name_or_list, **context)
    Renders a template from the template folder with the given context.
  • flask.render_template_string(source, **context)
    Renders a template from the given template source string with the given context.
  • flask.get_template_attribute(template_name, attribute)
    Loads a macro (or variable) a template exports. This can be used to invoke a macro from within Python code.

这其中常用的就是前两个函数。

这个实例中使用了模板继承、if判断、for循环。

11.1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 创建并编辑HelloWorld/templates/default.html

内容如下:

    

 

    {% block body %}{% endblock %}
 
 
```
 
可以看到,在``标签中使用了if判断,如果给模板传递了`page_title`变量,显示之,否则,不显示。
 
``标签中定义了一个名为`body`的block,用来被其他模板文件继承。
 
### 11.3 创建并编辑HelloWorld/templates/user_info.html
内容如下:
 
```
{% extends "default.html" %}
 
{% block body %}
    {% for key in user_info %}
 
        {{ key }}: {{ user_info[key] }} 
 
 
    {% endfor %}
{% endblock %}

变量user_info应该是一个字典,for循环用来循环输出键值对。

4 编辑HelloWorld/server.py

内容如下:

from flask import Flask, render_template
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/user')
def user():
    user_info = {
        'name': 'letian',
        'email': '[email protected]',
        'age':0,
        'github': 'https://github.com/letiantian'
    }
    return render_template('user_info.html', page_title='letian\'s info', user_info=user_info)
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

render_template()函数的第一个参数指定模板文件,后面的参数是要传递的数据。

5 运行与测试

运行HelloWorld/server.py:

$ python3 HelloWorld/server.py

在浏览器中访问http://127.0.0.1:5000/user,效果图如下:

查看网页源码:

        name: letian 
        email: [email protected] 
        age: 0 
        github: https://github.com/letiantian 

6 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-009

十二. 自定义404等错误的响应

要处理HTTP错误,可以使用flask.abort函数。

1 示例1:简单入门

建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

代码

编辑HelloWorld/server.py:

from flask import Flask, render_template_string, abort
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/user')
def user():
    abort(401)  # Unauthorized 未授权
    print('Unauthorized, 请先登录')
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

效果

运行HelloWorld/server.py,浏览器访问http://127.0.0.1:5000/user,效果如下:

要注意的是,HelloWorld/server.py中abort(401)后的print并没有执行。

2 示例2:自定义错误页面

代码
将服务器代码改为:

from flask import Flask, render_template_string, abort
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/user')
def user():
    abort(401)  # Unauthorized


@app.errorhandler(401)
def page_unauthorized(error):
    return render_template_string('<h1> Unauthorized </h1><h2>{{ error_info }}</h2>', error_info=error), 401
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

page_unauthorized函数返回的是一个元组,401 代表HTTP 响应状态码。如果省略401,则响应状态码会变成默认的 200。

效果

运行HelloWorld/server.py,浏览器访问http://127.0.0.1:5000/user,效果如下:

 

3 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-010

十三. 用户会话

session用来记录用户的登录状态,一般基于cookie实现。

下面是一个简单的示例。

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 编辑HelloWorld/server.py

内容如下:

from flask import Flask, render_template_string, \
    session, request, redirect, url_for
 
app = Flask(__name__)
 
app.secret_key = 'F12Zr47j\3yX R~X@H!jLwf/T'
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/login')
def login():
    page = '''
        <form action="{{ url_for('do_login') }}" method="post">
        <p>name: <input type="text" name="user_name" /></p>
        <input type="submit" value="Submit" />
    </form>
    '''
return render_template_string(page)

@app.route('/do_login', methods=['POST'])
def do_login():
    name = request.form.get('user_name')
    session['user_name'] = name
    return 'success'
 
 
@app.route('/show')
def show():
    return session['user_name']
 
 
@app.route('/logout')
def logout():
    session.pop('user_name', None)
    return redirect(url_for('login'))
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

3 代码的含义

app.secret_key用于给session加密。

/login中将向用户展示一个表单,要求输入一个名字,submit后将数据以post的方式传递给/do_login/do_login将名字存放在session中。

如果用户成功登录,访问/show时会显示用户的名字。此时,打开firebug等调试工具,选择session面板,会看到有一个cookie的名称为session

/logout用于登出,通过将session中的user_name字段pop即可。Flask中的session基于字典类型实现,调用pop方法时会返回pop的键对应的值;如果要pop的键并不存在,那么返回值是pop()的第二个参数。

另外,使用redirect()重定向时,一定要在前面加上return

4 效果

进入http://127.0.0.1:5000/login,输入name,点击submit:

进入http://127.0.0.1:5000/show查看session中存储的name:

5 设置sessin的有效时间

下面这段代码来自Is there an easy way to make sessions timeout in flask?

from datetime import timedelta
from flask import session, app
 
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)

这段代码将session的有效时间设置为5分钟。

6 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-011

十四. 使用Cookie

Cookie是存储在客户端的记录访问者状态的数据。具体原理,请见 http://zh.wikipedia.org/wiki/Cookie 。 常用的用于记录用户登录状态的session大多是基于cookie实现的。
cookie可以借助flask.Response来实现。下面是一个示例。

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 代码

修改HelloWorld/server.py:

from flask import Flask, request, Response, make_response
import time
 
app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/add')
def login():
    res = Response('add cookies')
    res.set_cookie(key='name', value='letian', expires=time.time()+6*60)
    return res
 
 
@app.route('/show')
def show():
    return request.cookies.__str__()
 
 
@app.route('/del')
def del_cookie():
    res = Response('delete cookies')
    res.set_cookie('name', '', expires=0)
    return res
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)

由上可以看到,可以使用Response.set_cookie添加和删除cookie。expires参数用来设置cookie有效时间,它的值可以是datetime对象或者unix时间戳,笔者使用的是unix时间戳。

res.set_cookie(key='name', value='letian', expires=time.time()+6*60)

上面的expire参数的值表示cookie在从现在开始的6分钟内都是有效的。

要删除cookie,将expire参数的值设为0即可:

res.set_cookie('name', '', expires=0)

set_cookie()函数的原型如下:

set_cookie(key, value=’’, max_age=None, expires=None, path=’/‘, domain=None, secure=None, httponly=False)

Sets a cookie. The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data, too.
Parameters:

     key – the key (name) of the cookie to be set.
value – the value of the cookie.
max_age – should be a number of seconds, or None (default) if the cookie should last only as long as the client’s browser session.

     expires – should be a datetime object or UNIX timestamp.

     domain – if you want to set a cross-domain cookie. For example, domain=”.example.com” will set a cookie that is readable by the domain www.example.com, foo.example.com etc. Otherwise, a cookie will only be readable by the domain that set it.

     path – limits the cookie to a given path, per default it will span the whole domain.

3 运行与测试

运行HelloWorld/server.py:

$ python3 HelloWorld/server.py

使用浏览器打开http://127.0.0.1:5000/add,浏览器界面会显示

add cookies

下面查看一下cookie,如果使用firefox浏览器,可以用firebug插件查看。打开firebug,选择Cookies选项,刷新页面,可以看到名为name的cookie,其值为letian。

在“网络”选项中,可以查看响应头中类似下面内容的设置cookie的HTTP「指令」:

Set-Cookie: name=letian; Expires=Sun, 29-Jun-2014 05:16:27 GMT; Path=/

在cookie有效期间,使用浏览器访问http://127.0.0.1:5000/show,可以看到:

{'name': 'letian'}

4 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-012

十五. 闪存系统 flashing system

Flask的闪存系统(flashing system)用于向用户提供反馈信息,这些反馈信息一般是对用户上一次操作的反馈。反馈信息是存储在服务器端的,当服务器向客户端返回反馈信息后,这些反馈信息会被服务器端删除。

下面是一个示例。

1 建立Flask项目

按照以下命令建立Flask项目HelloWorld:

mkdir HelloWorld
mkdir HelloWorld/static
mkdir HelloWorld/templates
touch HelloWorld/server.py

2 编写HelloWorld/server.py

内容如下:

from flask import Flask, flash, get_flashed_messages
import time
 
app = Flask(__name__)
app.secret_key = 'some_secret'
 
 
@app.route('/')
def index():
    return 'hi'
 
 
@app.route('/gen')
def gen():
    info = 'access at '+ time.time().__str__()
    flash(info)
    return info
 
 
@app.route('/show1')
def show1():
    return get_flashed_messages().__str__()
 
 
@app.route('/show2')
def show2():
    return get_flashed_messages().__str__()
 
 
if __name__ == "__main__":
    app.run(port=5000, debug=True)

3 效果

运行服务器:

$ python3 HelloWorld/server.py

打开浏览器,访问http://127.0.0.1:5000/gen,浏览器界面显示(注意,时间戳是动态生成的,每次都会不一样,除非并行访问):

access at 1404020982.83

查看浏览器的cookie,可以看到session,其对应的内容是:

.eJyrVopPy0kszkgtVrKKrlZSKIFQSUpWSknhYVXJRm55UYG2tkq1OlDRyHC_rKgIvypPdzcDTxdXA1-XwHLfLEdTfxfPUn8XX6DKWCAEAJKBGq8.BpE6dg.F1VURZa7VqU9bvbC4XIBO9-3Y4Y

再一次访问http://127.0.0.1:5000/gen,浏览器界面显示:

access at 1404021130.32

cookie中session发生了变化,新的内容是:

.eJyrVopPy0kszkgtVrKKrlZSKIFQSUpWSknhYVXJRm55UYG2tkq1OlDRyHC_rKgIvypPdzcDTxdXA1-XwHLfLEdTfxfPUn8XX6DKWLBaMg1yrfCtciz1rfIEGxRbCwAhGjC5.BpE7Cg.Cb_B_k2otqczhknGnpNjQ5u4dqw

然后使用浏览器访问http://127.0.0.1:5000/show1,浏览器界面显示:

['access at 1404020982.83', 'access at 1404021130.32']

这个列表中的内容也就是上面的两次访问http://127.0.0.1:5000/gen得到的内容。此时,cookie中已经没有session了。

如果使用浏览器访问http://127.0.0.1:5000/show1或者http://127.0.0.1:5000/show2,只会得到:

[]

4 高级用法

flash系统也支持对flash的内容进行分类。修改HelloWorld/server.py内容:

from flask import Flask, flash, get_flashed_messages
import time
 
app = Flask(__name__)
app.secret_key = 'some_secret'
 
 
@app.route('/')
def index():
    return 'hi'
 
 
@app.route('/gen')
def gen():
    info = 'access at '+ time.time().__str__()
    flash('show1 '+info, category='show1')
    flash('show2 '+info, category='show2')
    return info
 
 
@app.route('/show1')
def show1():
    return get_flashed_messages(category_filter='show1').__str__()
 
@app.route('/show2')
def show2():
    return get_flashed_messages(category_filter='show2').__str__()
 
 
if __name__ == "__main__":
    app.run(port=5000, debug=True)

某一时刻,浏览器访问http://127.0.0.1:5000/gen,浏览器界面显示:

access at 1404022326.39

不过,由上面的代码可以知道,此时生成了两个flash信息,但分类(category)不同。

使用浏览器访问http://127.0.0.1:5000/show1,得到如下内容:

['1 access at 1404022326.39']

而继续访问http://127.0.0.1:5000/show2,得到的内容为空:

[]

5 在模板文件中获取flash的内容

在Flask中,get_flashed_messages()默认已经集成到Jinja2模板引擎中,易用性很强。下面是来自官方的一个示例:

6 本节源码

https://github.com/letiantian/Learn-Flask/tree/master/flask-demo-013

gin+gorm+router 快速搭建 crud restful API 接口

gin mysql_gin+gorm+router 快速搭建 crud restful API 接口

下载扩展

go get github.com/go-sql-driver/mysql 
go get github.com/jinzhu/gorm
go get github.com/gin-gonic/gin

建表语句

CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
`password` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

结构

├──api
│ ├── apis 
│ │ └── user.go 
│ ├── database 
│ │ └── mysql.go 
│ ├── models 
│ │ └── user.go 
│ └── router 
│ └── router.go
└──main.go

apis/apis/user.go

package apis

import (
    "github.com/gin-gonic/gin"
    model "api/models"
    "net/http"
    "strconv"
)

//列表数据
func Users(c *gin.Context) {
    var user model.User
    user.Username = c.Request.FormValue("username")
    user.Password = c.Request.FormValue("password")
    result, err := user.Users()

    if err != nil {
        c.JSON(http.StatusOK, gin.H{
            "code":    -1,
            "message": "抱歉未找到相关信息",
        })
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "code": 1,
        "data":   result,
    })
}

//添加数据
func Store(c *gin.Context) {
    var user model.User
    user.Username = c.Request.FormValue("username")
    user.Password = c.Request.FormValue("password")
    id, err := user.Insert()

    if err != nil {
        c.JSON(http.StatusOK, gin.H{
            "code":    -1,
            "message": "添加失败",
        })
        return
    }
    c.JSON(http.StatusOK, gin.H{
        "code":  1,
        "message": "添加成功",
        "data":    id,
    })
}

//修改数据
func Update(c *gin.Context) {
    var user model.User
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    user.Password = c.Request.FormValue("password")
    result, err := user.Update(id)
    if err != nil || result.ID == 0 {
        c.JSON(http.StatusOK, gin.H{
            "code":    -1,
            "message": "修改失败",
        })
        return
    }
    c.JSON(http.StatusOK, gin.H{
        "code":  1,
        "message": "修改成功",
    })
}

//删除数据
func Destroy(c *gin.Context) {
    var user model.User
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    result, err := user.Destroy(id)
    if err != nil || result.ID == 0 {
        c.JSON(http.StatusOK, gin.H{
            "code":    -1,
            "message": "删除失败",
        })
        return
    }
    c.JSON(http.StatusOK, gin.H{
        "code":  1,
        "message": "删除成功",
    })
}

database/mysql.go

package database

import (
    _ "github.com/go-sql-driver/mysql" //加载mysql
    "github.com/jinzhu/gorm"
    "fmt"
)

var Eloquent *gorm.DB

func init() {
    var err error
    Eloquent, err = gorm.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local&timeout=10ms")

    if err != nil {
        fmt.Printf("mysql connect error %v", err)
    }

    if Eloquent.Error != nil {
        fmt.Printf("database error %v", Eloquent.Error)
    }
}

models/user.go

    package models

    import (
        orm "api/database"
    )

    type User struct {
        ID       int64  `json:"id"`       // 列名为 `id`
        Username string `json:"username"` // 列名为 `username`
        Password string `json:"password"` // 列名为 `password`
    }

    var Users []User

    //添加
    func (user User) Insert() (id int64, err error) {

        //添加数据
        result := orm.Eloquent.Create(&user)
        id =user.ID
        if result.Error != nil {
            err = result.Error
            return
        }
        return
    }

    //列表
    func (user *User) Users() (users []User, err error) {
        if err = orm.Eloquent.Find(&users).Error; err != nil {
            return
        }
        return
    }

    //修改
    func (user *User) Update(id int64) (updateUser User, err error) {

        if err = orm.Eloquent.Select([]string{"id", "username"}).First(&updateUser, id).Error; err != nil {
            return
        }

        //参数1:是要修改的数据
        //参数2:是修改的数据
        if err = orm.Eloquent.Model(&updateUser).Updates(&user).Error; err != nil {
            return
        }
        return
    }

    //删除数据
    func (user *User) Destroy(id int64) (Result User, err error) {

        if err = orm.Eloquent.Select([]string{"id"}).First(&user, id).Error; err != nil {
            return
        }

        if err = orm.Eloquent.Delete(&user).Error; err != nil {
            return
        }
        Result = *user
        return
    }

router/router.go

package router

import (
    "github.com/gin-gonic/gin"
    . "api/apis"
)

func InitRouter() *gin.Engine {
    router := gin.Default()

    router.GET("/users", Users)

    router.POST("/user", Store)

    router.PUT("/user/:id", Update)

    router.DELETE("/user/:id", Destroy)

    return router
}

main.go

    package main

    import (
        _ "api/database"
        "api/router"
        orm "api/database"
    )

    func main() {
        defer orm.Eloquent.Close()
        router := router.InitRouter()
        router.Run(":8000")
    }

执行 go run main.go

访问地址

    • POST localhost:8006/user 添加
    • GET localhost:8006/users 列表
    • DELETE localhost:8006/user/id 删除
    • PUT localhost:8006/user/id 修改

将Vue项目打包为Windows应用(.exe)

背景

朋友是做商品零售,每月都需要将销售数据汇总至年度销售表格中,在这个过程中存在很多重复性的工作,无奈中。在一次聊天中,我了解到他的需求,就用 Vue 做了一个页面,可以实现 Excel 转成 JSON 进行操作,最后再将 JSON 转成 Excel ,虽然后来了解到用 Python应该会更高效,待日后来研究!

不过咱好歹有个图形界面,用户体验好!(自我安慰一波~)

接下来问题便来了,朋友完全不懂编程,每次都准备开发环境也挺麻烦,便想着能不能做成可执行文件.exe,直接双击安装,生成快捷方式,直接就能用,人性化点赞!

1.首先从electron官网克隆一个demo

选择一个你想存放项目的盘。(可以不用新建文件夹,看个人)直接运行cmd;

注意这里的最好是npm的依赖包

npm与cnpm的区别

  • 说到npm与cnpm的区别,可能大家都知道,但大家容易忽视的一点,是cnpm装的各种node_module,这种方式下所有的包都是扁平化的安装。
  • 一下子node_modules展开后有非常多的文件。导致了在打包的过程中非常慢。但是如果改用npm来安装node_modules的话,所有的包都是树状结构的,层级变深。
  • 由于这个不同,对一些项目比较大的应用,很容易出现打包过程慢且node内存溢出的问题
  • 所以建议大家在打包前,讲使用cnpm安装的依赖包删除,替换成npm安装的依赖包。
git clone https://github.com/electron/electron-quick-start
cd electron-quick-start
cnpm install //npm,cnpm 都可以,cnpm速度较快.
npm start

项目跑起来以后, 就会出现electron的桌面页面,找到clone下来项目的入口文件main.js 和package.json.接下来修改路径和配置。

//----main.js----
function createWindow () {

// and load the index.html of the app.
mainWindow.loadURL(`file://${__dirname}/../dist/index.html`) //修改这里

2. 接下来,在已创建好的vue-cli项目中

安装electron依赖,运行如下命令:

npm install electron --save-dev
npm install electron-packager --save-dev

现在将clone项目中的main.js拷到刚刚新建的项目中的build文件夹下,并重命名为electron.js  , 并更改config/index.js中生产模式下(build)的assetsPublicPth

 build: {
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',

assetsPublicPath: './', //这里改为./

3. 在新建的项目的package.json文件中增加一条指令

如下:

 "scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"build": "node build/build.js",
"electron_dev": "npm run build && electron build/electron.js" //增加的指令

接着执行:

npm run build //生成dist目录(包含静态资源文件) 
npm run electron_dev //启动electron

现在,生成桌面应用基本成功实现了,还剩下最后一步:打包!

首先,复制build目录下的electron.js到dist目录中,注意很关键的一步是复制过来之后,要调整一下loadURL路径的格式,

像这样:

function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({

})

// and load the index.html of the app.
mainWindow.loadURL(`${__dirname}/index.html`) //--修改的--

接着,复制clone例子的package.json到新建项目的dist目录中。在项目的package.json中(注意不是dist下的package.json)为之前下载好的electron-packager,增加一条启动命令。

 "build": "node build/build.js",
"electron_dev": "npm run build && electron build/electron.js",
"electron_build": "electron-packager ./dist/ --platform=win32 --arch=ia32 --icon=./src/assets/yizhu.ico --overwrite" //--新增的命令--

接着,如果你要替换应用图标的话,就在项目中的scr文件夹下的assets目录下,放入你要设置的exe文件的图标,为.ico格式。

这里指的注意的是,你的ico图标是什么名称,上一条的electron_build里面的路径最后就要改成你图标的名称,像这里的yizhu.ico一样,yizhu.ico就是我自己图标的名称。(这点很重要!)

这里我要强调一点, 有同学到这里运行报错, 很有可能是你的图片路径没改过来. 还有一点就是你把自己的图片强行修改为ico格式了,这点是不允许的. 一定要是原生的ico格式的图标. 且看我最下面截图的ico的图标是怎样的. 这里我附上一个转为ico格式的链接. 操作简单.

最后,运行

npm run build //刷新静态资源文件
npm run electron_build //启动

这个时候已经生成了aps-win32-ia32文件夹,找到里面的helloworld.exe文件即可运行。当然,我这里没有给文件重命名,你们可以自行命名。

到这里,exe文件已经最终完成。

 

go 之 gorm

1、简介

ORM

Object-Relationl Mapping, 它的作用是映射数据库和对象之间的关系,方便我们在实现数据库操作的时候不用去写复杂的 sql 语句,把对数据库的操作上升到对于对象的操作。

特性

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

2.简单例子

gorm

gorm 就是基于 Go 语言实现的 ORM 库。

类似于 Java 生态里大家听到过的 Mybatis、Hibernate、SpringData 等。

Github

https://github.com/jinzhu/gorm

官方文档

https://gorm.io/

 

安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

快速入门

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // 迁移 schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // 根据整型主键查找
  db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

  // Update - 将 product 的 price 更新为 200
  db.Model(&product).Update("Price", 200)
  // Update - 更新多个字段
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - 删除 product
  db.Delete(&product, 1)
}

BuildAdmin开源后台介绍

使用流行技术栈快速创建商业级后台管理系统-BuildAdmin是基于TP6、Vue3.x、Typescript、Vite、Pinia、Element plus等开源后台系统的CRUD功能介绍。系统自带WEB终端、同时提供Web和Server端、内置全局数据回收站和字段级数据修改保护、自动注册路由、无限子级权限管理等,无需授权即可免费商用,希望能帮助大家实现快速开发。设计数据表,界面立成。一行命令即可直接生成数据表的增删改查代码,大气实用的表格、多达22种表单组件支持、拖拽排序、受权限控制的编辑和删除、支持关联表等等,可为您节省大量开发时间。BuildAdmin内置了一个WEB终端以实现一些理想中的功能,比如:虽然是基于vue3的系统,但你在安装本系统时,并不需要手动执行npm install和npm build命令。且后续本终端将为您提供更多方便、快捷的服务。

🚀 CRUD代码生成

设计数据表,界面立成。一行命令即可直接生成数据表的增删改查代码,大气实用的表格、多达22种表单组件支持、拖拽排序、受权限控制的编辑和删除、支持关联表等等,可为您节省大量开发时间。[ 视频介绍 | 使用文档 ]

💥 内置WEB终端

我们内置了WEB终端以实现一些理想中的功能,比如:虽然是基于vue3的系统,但你在安装本系统时,并不需要手动执行npm installnpm build命令。且后续本终端将为您提供更多方便、快捷的服务。[ 视频介绍 | 使用文档 ]

🔀 前后端分离

web文件夹内包含:干净(不含后端代码)、完整(所有前端代码文件均在此内) 的前端代码文件,对前端开发者友好,且我们正在开发package.json自动维护以及积分制的模板与案例市场功能,作为纯前端开发者,您可以将BAdmin当做学习与资源的社群,本系统可为您准备好案例和模板等所需要的环境,而您只需专注于学习或工作,不需要会任何后端代码! [ 邀您:和我们一起 ]

👍 流行且稳定的技术栈

除了基于TP6前后端分离架构外,我们的Vue3使用了Setup、状态管理使用了Pinia、并使用了TypeScript、Vite、Element plus等可以为你的知识面添砖加瓦的技术栈。[ 相关技术学习文档 ]

✨ 高颜值

提供三种布局模式,其中默认布局使用无边框设计风格,它并没有强行填满屏幕的每一个缝然后使用边框线进行分隔,所有的功能版块,都像是悬浮在屏幕上的,同时又将屏幕空间及其合理的利用了 [ 查看在线演示 ]

🔐 权限验证

可视化的权限管理,然后根据权限动态的注册路由、菜单、页面、按钮(权限节点)、支持无限父子级权限分组、前后端搭配鉴权,自由分派页面和按钮权限 [ 使用文档 ]

Python如何安装cv2模块

通常由俩种方式,一种使用pip,无需选择opencv版本;一种手动选择opencv版本,使用whl文件下载

一. pip自动下载

pip install opencv-python

大多数的情况下,是可以的安装成功CV2,可是有时,这个指令安装的pip会出现CV2版本与python安装的版本,不匹配导致,你安装的opencv不成功,由于国外的网路问题,可以加入 -i 指定国内源

清华:https://pypi.tuna.tsinghua.edu.cn/simple

阿里云:https://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

华中理工大学:http://pypi.hustunique.com/

山东理工大学:http://pypi.sdutlinux.org/

二. 下载whl文件,手动选择opencv版本

1.先更新pip

python -m pip install --upgrade pip

2.从清华源中选择对应的opencv版本,中下载其他版本的库,因为根据安装的python 版本下载相应的镜像文件
比如 python 3.5.4 64bit
我下载的是

opencv_python-3.1.0.0-cp35-cp35m-win_amd64.whl

3.下载完成后,在whl文件对应的目录下,cmd 到whl对应文件目录下,执行

pip install opencv_python-3.1.0.0-cp35-cp35m-win_amd64.whl

结语

针对python cv2安装遇到的安装失败问题,提出多种方法,进行实验,本文的方法参考网页的方法,进行汇总。

You-get安装方法及使用教程

you-get爬虫,依赖于Python3.10,可以爬取网页无法下载的视频文件,具体步骤如下:
1,下载Python3.10无脑下一步安装
python语言生态非常繁荣,有大量使用python开发的工具。you-get 只是其中一个。关注我后续会介绍更多好用的 python 工具。
2,新建一个空白文件夹,清空地址栏输入cmd后回车打开“命令指示符”
3,输入以下字符下载you-get模块

pip install you-get

显示 Successfully installed you-get 字样即表示 you-get 安装成功。

这里解释一下,上述命令行 -i 后面的值代表使用清华大学的软件源,下载安装速度会比较快。
4,打开浏览器,复制视频所在地址链接
5,输入“you-get http://视频链接”

you-get https://www.bilibili.com/video/BV1st4y1D771?spm_id_from=333.851.b_62696c695f7265706f72745f64616e6365.7

记住删除地址后面.recommand后缀

另外如果是批量下载一个视频列表,可以在上述命令后加上 ” –playlist”, 例如:

you-get https://www.bilibili.com/video/BV1st4y1D771?spm_id_from=333.851.b_62696c695f7265706f72745f64616e6365.7 --playlist

 

6,下载的问题,you-get 帮你解决了,you-get下载的视频一般挺大,需要一个外置存储设备。我用的是闪迪 500 GB的移动固态硬盘,感觉比移动机械硬盘小巧很多,速度快,而且是 type-c,插我的 Macbook Pro 简直完美,不用转接线。

Protocol Buffers 短暂介绍

protobuf简介

protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

protocol buffer初步使用

下面是一个简单的使用的例子:
首先需要定义一个.proto文件,其中需要定义你希望操作的对象的结构。

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
 
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
 
  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
 
  repeated PhoneNumber phone = 4;
}

保存为person.proto文件,之后下载protoc编译工具,并解压,使用PB将proto文件生成java类。

protoc.exe --java_out=. person.proto

在指定的java_out目录下就可以生成java对应的类,这里生成了PersonOuterClass.java。将文件放入项目,并引入PB的jar包。

compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.1.0'

接下来就可以使用PB进行对象的操作了。

public static void main(String[] args) throws Exception{
        //创建对象
        PersonOuterClass.Person p= PersonOuterClass.Person.newBuilder().setName("my_name").setId(2).build();
        System.out.print(p.toString());
        //序列化对象
        FileOutputStream fos=new FileOutputStream("D://person");
        p.writeTo(fos);
        fos.close();
        //反序列化对象
        FileInputStream fis=new FileInputStream("D://person");
        PersonOuterClass.Person pread=PersonOuterClass.Person.parseFrom(fis);
        System.out.println(pread.toString());
        fis.close();
 }

与java原生的serializable接口进行比较

public class JavaPerson implements Serializable{
       public String name;
       public Integer id;
       public String email;
        
       public enum PhoneType{
           MOBILE,HOME,WORK
       }
       public class PhoneNumber{
           public String number;
           public PhoneType type;
       }
       List phone;
        
       public static void main(String[] args) throws Exception{
           JavaPerson jp=new JavaPerson();
           jp.name="my_name";
           jp.id=2;
 
           FileOutputStream fileOut = new FileOutputStream("d://person.ser");
           ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
           outStream.writeObject(jp);
           outStream.close();
           fileOut.close();
 
 
           FileInputStream fileIn =new FileInputStream("d://person.ser");
           ObjectInputStream in = new ObjectInputStream(fileIn);
           jp = (JavaPerson) in.readObject();
           in.close();
           fileIn.close();
       }
   }

运行比较结果:

PB较java默认形式代码更简洁。
循环运行10000次序列化和反序列化后PB比java快约30%。
生成的文件PB有11字节而java有215字节。
PB提供额外的字段校验支持。

SwiftAdmin 极速开发框架

开发环境:Windows服务器版 VScode Apache MySQL5.7 PHP7 – PHP8

基于ThinkPHP Layui 完美契合,在开发上采用最精简最高效的做法去完成业务系统的需求,是一款优秀的中后台极速开发解决方案。

开发初衷


  1. SAPHP框架的开发,主要是为了减少在自己开发过程中的频繁造轮子,并且SAPHP框架主张简单就是高效的原则,所以最简单的东西才是效率最高的,可能你的应用场景很复杂,但是SAPHP足够应付!
  2. 在最开始接触互联网的时候,都是用一些开源的CMS系统制作自己的网站,后期因为扩展和二次开发的问题,导致觉得很多东西并不是那么简单易用,比如后台的很多JS代码封装的不是很好,而且界面可操作性很差,所以自己开发这款框架封装了很多常用的特性,足以满足日常后台的开发需要,在使用的过程中你会发现,SAPHP框架里面用的最多的是属性而不是对象,一是为了在书写HTML标签的时候方便。二是为了和layui本身区分开!这样让你更容易在这个上面进行扩展!
  3. 系统默认从基础控制器继承了增删改查操作。但这种方式并不适合大多数硬性的应用场景和逻辑需求,你可能在后期需要摈弃大多数利用了一键CURD的方法进行重载函数,虽然SAPHP框架里面也有,但框架的设计初衷是为了在易用性和操作性上折中找一个方案来做,当前基于第一个版本的SAPHP框架在这方面的表现还不是特别好。但随着应用场景检验和优化,本框架会逐步的进行完善和提高性能!
  4. 在市面上目前的开源极速开发框架的学习成本略高,想搞一个学习成本极低,但性能不低的框架(CMS系统)!
  5. 想着开发一款底层设计配置和应用分开的系统,这样对于很多小白用户不会在项目已经上线运行中的时候,误操作系统的配置导致数据丢失,错乱的问题。比如有些字段需要手动在数据库进行修改。

侧重点


  • SAPHP的架构和开发更倾向于内容管理系统[CMS]的方向,当然你也可以当中API系统使用
  • 系统默认的缓存机制为redis缓存,所以请确保安装redis扩展和服务器[摒弃操蛋的file缓存吧]
  • 如果你只是需要一个极简的API管理系统,那么建议你删除不需要的模块和菜单项!
  • 会侧重于SEO优化、客户管理、流量管理、蜘蛛池、区块链以及采集方面的应用!!!
  • 坚持偏向于社区版开源的方向,主要由社区共同的爱好者免费开发维护插件!!!
  • SAPHP已经上线插件市场🔧,适用于中小型企业采购付费商业版插件的使用!!
  • 本框架特别适合个人开发者和小型创业公司,找一款真正适合自己的框架不容易,所以先来试试SAPHP吧!

框架优势


  • 代码量最少最精简、逻辑简单清晰
  • 参考官方文档,只需会PHP JS 开箱即用
  • 界面基于ant design设计 [可操作性强]
  • 控制器与栏目管理双鉴权,满足日常大部分需求
  • 前端JavaScript鉴权,后端AUTH类鉴权,减少请求
  • 封装常用组件和快捷属性,小白即可快速二次开发
  • 支持全文索引XS/ElasticSearch轻松支持PB级数据
  • 通用型thinkPHP插件开发架构,可轻松迁移其他插件
  • 代码安全质量高,修复大部分低危、高危代码漏洞
  • 高占比AJAX数据调用,响应速度可媲美前后端分离

集成功能


  • [√] API模块 支持token鉴权,支持细分规则
  • [√] CMS模块 系统内容CMS模块,搭配模板,开箱即用
  • [√] 用户管理 用户是系统操作者,该功能主要完成系统用户配置。
  • [√] 公司管理 设置公司常用信息,前端标签调用
  • [√] 部门管理 配置系统组织机构(部门、小组),树结构展现支持数据权限。
  • [√] 岗位管理 配置系统用户所属担任职务。
  • [√] 菜单管理 配置系统菜单,操作权限,按钮、栏目等权限标识等。
  • [√] 角色管理 角色菜单权限分配、设置角色按机构进行数据范围权限划分。
  • [√] 插件管理 可开发定制属于自己的插件,可安装升级社区插件!!!
  • [√] 导航管理 支持导航定制,小分类导航配置适合SEO
  • [√] 内容管理 系统默认模型数据已完成后端数据录入,可快速二次开发!!!!
  • [√] 广告管理 运营必选功能,获取广告代码自动校验过期时间
  • [√] 数据字典 对系统中经常使用的一些较为固定的数据进行维护,并使用自定义标签交互
  • [√] 操作日志 用户后台操作日志,全局异常、SQL注入等记录
  • [√] TAG过滤 支持违规词、敏感词配置
  • [√] 短信平台 支持阿里云、腾讯云短信发送
  • [√] 附件上传 支持FTP、阿里云、腾讯云OSS附件上传
  • [√] 全文检索 支持XunSearch、ElasticSearch集群PB级全文检索
  • [ ] 服务监控 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
  • [ ] 定时任务 在线(添加、修改、删除)任务调度包含执行结果日志。
  • [ ] 代码生成 前后端代码的生成(php、html、layui、sql)支持一键CRUD 。