前言

用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. ProxyChains 的坑, 需要关闭 sip

    之前 10.12.6 还可以的,现在升级下系统就用不了了. search下发生时sip问题. 解决方案就时要关闭这个东西; 关闭SIP reboot # 按住 option 键 # 到系统选择页面后, ...

  2. 基于Linux下catalog方式的 Oracle 备份策略(RMAN)

    --********************************** -- 基于Linux下 Oracle 备份策略(RMAN) --******************************* ...

  3. Arguments Optional 计算两个参数之和的 function

    创建一个计算两个参数之和的 function.如果只有一个参数,则返回一个 function,该 function 请求一个参数然后返回求和的结果. 例如,add(2, 3) 应该返回 5,而 add ...

  4. SpringBoot:Invalid character found in method name. HTTP method names must be tokens

    问题背景 关于SpringBoot应用挂了很久之后,会发生Invalid character found in method name. HTTP method names must be token ...

  5. 记录BigInteger犯过的一个错误

    2019年4月18号,面试遇到的面试题,当时做错了,纪念下. public class StrTest { public static void main(String[] args) { BigIn ...

  6. 【MySQL 读书笔记】SQL 刷脏页可能造成数据库抖动

    开始今天读书笔记之前我觉得需要回顾一下当我们在更新一条数据的时候做了什么. 因为 WAL 技术的存在,所以当我们执行一条更新语句的时候是先写日志,后写磁盘的.当我们在内存中写入了 redolog 之后 ...

  7. React Native Flexbox & CSS3 Flexbox

    React Native Flexbox & CSS3 Flexbox https://facebook.github.io/react-native/docs/flexbox/ https: ...

  8. EntityFramework Core笔记:保存数据(4)

    1. 基本保存 每个DBContext实例都有一个ChangeTracker,负责跟踪需要写入数据库的更改.当实例发生更改时,更改会被记录在ChangeTracker中,在调用 SaveChanges ...

  9. 关于解决Tomcat服务器Connection reset by peer 导致的宕机

    org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer at ...

  10. 简单 php 代码跟踪调试实现

    简单 php 代码跟踪调试实现 debug_backtrace:生成回溯 debug_print_backtrace:打印回溯 1. debug_backtrace ($options = DEBUG ...