Python ToDoList 项目文档

环境安装

Python 安装

下载地址:https://www.python.org/downloads/,下载版本为 3.6+ 即可。示例地址:https://www.python.org/downloads/release/python-386/。可在列表中根据自己使用机器进行选择性下载。

Pycharm 安装

下载地址:https://www.jetbrains.com/pycharm/download/#section=windows

Pycharm 激活

如果下载的是 Professional 版本的 Pycharm ,那么将 Pycharm 安装完成之后需要进行激活操作,激活方式可以分为账号激活,激活码激活,激活URL等方式,这里采用激活码的方式进行。文档地址:https://github.com/wenyanjun/free。为了方便,已将激活码复制到地址 https://paste.ubuntu.com/p/XSfKGFFFt3/ ,只需复制激活码到 Pycharm 中激活即可。

ToDoList

在线体验地址 http://www.todolist.cn/

需求分析

添加 ToDo

在添加 ToDo 输入框中,输入待办的 ToDo,那么该 ToDo 就会添加到正在进行的列表中

完成 ToDo

勾选 ”正在进行“ 中的待办 ToDo,那么就修改该 ToDo 的状态为已完成

修改 ToDo

可将 ”正在进行“ 中的待办 ToDo 的状态修改为 “已经完成”

可以修改 “正在进行” 中的待办的 ToDo 的内容。例如将 “今晚去游泳” 修改为 “今晚去聚餐”

已经完成的 ToDo 不能进行修改,只能被删除

删除 ToDo

可对正在进行或者已经完成的 ToDo 进行删除

删除所有 ToDo

支持一键删除所有的 ToDo,包含 “正在进行” 和 “已经完成” 两种状态的

获取所有 ToDo

需要提供查询接口,查询所有的 ToDo,或者单独查询 “正在进行” 的 ToDo,或者单独查询 “已经完成” 的 ToDo

模块拆分

在本项目中因为只涉及到 ToDo 的操作,所以可以将 ToDo 看作一个模块,只需开发该模块的增删改查接口即可。模块拆分的原则可参考系统架构设计模块拆分维度和原则模块拆分案例 - 5

问题:如果增加用户登录,注册,注销。评论等功能,那么该怎么拆分模块呢?

系统设计

项目结构设计

│─tolist
│  app.py  # 应用文件
│  database.py  # 数据库连接
│  models.py  # 数据库实体
│  test.db  # 数据库文件

数据库设计

create table todo
(
    id      INTEGER     not null
        primary key,
    name    VARCHAR(50) not null,
    status  SMALLINT    not null,
    deleted BOOLEAN     not null,
    check (deleted IN (0, 1))
);

ToDo 实体类设计

每个 ToDo 实体都有状态(正在进行、已经完成),名称,为了能唯一标识一个 ToDo,还可以为每个 ToDo 分配一个 ID,即实体类设计如下:

class ToDo(Base):
    __tablename__ = 'todo'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), default="", nullable=False)
    status = Column(SMALLINT, default=0, nullable=False)

需求开发

Flask 入门

打开 Pycharm,选择 new project 新建一个 Flask 项目

项目搭建

上述创建一个 Flask 项目后,即可生成一个最简单的 Flask 应用,其中 app.py 内容如下:

from flask import Flask

app = Flask(__name__)


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


if __name__ == '__main__':
    app.run()

新建 database.py 文件,内容如下:

"""
https://dormousehole.readthedocs.io/en/latest/patterns/sqlalchemy.html
"""

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:///test.db', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

新建 models.py 文件,内容如下:

from sqlalchemy import Column, Integer, String, SMALLINT
from database import Base, engine


class ToDo(Base):
    __tablename__ = 'todo'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), default="", nullable=False)
    status = Column(SMALLINT, default=0, nullable=False)

    def __init__(self, name=None, status=None):
        self.name = name
        self.status = status

    def __repr__(self):
        return '<ToDo %r>' % (self.name)


def init_db():
    Base.metadata.create_all(bind=engine)


if __name__ == '__main__':
    init_db()

接口开发

添加 ToDo

@app.route('/todo', methods=["POST"])
def create_todo():
    # 接收参数
    params = request.json
    # 判断参数是否存在
    if params:
        name = params.get("name")
        todo = ToDo(name=name, status=0)
        db_session.add(todo)
        db_session.commit()
        return {"status": 1, "message": "success"}
    else:
        return {"status": 1, "message": "params error"}, 401

完成 ToDo/修改 ToDo

@app.route('/todo/<string:todo_id>', methods=["PUT"])
def update_todo(todo_id):
    data = {}
    # 接收参数
    params = request.json
    # 判断参数是否存在
    if params:
        if params.get("status") is not None:
            data.update({"status": bool(params.get("status"))})
        if params.get("name") is not None:
            data.update({"name": params.get("name")})
        try:
            rows = db_session.query(ToDo).filter(ToDo.id == todo_id, ToDo.deleted != True).update(data)
            if not rows:
                return {"status": 0, "message": f"update error"}, 401
            db_session.commit()
        except Exception as e:
            return {"status": 0, "message": f"update error: {e}"}, 401
        return {"status": 1, "message": "success"}
    else:
        return {"status": 1, "message": "params error"}, 401

删除 ToDo/删除所有 ToDo

@app.route('/todo/', methods=["DELETE"])
@app.route('/todo/<string:todo_id>', methods=["DELETE"])
def delete_todo(todo_id=None):
    try:
        if todo_id:
            rows = db_session.query(ToDo).filter(ToDo.id == todo_id).update({"deleted": True})
        else:
            rows = db_session.query(ToDo).update({"deleted": True})
        if not rows:
            return {"status": 0, "message": "delete error"}, 401
        db_session.commit()
    except Exception as e:
        return {"status": 0, "message": f"delete error: {e}"}, 401
    return {"status": 1, "message": "success"}

获取所有 ToDo

@app.route('/todo/<string:todo_id>', methods=["PUT"])
def update_todo(todo_id):
    data = {}
    # 接收参数
    params = request.json
    # 判断参数是否存在
    if params:
        if params.get("status") is not None:
            data.update({"status": bool(params.get("status"))})
        if params.get("name") is not None:
            data.update({"name": params.get("name")})
        try:
            rows = db_session.query(ToDo).filter(ToDo.id == todo_id, ToDo.deleted != True).update(data)
            if not rows:
                return {"status": 0, "message": f"update error"}, 401
            db_session.commit()
        except Exception as e:
            return {"status": 0, "message": f"update error: {e}"}, 401
        return {"status": 1, "message": "success"}
    else:
        return {"status": 1, "message": "params error"}, 401

功能测试

使用 postman 进行功能性测试,下载地址 https://www.postman.com/, chrome 插件地址: https://chrome.google.com/webstore/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo?utm_source=chrome-ntp-icon

源码信息

可在此处下载上述源码:https://raw.githubusercontent.com/rexyan/warehouse/master/tolist.zip

参考资料来源

ToDoList: http://www.todolist.cn/

Python官网:https://www.python.org

模块拆分案例 - 5:https://zhuanlan.zhihu.com/p/172528871

系统架构设计模块拆分维度和原则:https://blog.csdn.net/varyall/article/details/81461002

###