简约的Flask代码统计系统

前言

  最近在看flask的课程,根据视频的要求做了一个flask的小项目:代码统计系统。简单的完善了一下。
  不得不说虽然只是一个小项目,但是也是有完整的目录结构的,所以就不讲解代码了。
  编译环境及工具,库:

  • pycharm,anaconda,mysql,redis
  • sqlalchemy,wtforms,redis,redis-py,flask,pipreqs,flask-migrate,flask-script,flask-sqlalchemy,flask-session

  mysql驱动的话用这个mysql-connector-python,不用pymysql

# 先 pip install mysql-connector-python
class BaseConfig(object):
    # ###flask-sqlalchemy的配置###
    SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://root:password@127.0.0.1:3306/你的数据库名?charset=utf8"

1.实现效果

  先上目录结构:
flask目录结构

1. 首页

first

2. 登录

login

3. 注册

register

4. 用户界面

柱状图有序显示用户的总代码提交量index

5. 代码详情

折线图根据日期有序显示代码提交趋势detail

6. 上传页面

上传代码压缩包给服务器,得到里面的代码写的总行数
upload

2.一些讲解

  用的mysql做数据保存,用sqlalchemy库来做orm,不用原生sql,并且使用flask-sqlalchemy库更贴合flask来进行开发。使用wtfoems库来做登陆和注册表单,更简洁高效。使用pipreqs库来合成项目的requirements,方便的显示所使用的第三方库。使用flask-script和flask-migrate,因flask-migrate依赖于flask-script,其参数migrate生成数据库更改命令,upgrate进行数据库更改,能更简洁高效的对数据库进行更改。
  flask因其特性,不可避免的会使用大量的第三方库,合理的使用资源才可提高效率。
  以下是项目的打包,echarts的js包。(免费)

  1. 代码提交系统
  2. echarts的js包

3.遇见的问题和解决(要点)

1. echarts的使用和下载 码云

  因为echarts的js包是在github上的,下载很慢还会中断,所以我帮你们下载下来了。我用的是码云的办法,在网上可以搜到。使用的话有说明文档,解决基本功能还是可以的。

2. 文件解压问题 byteio

   这是一个很重要的问题,因为之前的方法没用了。

# 保存解压后的文件(原压缩文件不保存)
# obj.stream是flask上传后取到的文件流,有open()功能
target_path = "保存路径"
shutil._unpack_zipfile(obj.stream, target_path)

     如果你直接把stream放进去的话,会报错:

AttributeError: 'SpooledTemporaryFile' object has no attribute 'seekable'

     可能flask返回的并非真正的文件对象,所以没有’seekable’参数,改为如下就可以了:

from io import BytesIO
temp = BytesIO(obj.stream.read())
target_path = "保存路径"
shutil._unpack_zipfile(temp, target_path)
3. wtforms自定义validate

  为了检查用户名是否别人已经注册过了,而且wtforms没有对比的方法,就自己自定义,如下:

class RegisterForm(Form):
    name = simple.StringField(
        label='用户名:',
        validators=[
            validators.DataRequired(message='用户名不可为空!'),
            validators.Length(min=2, max=12, message='用户名长度必须大于%(min)d且小于%(max)d'),
        ],
        render_kw={'placeholder': '请输入用户名:', 'id': 'name'}
    )
        @staticmethod
    def validate_name(self, filed):  # validate是必须的前缀,后面的name是上面对应组件的对象名
        name = db.session.query(models.Users.name).filter(models.Users.name == filed.data).first()
        db.session.remove()
        if name:
            raise ValidationError("用户名:%s 已存在!" % name)
4. html的table的滑动条

  html的table很难看,而且如果限制显示区域就有上下和左右的滑动条,添加如下属性则隐藏:

table{
    width: 60%;
    margin: 10px auto;
    border-collapse: collapse;/*border-collapse:collapse合并内外边距(去除表格单元格默认的2个像素内外边距*/
    background-color: rgba(0,0,0,0);
    table-layout: fixed;
}
/*table可滚动start*/
tbody{
    display: block;
    height: 250px;
    overflow-y: auto;
    overflow-x: hidden !important;
    -webkit-overflow-scrolling: touch; /*为了滚动顺畅*/
}
table tbody::-webkit-scrollbar {
    display: none; /*隐藏滚动条*/
}
thead, tbody tr{
    display: table;
    width: 100%;
    table-layout: fixed;
}
table thead{
    width: 100%;
}
/*table可滚动end*/

     虽然是隐藏了,但是当数据多的时候也没有下拉滚动条,只能滚动鼠标轮,所以最好的是当数据多的超出了table范围的时候自动显示下拉滚动,未超出时就隐藏。

5. flask的handler的错误处理

  flak处理错误的时候不能在蓝图里面,必须在全局app上,如下:

def create_app():
    app = Flask(__name__)
    app.config.from_object('settings.ProConfig')
    app.debug = True

    @app.errorhandler(404)
    def page_not_found(error):
        return render_template("ac_404.html", error=error), 404

    app.register_blueprint(account)
    app.register_blueprint(administration)
    # Session(app)
    db.init_app(app)
    return app

     还有就是处理 413错误(上传文件过大) 的时候,没有返回,上传文件过大,无法处理,只能提醒,且会断开链接。

6. pipreqs好用

  python的pipreqs库挺好用的,会自动生成所引用的第三方库,不过我觉得还是有一些缺陷所在,但是基本问题不大!

7. mysql的连表子查询

  比如查询一个人在另一张表里的所有代码总数和。

subqry = db.session.query(db.func.sum(models.CodeMes.code_line)).filter(models.Users.id == models.CodeMes.user_id).correlate(
   models.Users).as_scalar()  # 生成mysql语句不查询
index_result = db.session.query(models.Users, subqry).order_by(subqry.desc()).all()
db.session.remove()
8. 闪现的使用或者g,jinjia模板不能在{{}}里调用字符串的[0]

   原本我以为flask的闪现很鸡肋,后来用到了发现还是有一点用的,场景:比如我注册成功了,跳转到登陆界面,那么界面下应该“显示注册成功,请登录”吧,但是flask的跳转函数

return redirect("login")

是没有可以传参的。但是从注册成功到跳转登陆界面只是一次请求,所以flask的g对象可以完成这个,因为g就是只存在一次请求中就消失的。不过用闪现可能简单点,就先用它了。

flash("注册成功,请登录!", 'success')

success = ''
if get_flashed_messages(category_filter=['success']):
     success = get_flashed_messages(category_filter=['success'])[0]
# 因为jinjia模板不能在html的{{}}里调用字符串的[0],而get_flashed_messages()函数返回的是列表,所以就在后端的处理里直接取[0]了。
9. html的textarea的运用

  因为在注册或登陆时如果你的信息不符合要求,应该要有返回错误的,那么如何接受以及放在合适的位置就很重要了。而textarea就勉强符合要求:

<textarea id="error1" cols="30" rows="3" readonly disabled>{% for field in form %}{{field.errors[0]}}{% endfor %}{{user_pwd_error}}
</textarea>
#error1{
	position: absolute;
	top: 230px;
	right: 30px;
	resize: none;
	color: red;
	background-color: rgba(0,0,0,0);
	border: none;
}

error
     设为只读,控制长宽和位置

10. Flask的自定义扩展 (2020-02-17更新)

  很多时候我们会遇见如下情况:Flask的全局处理路由放到创建app的函数里,这样既不美观,也不方便,有其他办法吗?放到函数外面然后在内部调用?

from flask import Flask
# from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()  # 必须在蓝图之前,不然蓝图不能调用

from exts.auth import Auth
from .views.account import account
from .views.administration import administration
from .models import *


def create_app():
    app = Flask(__name__)
    app.config.from_object('settings.ProConfig')
    app.debug = True
    Auth(app, [administration])  # 因为对于蓝图的处理,所以这个地方必须放到蓝图注册之前。因为蓝图在注册的时候会对所有的蓝图函数进行添加处理。
    app.register_blueprint(account)
    app.register_blueprint(administration)

    @app.errorhandler(404)
    def xxx():
        print('xxx')
        return 'xxx'

    # Session(app)
    db.init_app(app)
    return app

    这时就要涉及一个知识点了,就是python的装饰器是如何实现的!

# 方法一
@app.errorhandler(404)
def xxx():
    print('xxx')
    return 'xxx'
# 方法二
def page_not_found(self, error):
    return render_template("ac_404.html", error=error), 404
app.errorhandler(404)(self.page_not_found)

    基于如此我们就可以再开一个目录exts来存放全局处理的路由和函数了。

from flask import render_template, session, redirect


class Auth(object):
    def __init__(self, app=None, bluep_list=None):
        self.app = app
        if bluep_list is None:
            bluep_list = []
        self.bluep_list = bluep_list
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        app.auth_manager = self
        for bluep in self.bluep_list:
            bluep.before_request(self.check_login)
        app.errorhandler(404)(self.page_not_found)
        # 这个函数可以处理一些值在全部的视图里直接使用,而不需要render_template()来导入值。
        app.context_processor(self.context_processor)

    def check_login(self):
        if not session.get('user_info'):
            return redirect("/login")

    def context_processor(self):
        user = session.get('user_info')  # 返回的变量可视图中全局使用
        return dict(current_user=user)

    def page_not_found(self, error):
        return render_template("ac_404.html", error=error), 404

    def login_session(self, login_result):
        session['user_info'] = {'id': login_result[0], 'nikename': login_result[1]}

    def logout_session(self):
        if 'user_info' in session:
            session.pop('user_info')

    如上的做法还处理了一个问题,当你的蓝图很多的时候,你还需要在很多的蓝图里使用before_request装饰器来验证用户是否登录,这是否也不符合程序的简洁性。所以我们使用外部扩展来处理,在Auth类里传入蓝图对象列表,然后for循环来添加before_request函数。

    这里还有一个小细节:因为对于蓝图的处理,所以外部的扩展处理 Auth(app, [administration]) 必须放到蓝图注册之前。因为蓝图在注册的时候会对所有的蓝图函数进行添加处理。

    为什么又要把登录,注销的处理也放到这里面嘞,因为可以对session里的值进行统一处理,不会分散多处。


  最后因能力有限,暂时就先这样,后面如果js和mysql学的好的话,还可以更丰富,特别时js感觉在前端里是真的强大!

后期可优化:

  • 限制每日提交一次
  • 使用正则来约束用户名和密码的格式
  • ajax传数据


感谢大家支持!人生苦短,我用python!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值