前言

用Flask框架,SQLalchemy,SQlite Vertabelo 搭建一个日程表。

这个并不是最终产品,目的是展示python web开发的流程,欢迎不吝赐教!

github下载源码

项目介绍

使用Flask开发一个增删改查(CRUD)应用程序:

  • add/update 更新、添加一个类别
  • delete/mark 标记或删除
  • manage categories. 管理类别

三个视图:

  1. 创建类别

  1. 创建待办事项

用户可以通过填写带有描述的表单,从现有优先级中选择优先级并从现有类别中选择类别来创建新待办事项。

  1. 管理视图

主视图列出了所有待办事项和可用类别,通过单击屏幕左侧类别菜单中的类别,将列出所选类别的待办事项列表。

技术栈

  • 数据库:SQLite
  • 数据库ORM:SQLAlchemy
  • 路由框架:Flask
  • CSS框架:Bootstrap

Flask Web开发流程

  • SQLAlchemy:获取数据库ORM对象
  • Flask:Web服务器网关接口(WSGI)
  • Bootstrap:组合HTML,CSS和JavaScript

注:ORM (Object-relational Mapping) ,关系模型映射到一个对象。pyrhon中最流行是SQLAlchemy;

一、搭建环境

1.1: 创建虚拟环境

win系统

virtualenv venv
venv\Scripts\activate

1.2: 安装依赖包

  • Flask:web骨架
  • SQLAlchemy:数据库ORM框架
pip install Flask
pip install Flask-SQLAlchemy

1.3: 创建依赖包列表文件

requirements.txt

$ pip freeze > requirements.txt.

1.4: 测试hello word

run.py

from flask import Flask

app = Flask(__name__)

from app.views import *
if __name__ == '__main__':
app.run()

views.py

from run import app

@app.route('/')
def index():
return '<h1>Hello World!</h1>'
python .\run.py

二、应用程序开发

2.1:应用程序结构

|-- app/
| |-- __init__.py
| |-- data/
| | `-- todoapp.db
| |-- models.py # SQLAlchemy 数据库模型
| |-- static/ #静态文件夹包含所有的CSS和JavaScript文件
| | |-- css/
| | |-- fonts/
| | `-- js/
| |-- templates/ #该模板文件夹中包含Jinja2的模板
| | |-- layout.html
| | |-- list.html
| | |-- macros.html
| | |-- new-category.html
| | `-- new-task.html
| `-- views.py #视图函数
|-- config.py #配置函数
|-- manage.py #Flask-Script支持命令行选项
|-- readme.md
`-- run.py

2.2:数据库设计

业务逻辑:

  • 将代办事项分类
  • 待办事项按条记录在都将在todos表中
  • 待办事项表将具有所需的id,description,creation_date等字段
  • 代办事项有优先级,按照优先级排序

在线建模工具:vertabelo

根据需求

app\models.py

from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app import app app = Flask(__name__)
db = SQLAlchemy(app) class Todo (db.Model):
__tablename__ = "todo"
id = db.Column('id', db.Integer, primary_key=True)
category_id = db.Column('category_id', db.Integer, db.ForeignKey('category.id'))
priority_id = db.Column('priority_id', db.Integer, db.ForeignKey('priority.id'))
description = db.Column('description', db.Unicode)
creation_date = db.Column('creation_date', db.Date,default=datetime.utcnow)
is_done = db.Column('is_done', db.Boolean, default=False) priority = db.relationship('Priority', foreign_keys=priority_id)
category = db.relationship('Category', foreign_keys=category_id) class Priority (db.Model):
__tablename__ = "priority"
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column('name', db.Unicode)
value = db.Column('value', db.Integer) class Category (db.Model):
__tablename__ = "category"
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column('name', db.Unicode)

2.2:配置和初始文件

Flask-SQLAlchemy数据库URI格式:

Database engine URL
MySQL mysql://username:password@hostname/database
Postgres postgresql://username:password@hostname/database
SQLite (Unix) sqlite:////absolute/path/to/database
SQLite (Windows) sqlite:///c:/absolute/path/to/database

config.py

class Config(object):
SECRET_KEY = 'you-will-never-guess'
SQLALCHEMY_DATABASE_URI = 'sqlite:///data/todoapp.db '
SQLALCHEMY_TRACK_MODIFICATIONS = False

app/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app) from app import views, models

2.3: 创建数据库和表

  • 安装ipython,创建初始数据库更加友好
pip install ipython
  • 创建数据库和表。
from models import db
from app.models import db
db.create_all()
  • 插入数据分三步

    1. 创建一个Python对象
    2. 将其添加到会话中
    3. 提交会话
  • 提交一些类别

from app.models import Category
work = Category(name=u'work')
home = Category(name=u'home')
db.session.add(work)
db.session.add(home)
db.session.commit()
  • 创建优先级表
from models import Priority
high = Priority(name=u'high', value=3)
medium = Priority(name=u'medium', value=2)
low = Priority(name=u'low', value=1)
db.session.add(high)
db.session.add(medium)
db.session.add(low)
db.session.commit()
  • 更多更改
db.session.add(object)
  • 检查数据库

2.4:视图函数

视图函数是python脚本,接受一个http请求并返回一个http响应。此响应可以是HTML页面或重定向。

2.4.1 @app.route('/')

  • @app.route('/') 响应的路径
  • route将函数注册为URL
  • 函数的返回值叫做响应,是浏览器接收的值

views.py

from flask import render_template, request
from models import Category, Todo, Priority, db
# 列出我们按优先级值和所有类别降序排列的所有TODO项目。
@app.route('/')
def list_all():
return render_template(
'list.html',
categories=Category.query.all(),
todos=Todo.query.join(Priority).order_by(Priority.value.desc())

2.4.2 render_template

Flask和Jinja2之间的集成是通过该render_template功能实现。

Jinja2 负责生成HTML,Flask提供模板的名称和参数作为Keyword,keyword是键值对

上面的函数中,categoriestodos是在分配了当前值的模板中写入的占位符。这些当前值是从数据库中检索的准确值。

2.4.3 创建代办事项

从数据库读取数据,在浏览器展现代办事项。

@app.route('/new-task', methods=['POST']),函数响应一个POST请求

服务器从浏览器接收到POST后,将数据插入到数据库,操作成功重新定向页面;如果出现问题,将再次呈现新待办事项。

@app.route('/new-task', methods=['POST'])
def new():
if request.method == 'POST':
category = Category.query.filter_by(id=request.form['category']).first()
priority = Priority.query.filter_by(id=request.form['priority']).first()
todo = Todo(category, priority, request.form['description'])
db.session.add(todo)
db.session.commit()
return redirect('/')
else:
return render_template(
'new-task.html',
page='new-task',
categories=Category.query.all(),
priorities=Priority.query.all()
)

2.4.4 更新代办事项

@app.route('/<int:todo_id>', methods=['GET', 'POST'])

GET:服务器收到浏览器GET请求,渲染模板,并检索数据库追加给新的页面

POST:如果浏览器输入值,服务器将更新数据库

浏览器

@app.route('/<int:todo_id>', methods=['GET', 'POST'])
def update_todo(todo_id):
todo = Todo.query.get(todo_id)
if request.method == 'GET':
return render_template(
'new-task.html',
todo=todo,
categories=Category.query.all(),
priorities=Priority.query.all()
)
else:
category = Category.query.filter_by(id=request.form['category']).first()
priority = Priority.query.filter_by(id=request.form['priority']).first()
description = request.form['description']
todo.category = category
todo.priority = priority
todo.description = description
db.session.commit()
return redirect('/')

2.4.5 视图完整代码

app/views.py


from flask import render_template, request, redirect, flash,url_for
from app.models import Category, Todo, Priority, db
from app import app @app.route('/')
def list_all():
return render_template(
'list.html',
categories=Category.query.all(),
todos=Todo.query.all(),#join(Priority).order_by(Priority.value.desc())
) @app.route('/<name>')
def list_todos(name):
category = Category.query.filter_by(name=name).first()
return render_template(
'list.html',
todos=Todo.query.filter_by(category=category).all(),# .join(Priority).order_by(Priority.value.desc()),
categories=Category.query.all(), ) @app.route('/new-task', methods=['GET', 'POST'])
def new():
if request.method == 'POST':
category = Category.query.filter_by(id=request.form['category']).first()
#priority = Priority.query.filter_by(id=request.form['priority']).first()
#todo = Todo(category=category, priority=priority, description=request.form['description'])
todo = Todo(category=category, description=request.form['description'])
db.session.add(todo)
db.session.commit()
return redirect(url_for('list_all'))
else:
return render_template(
'new-task.html',
page='new-task',
categories=Category.query.all(),
#priorities=Priority.query.all()
) @app.route('/<int:todo_id>', methods=['GET', 'POST'])
def update_todo(todo_id):
todo = Todo.query.get(todo_id)
if request.method == 'GET':
return render_template(
'new-task.html',
todo=todo,
categories=Category.query.all(),
#priorities=Priority.query.all()
)
else:
category = Category.query.filter_by(id=request.form['category']).first()
#priority = Priority.query.filter_by(id=request.form['priority']).first()
description = request.form['description']
todo.category = category
#todo.priority = priority
todo.description = description
db.session.commit()
return redirect('/') @app.route('/new-category', methods=['GET', 'POST'])
def new_category():
if request.method == 'POST':
category = Category(name=request.form['category'])
db.session.add(category)
db.session.commit()
return redirect('/')
else:
return render_template(
'new-category.html',
page='new-category.html') @app.route('/edit_category/<int:category_id>', methods=['GET', 'POST'])
def edit_category(category_id):
category = Category.query.get(category_id)
if request.method == 'GET':
return render_template(
'new-category.html',
category=category
)
else:
category_name = request.form['category']
category.name = category_name
db.session.commit()
return redirect('/') @app.route('/delete-category/<int:category_id>', methods=['POST'])
def delete_category(category_id):
if request.method == 'POST':
category = Category.query.get(category_id)
if not category.todos:
db.session.delete(category)
db.session.commit()
else:
flash('You have TODOs in that category. Remove them first.')
return redirect('/') @app.route('/delete-todo/<int:todo_id>', methods=['POST'])
def delete_todo(todo_id):
if request.method == 'POST':
todo = Todo.query.get(todo_id)
db.session.delete(todo)
db.session.commit()
return redirect('/') @app.route('/mark-done/<int:todo_id>', methods=['POST'])
def mark_done(todo_id):
if request.method == 'POST':
todo = Todo.query.get(todo_id)
todo.is_done = True
db.session.commit()
return redirect('/')

2.5:模板

2.5.1 模板简介

模板能使前后端分开,是包含响应的HTML文件;文件包含的变量仅在请求的上下文有效;通过Jinja2模板引擎渲染

Jinja2模板引擎官网

2种分隔符:

  • {{ variable }} 用于变量
  • {% control structures %} 用于控制结构

2.5.2 解析一个视图函数:

在主页上显示类别和代办事项,

为了获得所有类别和TODO,用for语句{% ... %}和特定类别的块内循环迭代,显示其名称。

@app.route('/')
def list_all():
return render_template(
'list.html',
categories=Category.query.all(),
todos=Todo.query.join(Priority).order_by(Priority.value.desc())

相应的模板如下所示。

<h3>Category</h3><font></font>
<table><font></font>
{%- for category in categories %}<font></font>
<tr><font></font>
<td>{{ category.name }}</td><font></font>
</tr><font></font>
{%- endfor %}<font></font>
</table><font></font>

2.5.3 模板继承

在主页顶部有一个导航栏,其中包含指向create_category页面和create_todo页面以及页脚的链接。我希望在每个页面上重复这些组件。为了在所有页面上保留公共元素,我将使用模板继承。

继承是块控制语句。在此结构中,您可以定义派生模板可以插入其内容的位置。

layout.html负责一般结构。extends块建立三个模板之间的继承(它用于从另一个模板扩展模板)。

2.6:Bootstrap美化Web应用

Bootstrap是最流行的Web开发前端框架。

2.6.1 下载插件

下载Bootstrap,解压到/static

Bootstrap教程

Navbar图标元素

2.6.2 Layout.html基本模板

layout.html是其余模板的基本布局。用navbar元素构建一个漂亮的导航栏,container-fluid使页面全屏

Menu
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">TODOapp</a>
</div>
<ul class="nav navbar-nav">
<li><a href="/new-task">New TODO</li>
<li><a href="/new-category">New category</li>
</ul>
</div>
</nav> <div class="container-fluid">
Messages
</div> <div class="container-fluid">
Footer
</div>

2.6.3 List.html

List.html负责列出TODO和类别。此模板继承自layout.html。在这里,我添加了Bootstrap网格系统。

默认情况下,网格系统最多可扩展到12列,可以更改为四个设备(大屏台式机,台式机,平板电脑和手机) 。

由于每行有12个单元,我制作了两列,一个是3个单元,第二个是9个单元。

网格系统要求将行放在.container.container-fluid中以进行正确的对齐和填充。

Bootstrap网格系统的更多信息

2.6.4 基本结构

要创建布局:

  • 创建一个容器 <div class="container">
  • 创建一行 <div class="row">
  • 在一行中添加所需的列数。

结构如下:

<div class="container">
<div class="row">
<div class="col-md-2">Table with categories</div>
<div class="col-md-10">Table with TODOs</div>
</div>
</div>

2.6.5 new-task.html

使用自定义的.selectpicker 对类别表单和优先级表单进行简单的选择

  • 下载Bootstrap-select,解压到/static
  • layout.html启用 Bootstrap-Select via JavaScript
    $('.selectpicker').selectpicker();

三 、验证

python .\run.py

Flask框架搭建一个日程表的更多相关文章

  1. 19.3.19 使用Flask框架搭建一个简易登录服务器

    import Flask import json from Flask import request server1 = flask.Flask(__name__) #实例化一个flask对象 @se ...

  2. 基于Flask框架搭建视频网站的学习日志(一)

    ------------恢复内容开始------------ 基于Flask框架搭建视频网站的学习日志(一)2020/02/01 一.Flask环境搭建 创建虚拟环境 初次搭建虚拟环境 搭建完虚拟环境 ...

  3. 基于Flask框架搭建视频网站的学习日志(二)

    基于Flask框架搭建视频网站的学习日志(二)2020/02/02 一.初始化 所有的Flask程序都必须创建一个程序实例,程序实例是Flask类的对象 from flask import Flask ...

  4. 基于Flask框架搭建视频网站的学习日志(三)之原始web表单

    基于Flask框架搭建视频网站的学习日志(三)1.原始Web 表单 本节主要用于体验一下前端后端直接数据的交互,样例不是太完善,下一节会加入Flash处理,稍微完善一下页面 (备注:建议先阅读廖雪峰老 ...

  5. linux下Flask框架搭建简单网页

    开始安装FLASK需要创建一个虚拟环境,虚拟环境可以不干扰正在使用的系统环境,避免影响,并且也不需要完全的root权限,更加安全可靠. 搭建环境 Python3.4 进入到microblog目录下创建 ...

  6. Flask框架搭建REST-API服务

    一.目的 为了能够将测试工具部署成RESTful-API服务,这样就能通过接口的方式提供统一测试工具服务,使用人员就不用构建application而产生的各种环境问题.使用问题. 适合人群:Pytho ...

  7. flask框架搭建项目的具体步骤

    flask是一个使用python编写的轻量级Web应用框架.与django不同,Django创建工程时,会直接构架好工程目录.而flask工程几乎是自己创建结构. 1.导入相关模块以及需要使用的模块: ...

  8. 基于Flask框架搭建视频网站的学习日志(六)之数据库

    使用Flask-SQLSlchemy管理数据库(1)--初步安装调试 一.介绍: Flask-SQLSlchemy是一个Flask扩展,简化了Flask中对sql的操作,是一个高层的框架,可以避免直接 ...

  9. Node.js基于Express框架搭建一个简单的注册登录Web功能

    这个小应用使用到了node.js  bootstrap  express  以及数据库的操作 :使用mongoose对象模型来操作 mongodb 如果没了解过的可以先去基本了解一下相关概念~ 首先注 ...

随机推荐

  1. Java jar包启动脚本

    #!/bin/bash APP_HOME=/wdcloud/app/rps/rps-module-admin APP_JAR=rps-module-admin-*.jar APP_PIDS=$(ps ...

  2. 01-Django介绍和安装

    01-Django介绍和安装 1.Django介绍 1.1介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M(Model),视图V(View)和控 ...

  3. 菜鸟学python之大数据的初认识

    这次作业的要求来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2639 1.这些分析所采用数据来源是什么? 国家数据库:中国铁路 ...

  4. Docker 核心技术之仓库

    Docker 仓库简介 什么是Docker仓库 Docker仓库就是存放docker镜像并有docker pull方法下载的云环境 Docker仓库分为公有仓库和私有仓库. 公有仓库指Docker H ...

  5. mysql 在线加索引 锁表

    mysql在线修改表结构大数据表的风险与解决办法归纳 - 王滔 - 博客园 http://www.cnblogs.com/wangtao_20/p/3504395.html MySQL 加索引 加字段 ...

  6. js获取元素宽高、位置相关知识汇总

    常见clientWidth.clientHeight.offsetWidth.offsetLeft,clientX.scrollTop等词语,比较混乱,现在总结下他们的区别. 1. clientWid ...

  7. 小项目一---Python日志分析

    日志分析 概述 分析的前提 半结构化数据 文本分析  提取数据(信息提取)  一.空格分隔 with open('xxx.log')as f: for line in f: for field in ...

  8. 定时任务调度工作(学习记录 二)timer定时函数的用法

    schedule的四种用法: 1.schedule(task,time) 参数: task----所安排的任务 time----执行任务的时间 作用: 在时间等于或超过time的时候执行且仅执行一次t ...

  9. Spring Boot(二):数据库操作

    本文主要讲解如何通过spring boot来访问数据库,本文会演示三种方式来访问数据库,第一种是JdbcTemplate,第二种是JPA,第三种是Mybatis.之前已经提到过,本系列会以一个博客系统 ...

  10. linux软连接文件的copy

    最近在做项目的时候遇到过一个问题:当copy一个工程模块时发现里面的目录文件有重复定义的情况. 最后查看源文件目录发现是存在软连接造成的. 出现这种情况的原因是:当直接copy文件目录时遇到软连接会把 ...