Flask框架之任务管理器代码

目录结构:
在这里插入图片描述
forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, \
    ValidationError, SelectField, DateTimeField
from wtforms.validators import DataRequired, Email, Length, EqualTo

# 注册表单
from app.models import User, Category


class RegisterForm(FlaskForm):
    email = StringField(
        label="邮箱",
        validators=[
            DataRequired(),
            Email(),

        ]
    )
    username = StringField(
        label="用户名",
        validators=[
            DataRequired(),

        ],
    )
    password = PasswordField(
        label='密码',
        validators=[
            DataRequired(),
            Length(6, 12, "密码必须是6-12位")
        ]
    )

    repassword = PasswordField(
        label='确认密码',
        validators=[
            EqualTo("password", "密码与确认密码不一致")
        ]
    )

    submit = SubmitField(
        label="注册"
    )

    # *****************************************************
    # 默认情况下validate_username会验证用户名是否正确, 验证的规则, 写在函数里面
    def validate_username(self, field):
        # filed.data ==== username表单提交的内容
        u = User.query.filter_by(username=field.data).first()
        if u:
            raise ValidationError("用户名%s已经注册" % (u.username))

    def validate_email(self, filed):
        u = User.query.filter_by(email=filed.data).first()
        if u:
            raise ValidationError("邮箱%s已经注册" % (u.email))


# 登录表单
class LoginForm(FlaskForm):
    username = StringField(
        label="用户名",
        validators=[
            DataRequired(),

        ],
    )
    password = PasswordField(
        label='密码',
        validators=[
            DataRequired(),
            # Length(6, 12, "密码必须是6-12位")
        ]
    )
    submit = SubmitField(
        label="登录"
    )

# 关于任务的基类
class TodoForm(FlaskForm):
    content = StringField(
        label="任务内容",
        validators=[
            DataRequired()
        ]
    )
    # 任务类型
    category = SelectField(
        label="任务类型",
        coerce=int,
        choices=[(item.id, item.name) for item in Category.query.all()]
    )
class AddTodoForm(TodoForm):
    finish_time = DateTimeField(
        label="任务终止日期"
    )
    submit = SubmitField(
        label="添加任务",
    )

class EditTodoForm(TodoForm):
    submit = SubmitField(
        label="编辑任务",
    )

models.py

from app import db
from werkzeug.security import  generate_password_hash, check_password_hash
from datetime import  datetime



# 用户和任务的关系: 一对多, 用户是一, 任务是多,
# 用户和分类的关系:
class User(db.Model):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    username = db.Column(db.String(20), unique=True)
    password_hash = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(30), unique=True)
    add_time = db.Column(db.DateTime, default=datetime.utcnow()) # 账户创建时间
    # 1).  User添加属性todos; 2). Todo添加属性user;
    todos = db.relationship('Todo', backref="user")
    categories = db.relationship('Category', backref='user')

    @property
    def password(self):
        """u.password"""
        raise  AttributeError("密码属性不可以读取")

    @password.setter
    def password(self, password):
        """u.password = xxxxx """
        self.password_hash = generate_password_hash(password)


    def verify_password(self, password):
        """验证密码是否正确"""
        return  check_password_hash(self.password_hash, password)

    def __repr__(self):
        return  "<User %s>" %(self.username)

# 任务和分类的关系: 一对多
# 分类是一, 任务是多, 外键写在多的一端
class Todo(db.Model):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    content = db.Column(db.String(100)) # 任务内容
    status = db.Column(db.Boolean, default=False) # 任务的状态
    # datetime.now()你当前所在时区的时间;
    # 使用协调时间时(Coordinated Universal Time,UTC)协调世界各地的时差问题;
    #   美国时间: 2019-3-15 00:00   北京时间: 2019-03-16 12:00  ***
    add_time = db.Column(db.DateTime, default=datetime.utcnow())  # 任务创建时间
    # 任务的类型,关联另外一个表的id
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    # 任务所属用户;
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return  "<Todo %s>" %(self.content[:6])


class Category(db.Model):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(20), unique=True)
    add_time = db.Column(db.DateTime, default=datetime.utcnow())  # 任务创建时间
    # 1). Category添加一个属性todos, 2). Todo添加属性category;
    todos = db.relationship('Todo', backref='category')

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return  "<Category %s>" %(self.name)






views.py

import json
from functools import wraps

from app import app, db

# 网站首页
from app.forms import RegisterForm, LoginForm, AddTodoForm, EditTodoForm
from flask import render_template, flash, redirect, url_for, session, request

from app.models import User, Todo


def is_login(f):
    """用来判断用户是否登录成功"""

    @wraps(f)
    def wrapper(*args, **kwargs):
        # 判断session对象中是否有seesion['user'],
        # 如果包含信息, 则登录成功, 可以访问主页;
        # 如果不包含信息, 则未登录成功, 跳转到登录界面;;
        if session.get('user', None):
            return f(*args, **kwargs)
        else:
            flash("用户必须登录才能访问%s" % (f.__name__))
            return redirect(url_for('login'))

    return wrapper


@app.route('/')
def index():
    return redirect(url_for('list'))


# 注册页面
@app.route('/register/', methods=['POST', 'GET'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        # 1. 从前端获取用户输入的值;
        email = form.email.data
        username = form.username.data
        password = form.password.data

        # 2. 判断用户是否已经存在? 如果返回位None,说明可以注册;
        u = User.query.filter_by(username=username).first()
        if u:
            flash("用户%s已经存在" % (u.username))
            return redirect(url_for('register'))
        else:
            u = User(username=username, email=email)
            u.password = password
            db.session.add(u)
            db.session.commit()
            flash("注册用户%s成功" % (u.username))
            return redirect(url_for('login'))
    return render_template('register.html',
                           form=form)


# 登录页面
@app.route('/login/', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data

        # 1. 判断用户是否存在?
        u = User.query.filter_by(username=username).first()
        if u and u.verify_password(password):
            session['user_id'] = u.id
            session['user'] = u.username
            flash("登录成功!")
            return redirect(url_for('index'))
        else:
            flash("用户名或者密码错误!")
            return redirect(url_for('login'))

    return render_template('login.html',
                           form=form)


@app.route('/logout/')
@is_login
def logout():
    session.pop('user_id', None)
    session.pop('user', None)

    return redirect(url_for('login'))


# 添加任务
@app.route('/todo/add/', methods=['GET', 'POST'])
@is_login
def todo_add():
    form = AddTodoForm()
    if form.validate_on_submit():
        # 获取用户提交的内容
        content = form.content.data
        category_id = form.category.data

        # 添加到数据库中
        # 用户登录才可以添加任务,
        todo = Todo(content=content,
                    category_id=category_id,
                    user_id=session.get('user_id'))
        db.session.add(todo)
        db.session.commit()
        flash("任务添加成功")
        return redirect(url_for('todo_add'))
    return render_template('todo/add_todo.html',
                           form=form)


# 编辑任务
@app.route('/todo/edit/<int:id>/', methods=['GET', 'POST'])
@is_login
def todo_edit(id):
    form = EditTodoForm()
    # *****重要: 编辑时需要获取原先任务的信息, 并显示到表单里面;
    todo = Todo.query.filter_by(id=id).first()
    form.content.data = todo.content
    form.category.data = todo.category_id
    if form.validate_on_submit():
        # 更新时获取表单数据一定要使用request.form方法获取, 而form.content.data并不能获取用户更新后提交的表单内容;
        # print(request.form)
        # content = form.content.data   # error
        # category_id = form.category.data   # error
        content = request.form.get('content')
        category_id = request.form.get('category')

        # 更新到数据库里面
        todo.content = content
        todo.category_id = category_id
        db.session.add(todo)
        db.session.commit()
        flash("更新任务成功")
        return redirect(url_for('list'))
    return render_template('todo/edit_todo.html',
                           form=form)


# 删除任务: 根据任务id删除
@app.route('/todo/delete/<int:id>/')
@is_login
def todo_delete(id):
    todo = Todo.query.filter_by(id=id).first()
    db.session.delete(todo)
    db.session.commit()
    flash("删除任务成功")
    return redirect(url_for('list'))


# 查看任务
@app.route('/todo/list/')
@app.route('/todo/list/<int:page>/')
@is_login
def list(page=1):
    # 任务显示需要分页
    # Todo.query.paginate(page, per_page=5)
    todoPageObj = Todo.query.filter_by(user_id=session.get('user_id')).paginate(page, per_page=app.config['PER_PAGE'])  # 在config.py文件中有设置;
    return render_template('todo/list_todo.html',
                           todoPageObj=todoPageObj,
                           )


# 修改任务状态为完成
@app.route('/todo/done/<int:id>/')
@is_login
def done(id):
    todo = Todo.query.filter_by(id=id).first()
    todo.status = True
    db.session.add(todo)
    db.session.commit()
    flash("修改状态成功")
    return redirect(url_for('list'))


# 修改任务状态为未完成
@app.route('/todo/undo/<int:id>')
@is_login
def undo(id):
    todo = Todo.query.filter_by(id=id).first()
    todo.status = False
    db.session.add(todo)
    db.session.commit()
    flash("修改状态成功")
    return redirect(url_for('list'))


@app.route('/showTodo/')
# @is_login
def showTodo():
    done_count = Todo.query.filter_by(status=True).count()
    undone_count = Todo.query.filter_by(status=False).count()
    return render_template('todo/show_todo.html',
                           info={
                               '已完成': done_count,
                               '未完成': undone_count

                           })


@app.route('/newShowTodo/')
def newShowTodo():
    return render_template('todo/new_show_todo.html')


@app.route('/get_data/')
@is_login
def get_data():
    # done_count = Todo.query.filter_by(status=True).count()
    # undone_count = Todo.query.filter_by(status=False).count()
    info = {
        'info': ["已完成", "未完成"],
        'count': [20, 60]
    }
    # 解决中文编码问题
    return json.dumps(info, ensure_ascii=False)


@app.route('/get_cpu/')
def get_cpu():
    import psutil
    cpuInfo = json.dumps({'CPU占有率', psutil.cpu_percent()},
                         ensure_ascii=False)

    return cpuInfo

html代码就不写了
templates/todo/show_todo.html 画饼状图代码
这个代码的数据式固定的,是直接从官网拷下来的,下面的那个代码数据是动态生成的

{% extends 'base.html' %}

{% block title %}

    图表分析
{% endblock %}


{% block scripts %}
    {{ super() }}
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));

        // 指定图表的配置项和数据
        var option = {
            title: {
                text: '任务完成度分析',

            },
            tooltip: {
                trigger: 'item',
                formatter: "{a} <br/>{b}: {c} ({d}%)"
            },
            legend: {
                orient: 'vertical',
                x: 'right',
                data: ['已完成', '未完成',]
            },
            series: [
                {
                    name: '访问来源',
                    type: 'pie',
                    radius: ['50%', '70%'],
                    avoidLabelOverlap: false,
                    label: {
                        normal: {
                            show: false,
                            position: 'center'
                        },
                        emphasis: {
                            show: true,
                            textStyle: {
                                fontSize: '20',
                                fontWeight: 'bold'
                            }
                        }
                    },
                    labelLine: {
                        normal: {
                            show: false
                        }
                    },
                    data: [
                        {value: 20, name: '已完成'},
                        {value: 80, name: '未完成'},

                    ]
                }
            ]

        };

        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>

{% endblock %}
{% block content %}
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="main" style="width: 500px;height:500px;
    border: 1px solid gray"></div>

{% endblock %}

/templates/todo/new_show_todo.html 画饼状图代码

这个代码数据是动态生成的

{% extends 'base.html' %}

{% block title %}

    图表分析
{% endblock %}


{% block scripts %}
    {{ super() }}
    <script type="text/javascript">


        $(function () {
            // 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById('main'));
            $.ajax({
                url: '/get_data/',
                success: function (data) {
                    json_data = JSON.parse(data)

                    // 指定图表的配置项和数据
                    var option = {
                        title: {
                            text: '任务统计分析'
                        },
                        tooltip: {
                            trigger: 'item',
                            formatter: "{a} <br/>{b}: {c} ({d}%)"
                        },
                        legend: {
                            orient: 'vertical',
                            x: 'right',
                            data: json_data['info']
                        },
                        series: [
                            {
                                name: '访问来源',
                                type: 'pie',
                                radius: ['50%', '70%'],
                                avoidLabelOverlap: false,
                                label: {
                                    normal: {
                                        show: false,
                                        position: 'center'
                                    },
                                    emphasis: {
                                        show: true,
                                        textStyle: {
                                            fontSize: '20',
                                            fontWeight: 'bold'
                                        }
                                    }
                                },
                                labelLine: {
                                    normal: {
                                        show: false
                                    }
                                },
                                data: json_data['count']
                            }
                        ]

                    };
                    // 使用刚指定的配置项和数据显示图表。
                    myChart.setOption(option);

                }
            })
        

        })






    </script>

{% endblock %}
{% block content %}

    <div class="container">
        <div class="col-lg-8 col-lg-offset-2">

            <div class="page-header">
                任务完成度分析
            </div>
            <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
            <div id="main" style="width: 500px;height:500px;
    border: 1px solid gray"></div>

        </div>
        <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
{#        <div id="cpu" style="width: 500px;height:500px;#}
{#    border: 1px solid gray"></div>#}

    </div>

    </div>

{% endblock %}

猜你喜欢

转载自blog.csdn.net/qq_41386300/article/details/88723791