Day04
1.分页器方法
分页器
BaseQuery.paginate()
page
per_page
False
Pagination
items
pages
获取总页数
prev_num
上一页的页码
has_prev
是否有上一页
next_num
下一个页码
has_next
是否有下一页
iter_pages
李晶:
__init__.py
from flask import Flask
from App import settings
from App.ext import init_ext
def create_app(envname):
app = Flask(__name__)
app.config.from_object(settings.ENV_NAME.get(envname))
init_ext(app)
return app
ext.py
# flask-script
# flask-blueprint
# flask-session
# flask-sqlalchemy
# flask-migrate
from flask_bootstrap import Bootstrap
from flask_debugtoolbar import DebugToolbarExtension
from flask_migrate import Migrate
from flask_session import Session
from App.models import db
def init_ext(app):
app.config['SECRET_KEY']='110'
app.config['SESSION_TYPE']='redis'
Session(app=app)
db.init_app(app=app)
migrate = Migrate()
migrate.init_app(app=app,db=db)
Bootstrap(app=app)
app.debug = True
# debugtoolbar
debugtoolbar = DebugToolbarExtension()
debugtoolbar.init_app(app=app)
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Emp(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(32))
age = db.Column(db.Integer,default=18)
gender = db.Column(db.String(2))
salary = db.Column(db.Float)
class Parent(db.Model):
id=db.Column(db.Integer,primary_key=True,autoincrement=True)
name=db.Column(db.String(30),unique=True)
# Child对应的表 backref 子表外键描述需要
children=db.relationship("Child",backref="parent",lazy=True)
class Child(db.Model):
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
name = db.Column(db.String(30), unique=True)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))
class User(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(32))
age = db.Column(db.Integer,default=18)
class Movie(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(32))
class Collection(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
u_id = db.Column(db.Integer,db.ForeignKey(User.id))
m_id = db.Column(db.Integer,db.ForeignKey(Movie.id))
setting.py
def get_database_uri(DATABASE):
dialect = DATABASE.get('dialect')
driver = DATABASE.get('driver')
username = DATABASE.get('username')
password = DATABASE.get('password')
host = DATABASE.get('host')
port = DATABASE.get('port')
database = DATABASE.get('database')
return '{}+{}://{}:{}@{}:{}/{}'.format(dialect,driver,username,password,host,port,database)
class Config():
Test = False
Debug = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopConfig(Config):
Debug = True
DATABASE={
'dialect':'mysql',
'driver':'pymysql',
'username':'root',
'password':'1234',
'host':'localhost',
'port':'3306',
'database':'flaskday04',
}
SQLALCHEMY_DATABASE_URI=get_database_uri(DATABASE)
ENV_NAME={
'develop':DevelopConfig
}
views.py
from flask import Blueprint, render_template, request, redirect, url_for
from App.models import Emp, db, Parent, Child, Collection
blue = Blueprint('blue',__name__)
@blue.route('/')
def hello_world():
return 'Hello World!'
@blue.route('/list/')
def list():
emps = Emp.query.all()
return render_template('userList.html',emps=emps)
@blue.route('/toadd/')
def toadd():
return render_template('add.html')
@blue.route('/add/',methods=['get','post'])
def add():
name = request.form.get('name')
age = request.form.get('age')
gender = request.form.get('gender')
salary = request.form.get('salary')
print(name,age,gender,salary)
emp = Emp()
emp.name = name
emp.age = age
emp.gender = gender
emp.salary = salary
db.session.add(emp)
db.session.commit()
return redirect(url_for('blue.list'))
@blue.route('/delete/')
def delete():
id = request.args.get('id')
emp = Emp.query.get(id)
db.session.delete(emp)
db.session.commit()
return redirect(url_for('blue.list'))
@blue.route('/toupdate/')
def toupdate():
id = request.args.get('id')
emp = Emp.query.get(id)
return render_template('update.html',emp=emp)
@blue.route('/update/',methods=['get','post'])
def update():
id = request.args.get('id')
emp = Emp.query.get(id)
name = request.form.get('name')
age = request.form.get('age')
gender = request.form.get('gender')
salary = request.form.get('salary')
emp.name=name
emp.age = age
emp.gender = gender
emp.salary = salary
db.session.add(emp)
db.session.commit()
return redirect(url_for('blue.list'))
# 添加主键对象 会不会直接将外键添加
@blue.route('/saveparent/')
def saveparent():
p = Parent()
p.name = '张三'
c = Child()
c.name = '张四'
c1 = Child()
c1.name = '王五'
p.children=[c,c1]
db.session.add(p)
db.session.commit()
return 'add success'
# 从查主
# select * from parent where id = (select pid from Child where id = 1)
# 主查从
# select * from Child where pid = (select id from Parent where id = 1)
# 查询
@blue.route('/getparent/')
def getparent():
p = Parent.query.filter(Child.parent_id == 1).first()
# print(p[0].name)
print(p.name)
return 'ok'
@blue.route('/getchild/')
def getchild():
clist = Child.query.filter(Parent.id == 1)
for c in clist:
print(c.name)
return 'ok'
# 多对多的添加
# 将用户id和电影id 获取 然后去数据库中查找 是否存在 如果存在那就告诉已经添加到购物车
# 否则 添加到购物车
@blue.route('/addcollection/')
def addcolletion():
u_id = int(request.args.get('u_id'))
m_id = int(request.args.get('m_id'))
print(u_id,m_id)
c = Collection.query.filter(Collection.u_id == u_id).filter(Collection.m_id==m_id)
if c.count() > 0:
return '已经添加到购物车'
else:
c1 = Collection()
c1.u_id = u_id
c1.m_id = m_id
db.session.add(c1)
db.session.commit()
return 'ok'
@blue.route('/bootstrapDemo/')
def bootstrapDemo():
# emps = Emp.query.all()
page = int(request.args.get('page',1))
per_page = int(request.args.get('per_page',2))
pagination = Emp.query.paginate(page=page,per_page=per_page,error_out=False)
return render_template('bootstrapDemo.html',pagination=pagination)
manager.py
from flask_migrate import MigrateCommand
from flask_script import Manager
from App import create_app
from App.views import blue
app = create_app('develop')
manager = Manager(app=app)
app.register_blueprint(blueprint=blue)
manager.add_command('db',MigrateCommand)
if __name__ == '__main__':
manager.run()
templates:
add.html
<html>
<head>
<title>regist</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">Main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
注册
</h1>
<form action="{{ url_for('blue.add') }}" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
用户名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="name" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
年龄:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="age" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
性别:
</td>
<td valign="middle" align="left">
男
<input type="radio" class="inputgri" name="gender" value="男"
checked="checked" />
女
<input type="radio" class="inputgri" name="gender" value="女" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
薪水:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="salary" />
</td>
</tr>
</table>
<p>
<input type="submit" class="button" value="添加" />
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
bootstrapDemo.py
{% extends 'bootstrap/base.html' %}
{% block navbar %}
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">万锋集团</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">员工风采 <span class="sr-only">(current)</span></a></li>
<li><a href="#">企业文化</a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="请输入员工姓名">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">注册</a></li>
<li><a href="#">登录</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
<div class="container">
<div class="jumbotron">
<h1>welcome to red langman</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">go</a></p>
</div>
</div>
<div align="center">
<table width="800" height="150" border="1" align="center">
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>gender</th>
<th>salary</th>
<th>operate</th>
</tr>
{% for emp in pagination.items %}
<tr>
<td>{{ emp.id }}</td>
<td>{{ emp.name }}</td>
<td>{{ emp.age }}</td>
<td>{{ emp.gender }}</td>
<td>{{ emp.salary }}</td>
<td>
<a>删除</a>
<a>修改</a>
</td>
</tr>
{% endfor %}
</table>
</div>
<div align="center">
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
{% if pagination.has_prev %}
<a href="{{ url_for('blue.bootstrapDemo') }}?page={{ pagination.prev_num }}" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
{% else %}
<a href="#" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
{% endif %}
</li>
{% for p in pagination.iter_pages() %}
<li><a href="#">{{ p }}</a></li>
{% endfor %}
<li>
{% if pagination.has_next %}
<a href="{{ url_for('blue.bootstrapDemo') }}?page={{ pagination.next_num }}" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
{% else %}
<a href="#" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
{% endif %}
</li>
</ul>
</nav>
</div>
{% endblock %}
login.html
<html>
<head>
<title>login</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">Main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
login
</h1>
<form action="userList.html" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
username:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="username" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
password:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" name="pwd" />
</td>
</tr>
</table>
<p>
<input type="submit" class="button"
value=" 确定 " />
<a href="regist.html">还没有帐户,请点击这儿注册</a>
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
regist.html
<html>
<head>
<title>regist</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">Main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
注册
</h1>
<form action="login.html" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
用户名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="username" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
真实姓名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="name" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
密码:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" name="pwd" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
年龄:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="age" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
性别:
</td>
<td valign="middle" align="left">
男
<input type="radio" class="inputgri" name="sex" value="m"
checked="checked" />
女
<input type="radio" class="inputgri" name="sex" value="f" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
电话:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="phone" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
对方要求:
</td>
<td valign="middle" align="left">
<textarea rows="5" cols="30" name="ask" style="resize:none;"></textarea>
</td>
</tr>
<tr>
<td valign="middle" align="right">
验证码:
<img id="num" src="checkCode" />
<a href="javascript:;"
onclick="document.getElementById('num').src = 'checkCode?'+(new Date()).getTime()">换一张</a>
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="number" />
</td>
</tr>
</table>
<p>
<input type="submit" class="button" value="Submit »" />
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
update.html
<html>
<head>
<title>regist</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">Main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
注册
</h1>
<form action="{{ url_for('blue.update') }}?id={{ emp.id }}" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td>id</td>
<td>
<input type="text" class="inputgri" name="id" value="{{ emp.id }}" readonly="true">
</td>
</tr>
<tr>
<td valign="middle" align="right">
用户名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="name" value="{{ emp.name }}"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
年龄:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="age" value="{{ emp.age }}" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
性别:
</td>
<td valign="middle" align="left">
{% if emp.gender == '男' %}
男
<input type="radio" class="inputgri" name="gender" value="男"
checked="checked" />
女
<input type="radio" class="inputgri" name="gender" value="女" />
{% else %}
男
<input type="radio" class="inputgri" name="gender" value="男"
/>
女
<input type="radio" class="inputgri" name="gender" value="女"
checked="checked" />
{% endif %}
</td>
</tr>
<tr>
<td valign="middle" align="right">
薪水:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="salary" value="{{ emp.salary }}"/>
</td>
</tr>
</table>
<p>
<input type="submit" class="button" value="修改" />
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
userDetail.html
<html>
<head>
<title>update Emp</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">Main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
User Detail:
</h1>
<table class="table">
<tr>
<td>
姓名
</td>
<td>
电话
</td>
</tr>
<tr>
<td>
zhangshan
</td>
<td>
010-12345678
</td>
</tr>
</table>
<h1>
对方要求:
</h1>
<table>
<tr>
<td colspan="2">
<textarea cols="80" style="border:0px;resize:none;"></textarea>
</td>
</tr>
</table>
<br/>
<h1>
Load Photo:
</h1>
<form action="userDetail.html" method="post"
enctype="multipart/form-data">
Upload File Name:
<input type="file" name="file1" />
<input type="submit" value="confirm" />
</form>
<h1>
view photo:
</h1>
<table>
<tr>
<td>
<img src="upload/t1.jpg" width="300"
height="200" />
</td>
</tr>
</table>
</div>
<input type="button" onclick="location='userList.html'" value="查看所有用户"/>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
userlist.html
<html>
<head>
<title>emplist</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<script type="text/javascript">
function toadd() {
{# 企业级开发中不要直接的跳转到某一个页面 必须要通过请求来跳转#}
{# window.location.href = '/toadd/'#}
window.open('/toadd/',target='_self')
}
</script>
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">主页面</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
Welcome!
</h1>
<table class="table">
<tr class="table_header">
<td>
ID
</td>
<td>
姓名
</td>
<td>
性别
</td>
<td>
年龄
</td>
<td>
薪水
</td>
<td>
操作
</td>
</tr>
{% for emp in emps %}
<tr class="row1">
<td>
{{ emp.id }}
</td>
<td>
{{ emp.name }}
</td>
<td>
{{ emp.gender }}
</td>
<td>
{{ emp.age }}
</td>
<td>
{{ emp.salary }}
</td>
<td>
{# /delete/?id=1 #}
<a href="{{ url_for('blue.delete') }}?id={{ emp.id }}">删除</a>
<a href='{{ url_for('blue.toupdate') }}?id={{ emp.id }}'>修改</a>
</td>
</tr>
{% endfor %}
</table>
<p>
<input type="button" class="button" value="添加员工"
onclick="toadd();" />
</p>
</div>
</div>
<div id="footer">
<div id="footer_bg">
ABC@126.com
</div>
</div>
</div>
</body>
</html>
2.flask-bootstrap
插件安装
pip install flask-bootstrap
ext中初始化
Bootstrap(app=app)
bootstrap案例--bootstrap模板 {% extends ‘bootstrap/base.html’%}
3.FLASK-DEBUGTOOLBAR
辅助调试插件
安装
pip install flask-debugtoolbar
初始化 ext
app.debug = True (最新版本需要添加)
debugtoolbar = DebugToolBarExtension()
debugtoolbar.init_app(app=app)
4.缓存flask-cache
1 缓存目的:
缓存优化加载,减少数据库的IO操作
2 实现方案
数据库
文件
内存
内存中的数据库 Redis
3 实现流程
从路由函数进入程序
路由函数到视图函数
视图函数去缓存中查找
缓存中找到,直接进行数据返回
如果没找到,去数据库中查找
查找到之后,添加到缓存中
返回到页面上
4 使用
安装 flask-cache
pip install flask-cache
初始化
指定使用的缓存方案
cache = Cache(config={'CACHE_TYPE':默认是simple})
cache.init_app(app=app)
使用
在路由的下面添加@cache.cached(timeout=30)
要配置config
TYPE
还可以配置各种缓存的配置信息
cache=Cache(config={'CACHE_KEY_PREFIX':'python'})
用法
装饰器
@cache.cached(timeout=xxx)
原生
get
set
5.钩子
before_request
6.四大内置对象
四大内置对象
request
- 请求的所有信息
session
- 服务端会话技术的接口
config
当前项目的配置信息
模板中可以直接使用
config
在python代码中
current_app.config
当前运行的app
使用记得是在初始化完成之后
用在函数中
g
global 全局
可以帮助开发者实现跨函数传递数据
7.路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
init中app = Flask(__name__,static_folder=static_folder)
static_folder=os.path.join(settings.BASE_DIR,'static')
8.Json
JSON
json数据不可以直接返回
json例子 $.getJson
jquery cdn
Day05
1.前后端分离
RESTful API
什么是REST
一种软件架构风格、设计风格、而不是标准,只是提供了一组设计原则和
约束条件。它主要用户客户端和服务器交互类的软件。
基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
REST全称是Representational State Transfer,表征性状态转移。
首次在2000年Roy Thomas Fielding的博士论文中出现,Fielding是一个
非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者,
Apache服务器软件的作者之一,Apache基金会的第一任主席。所以,
他的这篇论文一经发表,就引起了广泛的关注。
论文:
本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,
软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估
不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统
之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,
那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。
我这篇文章的写作目的,就是想在符合架构原理的前提下,
理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、
性能好、适宜通信的架构。
REST爆发
其实在REST架构推出的十几年间,它并没有一路高歌的发展,
真正的大范围推广是在2013年之后,伴随着移动端的飞速发展,
越来越多人的开始意识到,网站即软件,而且是一种新型的软件。
这种"互联网软件采用"客户端/服务器"模式,也就是我们常说的C/S模式,
这一切建立在分布式体系上,通过互联网通信,具有高延时,高并发等特点。
网站开发,完全采用软件开发开发的模式。但传统上,软件和网络是两个
不同的领域,很少有交集,软件开发主要针对单机环境,网络则主要
研究系统之间的通信。我们需要考虑的是如何开发在互联网环境中使用软件。
理解RESTful
要理解RESTful架构,最好的就是去理解它的单词 Representational State
Transfer 到底是什么意思,它的每一个词到底要表达什么。
REST的释义,“表现层状态转化”,其实这省略了主语。“表现层”其实指的是
“资源(Resource)”的“表现层”。
资源(Resource)
所谓“资源”,就是网络上的一个实体,或者说是网络上的一个具体信息。
它可以是一段文本,一张图片,一首歌曲,一种服务,总之就是一个具体的实例
。你可以使用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI
。要获取这个资源,访问它的URI就可以了,因此URI就成了每一个资源
的地址或独一无二的识别符。所谓“上网”就是与互联网上一系列的“资源”互动
,调用它们的URI。
表现层(Representation)
“资源”是一种信息实体,它可以有多种外在表现形式。我们把“资源”
具体呈现出来的形式,叫做它的”表现层“(Representation)。
URI只代表资源的实体,不代表它的形式。严格地说,有些网站最后的”
.html“后缀名是不必要的,因为这个后缀表示格式,属于”表现层“范畴,
而URI应该只代表”资源“的位置。它的具体表现形式,应该在HTTP请求
头的信息中使用Accept和Content-Type字段指定。
状态转换(State Transfer)
访问一个网站,就代表客户端和服务端的一个互动过程。在这个过程中,
势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,
所有的状态都保存在服务端。因此,如果客户端想要操作服务器,
就必须通过某种手段,让服务器端发生”状态转换(State Transfer)“。
而这种转换是建立在表现层之上的,所以就是”表现层状态转化“。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议中,
四个表示操作方式的动词:GET,POST,PUT,DELETE。
它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源
(也可用于更新资源),PUT用来更新资源,DELETE用来删除资源
到底什么是RESTful架构
每一个URI代表一种资源
客户端和服务器之间,传递这种资源的某种表现层
客户端通过四个HTTP动词,对服务端资源进行操作,实现”表现层状态转换“
RESTful API设计
协议
API与用户的通信协议,通常使用HTTP(S)协议。
域名
应该尽量将API部署在专用域名之下。
http://api.rock.com
如果确定API很简单,不会有大规模扩充,可以考虑放在主域名之下。
http://www.rock.com/api/
版本
应该将API的版本号放入URL。
http://api.rock.com/v1/
也有做法是将版本号放在HTTP的头信息中,但不如放在URL中方便和直观。
GITHUB是这么搞的。
路径(Endpoint)
路径又称”终点“(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(Resource),
所以网址中不能有动词,只能有名词,而且所用的名词往往与
数据库的表格名对应。一般来说,数据库中的表都是同种记录的”集合“
(collection),所以API中的名词也应该使用复数。
HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
HTTP常用动词
GET(SELECT):从服务器取出资源
POST(CREATE or UPDATE):在服务器创建资源或更新资源
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)
DELETE(DELETE):从服务器删除资源
HEAD:获取资源的元数据
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的、
示例
GET /students:获取所有学生
POST /students:新建学生
GET /students/id:获取某一个学生
PUT /students/id :更新某个学生的信息(需要提供学生的全部信息)
PATCH /students/id:更新某个学生的信息(需要提供学生变更部分信息)
DELETE /students/id:删除某个学生
过滤信息(Filtering)
如果记录数量过多,服务器不可能将它们返回给用户。API应该提供参数,
过滤返回结果。
?limit=10
?offset=10
?page=2&per_page=20
?sortby=name&order=desc
?student_id=id
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复,
比如 GET /students/id 和 ?student_id=id
状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些地方
200 OK - [GET]:服务器成功返回用户请求的数据
201 CREATED -[POST/PUT/PATCH]:用户新建或修改数据成功
202 Accepted - [*] :表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:表示数据删除成功
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误
401 Unauthorized - [*] :表示用户没有权限(令牌,用户名,密码错误)
403 Forbidden - [*]:表示用户得到授权,但是访问是被禁止的
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录
406 Not Acceptable - [*]:用户请求格式不可得
410 Gone - [GET] :用户请求的资源被永久移除,且不会再得到的
422 Unprocesable entity -[POST/PUT/PATCH]:当创建一个对象时,
发生一个验证错误
500 INTERNAL SERVER EROR - [*] :服务器内部发生错误
错误处理
如果状态码是4xx,就应该向用户返回出错信息。一般来说,
返回的信息中将error做为键名
返回结果
针对不同操作,服务器想用户返回的结果应该符合以下规范
GET /collection:返回资源对象的列表(数组,集合)
GET /collection/id:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/id:返回完整的资源对象
PATCH /collection/id:返回完整的资源对象
DELETE /collection/id:返回一个空文档
使用链接关联资源
RESTful API最好做到Hypermedia,即返回结果中提供链接,
连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{
"link": {
"rel": "collection https://www.rock.com/zoostudents",
"href": "https://api.rock.com/students",
"title": "List of students",
"type": "application/vnd.yourformat+json"
}
}
rel:表示这个API与当前网址的关系
href:表示API的路径
title:表示API的标题
type:表示返回的类型
其它
服务器返回的数据格式,应该尽量使用JSON
API的身份认证应该使用OAuth2.0框架
总之
为什么会出现Restful
在Restful之前的操作:
saveUser
http://127.0.0.1/user/query/1 GET 根据用户id查询用户数据
http://127.0.0.1/user/save POST 新增用户
http://127.0.0.1/user/update POST 修改用户信息
http://127.0.0.1/user/delete GET/POST 删除用户信息
RESTful用法:根据请求提交方式的不同 然后执行对应的方法
http://127.0.0.1/user/1 GET 根据用户id查询用户数据
http://127.0.0.1/user POST 新增用户
http://127.0.0.1/user PUT 修改用户信息
http://127.0.0.1/user DELETE 删除用户信息
if request.method = ‘get’:
User.query.first()
else request.method = ‘post’:
之前的操作是没有问题的,大神认为是有问题的,有什么问题呢?
你每次请求的接口或者地址,都在做描述,例如查询的时候用了query,
新增的时候用了save,其实完全没有这个必要,我使用了get请求,就是查询.
使用post请求,就是新增的请求,我的意图很明显,完全没有必要做描述,
这就是为什么有了restful.
ajax其实质利用浏览器内置ajax对象(xmlhttprequest xhr)
异步的向服务器发送请求
提交的是部分数据
利用返回的数据更新当前页面
整个过程中
页面无刷新
不打断用户的操作
整合网络和软件的一种架构模式
理解
Representtational
表现层
State Transfer
状态转换
表现层状态转换
资源(Resource)
每一个URI代表一类资源
对整个数据的操作
增删改查
RESTful中更推荐使用HTTP的请求谓词(动词)来作为动作标识
GET
POST
PUT
DELETE
PATCH
推荐使用json数据传输
状态码
200 ok
201 created
202 Accepted
204 删除成功
400
401
没有认证
403
没有权限
404
405
406 验证错误
422
500
设计原则
http(s)协议
应该有自己专属域名
在应用上添加一个api前缀
都是名词,复数形式
可以将版本号设计进去
增量操作
/collection/id/? id=xxx
2.原生实现
概念:就是判断不同的请求方式,实现请求方法
高内聚,低耦合
高内聚
相同的数据操作封装在一起
低耦合
MVC 没有模板--前后端分离
使用
md5
eg:
def generate_password(password):
hash = hashlib.md5()
hash.update(password.encode("utf-8"))
return hash.hexdigest()
get
坑---类型
eg:
@blue.route("/users/<int:id>/", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
def users(id):
if request.method == "GET":
page = int(request.args.get("page", default=1))
per_page = int(request.args.get("per_page", default=3))
users = User.query.paginate(page=page, per_page=per_page, error_out=False).items
users_dict = []
for user in users:
users_dict.append(user.to_dict())
data = {
"message": "ok",
"status": "200",
"data": users_dict
}
return jsonify(data)
post
eg:
elif request.method == "POST":
# 更新或创建
username = request.form.get("username")
password = request.form.get("password")
data = {
"message": "ok",
"status": "422"
}
if not username or not password:
data["message"] = "参数不正确"
return jsonify(data), 422
user = User()
user.u_name = username
user.u_password = generate_password(password=password)
try:
db.session.add(user)
db.session.commit()
data["status"] = "201"
except Exception as e:
data["status"] = "901"
data["message"] = str(e)
return jsonify(data), 422
return jsonify(data), 201
put
eg:
elif request.method == "PUT":
username = request.form.get("username")
password = request.form.get("password")
user = User.query.get(id)
user.u_name = username
user.u_password = generate_password(password)
db.session.add(user)
db.session.commit()
data = {
"message": "update success",
}
return jsonify(data), 201
delete
eg:
elif request.method == "DELETE":
user = User.query.get(id)
data = {
"message": "delete success"
}
if user:
db.session.delete(user)
db.session.commit()
return jsonify(data), 204
else:
data["message"] = "指定数据不存在"
return jsonify(data)
patch
eg:
elif request.method == "PATCH":
password = request.form.get("password")
user = User.query.get(id)
user.u_password = generate_password(password)
data = {
"messgage": "update success"
}
db.session.add(user)
db.session.commit()
return jsonify(data), 201
3.flask-restful
框架简化开发
使用
使用
安装 pip install flask-restful
初始化
urls---在init中调用init_urls
api = Api()
api.add_resource(Hello, "/hello/")
Hello是一个类的名字 hello是路由
def init_urls(app):
api.init_app(app=app)
apis--基本用法
继承自Resource
class Hello(Resource):
实现请求方法对应函数
def get(self):
return {"msg": "ok"}
def post(self):
return {"msg": "create success"}
定制输入输出
定制输入输出
输出
fields中的类型约束
String
Integer
Nested
List
Nested
@marshal_with的基本使用
类型括号中还允许添加约束
attribute
指定连接对应名字
attribute=名字
default
设置默认值
default=404
marshal_with特性
- 默认返回的数据如果在预定义结构中不存在,数据会被自动过滤
- 如果返回的数据在预定义的结构中存在,数据会正常返回
- 如果返回的数据比预定义结构中的字段少,预定义的字段会呈现一个默认值
如果类型是Integer 那么默认值是 0
如果类型是String 那么默认值是null
@marshal_with返回一个类对象
@marshal_with返回一个列表
输入
使用了parser=reqparse.RequestParser()
parser.add_argument("c_name", type=str)
parser.add_argument("id", type=int, required=True, help="id 是必须的")
parse = parser.parse_args()
cat_name = parse.get("c_name")
id = parse.get("id")
print(id)
在对象中添加字段
对字段添加约束
default
required
必须的参数
help
id是必须的
action
action=append
c_name=tom&c_name=zs
继承
copy
可以对已有字段进行删除和更新
继承解析
- 在整个项目中,通用字段可以创建一个基parser
- 复用已有的部分参数转换数据结构
parse_c parser.copy() parse_c.remove_argument('')
李晶1:
__init__.py
import os
from flask import Flask
from App import settings
from App.ext import init_ext
def create_app(envname):
# 获取项目路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(BASE_DIR)
templates_folder = os.path.join(BASE_DIR,'templates')
print(templates_folder)
static_folder = os.path.join(BASE_DIR,'static')
print(static_folder)
app = Flask(__name__,template_folder=templates_folder,static_folder=static_folder)
app.config.from_object(settings.ENV_NAME.get(envname))
init_ext(app)
return app
ext.py
# flask-script
# flask-blueprint
# flask-session
# flask-sqlalchemy
# flask-migrate
from flask_bootstrap import Bootstrap
from flask_cache import Cache
from flask_debugtoolbar import DebugToolbarExtension
from flask_migrate import Migrate
from flask_session import Session
from App.models import db
cache = Cache(config={'CACHE_TYPE': 'redis'})
def init_ext(app):
# 有循环导入问题
# app.register_blueprint(blueprint=blue)
app.config['SECRET_KEY']='110'
app.config['SESSION_TYPE']='redis'
Session(app=app)
db.init_app(app=app)
migrate = Migrate()
migrate.init_app(app=app,db=db)
Bootstrap(app=app)
app.debug = True
# debugtoolbar
debugtoolbar = DebugToolbarExtension()
debugtoolbar.init_app(app=app)
# cache
# cache有底层源码错误
# 解决办法 鼠标防到Cache上 ctrl + B 点击jinja2ext.py
# 进入之后 观察倒包 有一个叫做 flask.ext.cache的修改为flask_cache
# from flask.ext.cache import make_template_fragment_key===>
# from flask_cache import make_template_fragment_key
# cache = Cache(config={'CACHE_TYPE':'redis'})
cache.init_app(app=app)
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
setting.py
def get_database_uri(DATABASE):
dialect = DATABASE.get('dialect')
driver = DATABASE.get('driver')
username = DATABASE.get('username')
password = DATABASE.get('password')
host = DATABASE.get('host')
port = DATABASE.get('port')
database = DATABASE.get('database')
return '{}+{}://{}:{}@{}:{}/{}'.format(dialect,driver,username,password,host,port,database)
class Config():
Test = False
Debug = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopConfig(Config):
Debug = True
DATABASE={
'dialect':'mysql',
'driver':'pymysql',
'username':'root',
'password':'1234',
'host':'localhost',
'port':'3306',
'database':'flaskday05',
}
SQLALCHEMY_DATABASE_URI=get_database_uri(DATABASE)
ENV_NAME={
'develop':DevelopConfig
}
views.py
from flask import Blueprint, request, render_template, current_app,g
from App.ext import cache
blue = Blueprint('blue',__name__)
@blue.route('/')
def hello_world():
return 'Hello World!'
@blue.route('/index/')
# 不能导入---》 因为在init方法中
# 能导入-循环导入问题---》
# view -->ext cache
# ext --->view blue
@cache.cached(timeout=30)
def index():
print('你们多少个人去看电影阿 千万不要剧透')
return 'index'
# 第一次访问的时候 显示 欢迎光临 第二次访问的时候 显示小爬虫快走开
@blue.route('/index1/')
def index1():
ip = request.remote_addr
# 缓存中如果有记录 那么就证明你来过
# get获取的意思 set设置的意思
result = cache.get('ip')
if result:
return '小爬虫快走开'
else:
cache.set('ip',ip,timeout=30)
return '欢迎光临'
# AOP flask中 钩子 和 django中的中间件
# AOP 面向切面编程, 面向方面编程
# 在执行某方法之前 或者之后 执行一些方法
# eg: pymysql
# add delete update select
# coonection cursor execute close
# 每执行一个都需要链接 和 关闭
@blue.route('/testaop/')
def testaop():
print('i am testaop')
return 'ok'
# 默认情况下before_request是带()的 如果我们使用aop 需要将()删除
# @blue.before_request
# def aop():
# print('i am aop simida')
@blue.route('/add/')
def add():
print('i am add')
return 'add'
@blue.route('/delete/')
def delete():
print('i am delete')
return 'delete'
@blue.route('/testconfig/')
def testconfig():
for c in current_app.config:
print(c)
return render_template('testconfig.html')
@blue.route('/g/')
def g():
g.ip = request.remote_addr
return 'ok'
@blue.route('/testg1/')
def testg1():
# 问:怎么获取ip (不能移动ip的位置)
print(g.ip)
return 'ok1'
testconfig.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for c in config %}
<li>{{ c|lower }}</li>
{% endfor %}
</ul>
</body>
</html>
李晶Day05FZFlask:
flask-restful实现步骤
1 pip install flask-restful
2 在ext中 创建api对象 并且初始化 指定资源
api = Api()
api.init_app(app=app)
api.add_resource(资源的类,路由路径) eg:api.add_resource(Hello,'/hello/')
3 注册资源
在apis模块中 创建一个类
class Hello(Resource):
def get(self):
data={}
return data
def post(self):
data={}
return data
def delete/put/patch(self):
data={}
return data
restful最重要的就是返回值
作业:
1 整理本周内容 复习
2 登录页面添加验证码
3 注册页面当输入邮箱的时候 会往邮箱发送件 在邮箱激活之后 才会可以登录
__init__.py
from flask import Flask
from App import settings
from App.ext import init_ext
def create_app():
app = Flask(__name__)
# session
# app.config['SECRET_KEY']='114'
# app.config['SESSION_TYPE']='redis'
# Session(app=app)
# 建立与settings文件的关键
# settings.ENV_NAME.get('develop')获取的是settings文件中的value值
# app.config.from_object(DevelopConfig)将类加载过来
app.config.from_object(settings.ENV_NAME.get('develop'))
# sqlalchemy
# db.init_app(app=app)
init_ext(app)
return app
apis.py
# apis和views是一样的
from flask_restful import Resource
class Hello(Resource):
def get(self):
data = {
'msg':'get'
}
return data
def post(self):
data = {
'msg': 'post'
}
return data
ext.py
# 用来存放第三方扩展库
from flask_restful import Api
from flask_session import Session
from App.apis import Hello
from App.models import db
def init_ext(app):
app.config['SECRET_KEY']='114'
app.config['SESSION_TYPE']='redis'
Session(app=app)
db.init_app(app=app)
# restful
api = Api()
api.add_resource(Hello,'/hello/')
api.init_app(app=app)
views.py
from flask import Blueprint
blue = Blueprint('blue',__name__)
@blue.route('/')
def index():
return 'index'
FzFlask1:
__init__.py
from flask import Flask
from App import settings
from App.ext import init_ext
def create_app(envname):
app = Flask(__name__)
app.config.from_object(settings.ENV_NAME.get(envname))
init_ext(app)
return app
ext.py
'''
flask-script
flask-blueprint
flask-session
flask-sqlalchemy
'''
from flask_migrate import Migrate
from flask_session import Session
from App.models import db
def init_ext(app):
# session
app.config['SECRET_KEY']='10010'
app.config['SESSION_TYPE']='redis'
Session(app=app)
# sqlalchemy
db.init_app(app=app)
# migrate
migrate = Migrate()
# 注意参数的个数
migrate.init_app(app=app,db=db)
views.py
from flask import Blueprint, request
from flask.json import jsonify
from App.models import User, db
blue = Blueprint('blue',__name__)
@blue.route('/index/')
def index():
return 'index'
@blue.route('/getjson/')
def getjson():
data = {
'resultCode':'200',
'resultValue':'封神演义',
}
# 注意flask中视图函数 不允许直接返回json 因为视图函数返回的是应该是 字符传 元祖 response实例
# flask.json是flask的子类
return jsonify(data)
# 在后端 定义一个列表 注意列表中的数据是python中基础数据类型
@blue.route('/getscore/')
def getscore():
score_list = [4,6,3,7,9,10]
data = {
'status':200,
'score_list':score_list
}
return jsonify(data)
@blue.route('/adduser/',methods=['get','post'])
def adduser():
name = request.form.get('name')
age = request.form.get('age')
user = User()
user.name = name
user.age = age
db.session.add(user)
db.session.commit()
data = {'msg':'success'}
return jsonify(data)
# 通过请求方式执行不同的代码 并且返回的是json数据
@blue.route('/user/',methods=['GET','POST','DELETE','PUT','PATCH'])
def user():
data = {
'msg':'ok'
}
if request.method == 'GET':
print('111111111111111')
data['msg'] = 'get'
return jsonify(data)
elif request.method == 'POST':
data['msg'] = 'post'
return jsonify(data)
elif request.method == 'DELETE':
data['msg'] = 'delete'
return jsonify(data)
elif request.method == 'PUT':
data['msg'] = 'put'
return jsonify(data)
elif request.method == 'PATCH':
data['msg'] = 'patch'
return jsonify(data)
return jsonify(data)
manager.py
from flask_migrate import MigrateCommand
from flask_script import Manager
from App import create_app
from App.views import blue
app = create_app('develop')
manager = Manager(app=app)
manager.add_command('db',MigrateCommand)
app.register_blueprint(blueprint=blue)
if __name__ == '__main__':
manager.run()
json:一种轻量级的数据交换标准
物物交换
货币:贝壳 通过贝壳来买卖
人民币
python对象==》json《=== js对象
json/xml
xml可扩展的标记语言
<python1901></python1901>
常用于java语言
json解析速度快 存储文档小
html 超文本标记语言
文本标记语言 简单标签 超级文本标记语言(图片 视频 音频。。。)
浏览器小名 html解析器
http://127.0.0.1:5000/user/add
http://127.0.0.1:5000/user/delete
http://127.0.0.1:5000/user/update
http://127.0.0.1:5000/user/select
rest 通过请求的提交方式不同 然后去执行对象方法
http://127.0.0.1:5000/user
请求提交方式 get 查询
post 添加
delete 删除
put 修改 全部数据修改
patch 修改 部分数据修改
addUser.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/js/jquery.js"></script>
<script type="text/javascript">
$(function(){
$('button').click(function () {
var name = $('#name').val();
var age = $('#age').val();
$.ajax(
{
url:'/adduser/',
type:'post',
data:{'name':name,'age':age},
dataType:'json',
success:function (data) {
console.log(data);
}
}
)
})
})
</script>
</head>
<body>
name:<input type="text" id="name"><br>
age:<input type="text" id="age"><br>
<button>提交</button>
</body>
</html>
scoreList.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#b').click(function () {
$.getJSON('/getscore/',function (data) {
//{#不能通过data.get获取数据 并且注释还有问题 需要删除 #}
//{#score_list = data.get('score_list');#}
//score_list = data['score_list']
score_list = data.score_list;
$ul = $('ul');
for(var i=0; i<score_list.length;i++){
score = score_list[i];
$li = $('<li></li>');
$li.html(score);
$ul.append($li);
}
})
})
})
</script>
</head>
<body>
<button id="b">点我</button>
<ul>
</ul>
</body>
</html>
Day06
1.验证码
def get_code(request):
# 初始化画布,初始化画笔
mode = "RGB"
size = (200, 100)
red = get_color()
green = get_color()
blue = get_color()
color_bg = (red, green, blue)
image = Image.new(mode=mode, size=size, color=color_bg)
imagedraw = ImageDraw(image, mode=mode)
imagefont = ImageFont.truetype(settings.FONT_PATH, 100)
verify_code = generate_code()
request.session['verify_code'] = verify_code
for i in range(4):
fill = (get_color(), get_color(), get_color())
imagedraw.text(xy=(50*i, 0), text=verify_code[i], font=imagefont, fill=fill)
for i in range(100):
fill = (get_color(), get_color(), get_color())
xy = (random.randrange(201), random.randrange(100))
imagedraw.point(xy=xy, fill=fill)
fp = BytesIO()
image.save(fp, "png")
return HttpResponse(fp.getvalue(), content_type="image/png")
import random
def get_color():
return random.randrange(256)
def generate_code():
source = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM"
code = ""
for i in range(4):
code += random.choice(source)
return code
2.发送邮件
1 前后端分离 源码 restful
2 restful的结构化输出 2种输出
3 restful输入
4 注册 前端验证 js 验证
后端验证 用户名字是否存在 鼠标失去焦点 ajax
邮箱验证
提交表单 向邮箱发送邮件 打开邮箱 点击激活链接 将状态变成激活状态
邮件的生存时间
画一个页面
学生姓名 后台验证 前台验证
学生性别
学生年龄
学生密码
确认密码 前台密码确认验证
邮箱地址 发送邮件
激活状态 邮件的生存时间
邮箱激活:
flask-mail
1 设置邮箱的授权码
点击设置 然后 选择pop3 设置授权码
2 注意 授权码和密码不能一致
3 pip install flask-mail
4 mail = Mail()
5 mail.init_app(app=app)
6 在settings中写 MAIL_SERVER MAIL_USERNAME MAIL_PASSWORD
7 在apis中 msg = Message(subject sender recipients注意列表)
8 msg.html = 字符串 render_templates
9 mail.send(msg)
作业:
邮件的内容
尊敬的zslsww5您好:
请点击链接进行激活激活
http://127.0.0.1:8000/axf/doactive/?name=zslsww5
点击激活 修改active的状态
5 登录 验证码 先验证验证码 减少数据库io操作
6 celery
day06代码:
apis文件包
Cat1Apis.py
from flask_restful import Resource
class Cat1Resource(Resource):
def get(self):
return 'i am ok'
Cat2Apis.py
from flask_restful import Resource
class Cat2Resource(Resource):
def get(self):
return 'i am ok1'
Cat3Apis.py
from flask_restful import Resource, marshal_with, fields
class Cat3Resource(Resource):
'''
- 默认返回的数据如果在预定义结构中不存在,数据会被自动过滤
- 如果返回的数据在预定义的结构中存在,数据会正常返回
- 如果返回的数据比预定义结构中的字段少,预定义的字段会呈现一个默认值
如果类型是Integer 那么默认值是 0
如果类型是String 那么默认值是null
'''
r1fields={
# fields后面的类型 可以加() 可以不加()
'msg':fields.String(),
# 'resultCode':fields.Integer,
# 'resultValue':fields.String
# 'status':fields.String
'resultValue':fields.String(default='dddd'),
'status':fields.String(attribute='xxx')
}
# 结构化输出:按照指定的格式输出数据
@marshal_with(r1fields)
def get(self):
data = {
'msg':'ok',
'status':'200',
'xxx':'xxx',
}
return data
Cat4Apis.py
from flask_restful import Resource, fields, marshal
r4fields = {
'msg': fields.String(),
'resultCode': fields.Integer,
}
class Cat4Resource(Resource):
def get(self):
data = {
'msg':'ok',
'status':'200'
}
return marshal(data=data,fields=r4fields)
Cat5Apis.py
from flask_restful import Resource, marshal_with, fields
from App.models import Cat
catfields = {
'id':fields.Integer,
'name':fields.String,
'color':fields.String
}
r5fields = {
'msg':fields.String,
'cat':fields.Nested(catfields)
}
class Cat5Resource(Resource):
@marshal_with(r5fields)
def get(self):
cat = Cat.query.first()
data = {
'msg':'ok',
'cat':cat
}
return data
Cat6Apis.py
from flask_restful import Resource, marshal, fields
from App.models import Cat
catfields = {
'id':fields.Integer,
'name':fields.String,
'color':fields.String,
}
r6fields = {
'msg':fields.String,
'cats':fields.List(fields.Nested(catfields))
}
class Cat6Resource(Resource):
def get(self):
cats = Cat.query.all()
data = {
'msg':'ok',
'cats':cats
}
return marshal(data=data,fields=r6fields)
CatApis.py
from flask_restful import Resource
class CatResource(Resource):
def get(self):
return {'msg':'ok'}
DogApis.py
from flask_restful import Resource, reqparse
# 输入
# 1 获取parser对象 eg:parser = reqparser.RequestParser()
# 2 将参数绑定到parser上 eg:parser.add_argument(name,type,help,required)
# name 参数的名字 type 参数的类型 help如果没有参数的提示 required必须书写
# 3 将parser里的参数进行解析 解析之后为parse对象 eg parse = parser.parse_args()
# 4 可以通过parse对象的get方法获取参数值
parser = reqparse.RequestParser()
parser.add_argument(name='name',type=str,help='请输入用户名字',required=True)
parser.add_argument(name='age',type=int,help='请输入年龄',required=True)
parser.add_argument(name='color',type=str,help='请输入颜色',required=True)
class DogResource(Resource):
# 获取 name 然后如果用户名字为空 那么提示 用户名字不可以为空
# 获取年龄 然后如果年龄为空 那么提示 年龄不可以为空
# 获取颜色 然后如果颜色为空 那么提示 颜色不可以为空
def post(self):
parse = parser.parse_args()
name = parse.get('name')
age = parse.get('age')
color = parse.get('color')
print(name,age,color)
return 'i am ok'
StudentApis.py
from flask import request
from flask_mail import Message
from flask_restful import Resource
from App.ext import mail
from App.models import Student, db
class StudentResource(Resource):
# 前端验证 如果 符合要求 那么显示绿色字体 如果不符合要求 那么显示红色字体
def post(self):
# name = request.form.get('name')
#
#
#
# students = Student.query.filter(Student.name == name)
#
# data = {
# 'msg':'用户名字可以注册',
# 'status':'200',
# }
#
# if students.count() > 0:
# student = students.first()
# data['status'] = 422
# data['msg'] = '用户已经存在'
# return data
# return data
name = request.form.get('name')
gender = request.form.get('gender')
age = request.form.get('age')
password = request.form.get('password')
email = request.form.get('email')
student = Student()
student.name = name
student.gender = gender
student.age = age
student.password = password
student.email = email
db.session.add(student)
db.session.commit()
# 参数有三个 subject 主题 sender 发送者 recipients接受者
msg = Message(subject='激活',sender='yulin_ljing@163.com',
recipients=[email])
msg.html = "<h1>testingxxxx</h1>"
mail.send(msg)
return {'msg':'ok'}
__init__.py
from flask_restful import Resource, Api
#
# api = Api()
#
#
# def init_urls(app):
# api.init_app(app=app)
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/js/jquery.js"></script>
<script type="text/javascript">
$(function () {
/**
$('#name').blur(function () {
* 前端验证
var name = $(this).val();
var reg = /^[a-z]{3,6}$/;
//reg的test方法 是判断某字符串是否满足 某正则
//如果满足返回true 没有满足返回的是false
if(reg.test(name)){
$('#nameinfo').css('color','green');
}else{
$('#nameinfo').css('color','red');
}
//后台验证
var name = $(this).val();
$.ajax({
url:'/student/',
type:'post',
data:{'name':name},
dataType:'json',
success:function (data) {
var status = data['status']
if(status === 422){
$('#nameinfo').html(data['msg']);
}else{
$('#nameinfo').html(data['msg']);
}
}
})
});
*/
$('#sub').click(function () {
var name = $('#name').val();
var gender = $('#gender').val();
var age = $('#age').val();
var password = $('#password').val();
var email = $('#email').val();
$.ajax({
url:'/student/',
type:'post',
data:{
'name':name,
'gender':gender,
'age':age,
'password':password,
'email':email
},
dataType:'json',
success:function (data) {
console.log(data);
}
})
})
})
</script>
</head>
<body>
<!--{# 学生姓名 后台验证 前台验证
{# 学生性别
{# 学生年龄
{# 学生密码
{# 确认密码 前台密码确认验证
{# 邮箱地址 发送邮件
{# 激活状态 邮件的生存时间#}
-->
name :<input type="text" id="name">
<span id="nameinfo">请输入3到6个英文字母</span>
<br/>
gender:<input type="text" id="gender"><br/>
age :<input type="text" id="age"><br/>
passwd:<input type="password" id="password"><br/>
confirm:<input type="password" id="confirmpassword"><br/>
email:<input type="text" id="email"><br/>
<input type="button" value="提交" id="sub">
</body>
</html>
__init__.py
from flask import Flask
from App import settings
from App.ext import init_ext
from App.urls import init_urls
def create_app(envname):
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
app.config.from_object(settings.ENV_NAME.get(envname))
init_ext(app)
init_urls(app)
return app
ext.py
# flask-script flask-blueprint flask-session flask-sqlalchemy
# flask-migrate flask-boostrap flask-debugtoolbar flask-cache
# flask-restful
from flask_bootstrap import Bootstrap
from flask_cache import Cache
from flask_debugtoolbar import DebugToolbarExtension
from flask_mail import Mail
from flask_migrate import Migrate
from flask_session import Session
from App.models import db
from App.views import blue
cache = Cache(config={'CACHE_TYPE':'redis'})
mail = Mail()
def init_ext(app):
app.register_blueprint(blueprint=blue)
# flask-session
app.config['SECRET_KEY']='110'
app.config['SESSION_TYPE']='redis'
session = Session()
session.init_app(app=app)
# flasksqlalchemy
db.init_app(app=app)
# flask-migrate
# python manager.py db init
# python manager.py db migrate
# python manager.py db upgrade
# python manager.py db downgrade
migrate = Migrate()
migrate.init_app(db=db,app=app)
# flask-bootstrap
bootstrap = Bootstrap()
bootstrap.init_app(app=app)
# flask-debugtoolbar
app.debug = True
debugtoolbar = DebugToolbarExtension()
debugtoolbar.init_app(app=app)
# flask-cache
# cache = Cache(config={'CACHE_TYPE': 'redis'})
cache.init_app(app=app)
# flask-mail
mail.init_app(app=app)
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(32))
def to_dict(self):
return {'id':self.id,'name':self.name}
class Cat(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(32))
color = db.Column(db.String(16))
class Dog(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(32))
age = db.Column(db.Integer)
color = db.Column(db.String(16))
'''
学生姓名 后台验证 前台验证
学生性别
学生年龄
学生密码
确认密码 前台密码确认验证
邮箱地址 发送邮件
激活状态 邮件的生存时间'''
class Student(db.Model):
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(32),unique=True)
gender = db.Column(db.String(16),default='1')
age = db.Column(db.Integer)
password = db.Column(db.String(256))
email = db.Column(db.String(64))
active = db.Column(db.Boolean,default=False)
settings.py
def get_database_uri(DATABASE):
dialect = DATABASE.get('dialect')
driver = DATABASE.get('driver')
username = DATABASE.get('username')
password = DATABASE.get('password')
host = DATABASE.get('host')
port = DATABASE.get('port')
database = DATABASE.get('database')
return '{}+{}://{}:{}@{}:{}/{}'.format(dialect,driver,username,password,host,port,database)
class Config():
Test = False
Debug = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
MAIL_SERVER = 'smtp.163.com'
MAIL_USERNAME = 'yulin_ljing@163.com'
MAIL_PASSWORD = 'lijing0501115'
class DevelopConfig(Config):
Debug = True
DATABASE = {
'dialect':'mysql',
'driver':'pymysql',
'username':'root',
'password':'1234',
'host':'localhost',
'port':'3306',
'database':'finalflask'
}
SQLALCHEMY_DATABASE_URI = get_database_uri(DATABASE)
ENV_NAME = {
'develop':DevelopConfig
}
urls.py
from flask_restful import Api
from App.apis.Cat1Apis import Cat1Resource
from App.apis.Cat2Apis import Cat2Resource
from App.apis.Cat3Apis import Cat3Resource
from App.apis.Cat4Apis import Cat4Resource
from App.apis.Cat5Apis import Cat5Resource
from App.apis.Cat6Api import Cat6Resource
from App.apis.CatApis import CatResource
from App.apis.DogApis import DogResource
from App.apis.StudentApis import StudentResource
api = Api()
def init_urls(app):
api.init_app(app=app)
api.add_resource(CatResource,'/cat/')
api.add_resource(Cat1Resource,'/cat1/')
api.add_resource(Cat2Resource,'/cat2/')
api.add_resource(Cat3Resource,'/cat3/')
api.add_resource(Cat4Resource,'/cat4/')
api.add_resource(Cat5Resource,'/cat5/')
api.add_resource(Cat6Resource,'/cat6/')
api.add_resource(DogResource,'/dog/')
api.add_resource(StudentResource,'/student/')
views.py
from flask import Blueprint, jsonify
from App.models import User
blue = Blueprint('blue',__name__)
@blue.route('/index/')
def index():
return 'index'
@blue.route('/getuser/')
def getuser():
# 视图函数返回的是json数据 那么数据中 不可以直接返回自定义类型的对象的
# 可以直接返回python自带的数据类型对象
user = User.query.first()
# user = [1,2,3]
data = {
'msg':'ok',
'user':user.to_dict(),
}
return jsonify(data)
@blue.route('/getusers/')
def getusers():
users = User.query.all()
user_list = []
for user in users:
user_list.append(user.to_dict())
data = {
'msg':'ok',
'users':user_list
}
return jsonify(data)
manager.py
from flask_migrate import MigrateCommand
from flask_script import Manager
from App import create_app
app = create_app('develop')
manager = Manager(app=app)
manager.add_command('db',MigrateCommand)
if __name__ == '__main__':
manager.run()