持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
用户操作
<!--show.html-->
<tr>
<td>{{ loop.index }}</td>
<td>{{ user.username }}</td>
<td>{{ user.phone }}</td>
<td>{{ user.rdatetime }}</td>
<td>
<a href="{{ url_for('user.update') }}?id={{ user.id }}">update</a>
<a href="{{ url_for('user.delete') }}?id={{ user.id }}">delete</a>
{#错误写法 <a href="/{{ url_for('user.delete') }}?id={{ user.id }}">delete</a> #}
{# 这样写他就找 / #}
</tr>
查询
<div>
<p>
<input type="text" placeholder="search" name="search">
<input type="button" value="search" id="search">
</p>
</div>
# view.py
@user_bp.route('/search', endpoint='search')
def user_search():
if request.args.get('search'):
keyword = request.args.get('search')
user_list = User.query.filter(or_(User.username.contains(keyword), User.username.contains(keyword))).all()
# 查询手机号或者用户名
return render_template('user/show.html', users=user_list)
else:
return redirect(url_for('user.show'))
逻辑删除
就相当于更新,在数据库里添加isdelete字段进行判断
#model.py
from datetime import datetime
from ext import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), nullable=False)
password = db.Column(db.String(64), nullable=False)
phone = db.Column(db.String(11), unique=True)
rdatetime = db.Column(db.DateTime, default=datetime.now)
isdelete = db.Column(db.Boolean, default=False)
# 逻辑删除字段
#view.py
@user_bp.route('/delete', endpoint='delete')
def user_delete():
id = request.args.get('id')
user = User.query.get(id)
user.isdelete = True
db.session.commit()
return redirect(url_for('user.show'))
@user_bp.route('/show', endpoint='show')
def user_show():
users = User.query.filter(User.isdelete == False).all()
return render_template('user/show.html', users=users)
删除后数据库里依然有数据
bug:注册用户时手机号因为是unqiue的,如果手机号重复会报错
解决:注册时加一层数据库查询手机号,如果手机号存在则更新用户其他数据
物理删除
@user_bp.route('/delete', endpoint='delete')
def user_delete():
id = request.args.get('id')
user = User.query.get(id)
db.session.delete(user)
db.session.commit()
return redirect(url_for('user.show'))
不需要单独设置 isdelete 列
更新用户
#view.py
@user_bp.route('/update', endpoint='update', methods=['GET', 'POST'])
def user_update():
if request.method == 'POST':
id = request.form.get('id')
new_user = User.query.get(id)
new_user.username = request.form.get('new_username')
new_user.password = request.form.get('new_password')
new_user.phone = request.form.get('new_phone')
# 只有添加数据是用 db.sesson.add()
db.session.commit()
return redirect(url_for('user.show'))
else:
id = request.args.get('id')
user = User.query.get(id)
return render_template('user/update.html', user=user)
<!--update.html-->
{% extends 'base.html' %}
{% block middle %}
<form action="{{ url_for('user.update') }}" method="post">
<p><input type="hidden" name="id" value="{{ user.id }}"></p>
{# 隐藏表单提交用户id #}
<p><input type="text" name="new_username" value="{{ user.username }}"></p>
<p><input type="text" name="new_password" placeholder="new password"></p>
<p><input type="text" name="new_phone" value="{{ user.phone }}"></p>
<p><input type="submit" value="submit"></p>
</form>
{% endblock %}
多表关系
文档:www.pythondoc.com/flask-sqlal…
表与表间的关系可能十分复杂
#### 外键
外键表示了两个关系之间的相关联系,以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表,外键又称作外关键字
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
外键是从表中的一个字段
一对多
one to many项目完成总览
user/model.py
from datetime import datetime
from ext import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), nullable=False)
password = db.Column(db.String(64), nullable=False)
phone = db.Column(db.String(11), unique=True)
email = db.Column(db.String(25))
icon = db.Column(db.String(40))
isdelete = db.Column(db.Boolean, default=False)
rdatetime = db.Column(db.DateTime, default=datetime.now)
articles = db.relationship('Article', backref='user')
# 用于反向查找,实际不会添加到数据库中,相当于把两张表连起来
# 和外键匹配
# Article 为模型名大写
# user小写因为表名在数据库中为小写
article/model.py
from datetime import datetime
from ext import db
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
pdatetim = db.Column(db.DateTime, default=datetime.now)
click_num = db.Column(db.Integer, default=0)
save_num = db.Column(db.Integer, default=0)
love_num = db.Column(db.Integer, default=0)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
# 外键,这个字段会添加数据库中
@article_bp.route('/all')
def all_article():
articles = Article.query.all()
return render_template('article/all.html', articles=articles)
@article_bp.route('/all1')
def all_by_id():
id = request.args.get('id')
users = User.query.get(id)
return render_template('article/all1.html', users=users)
article/add_article.html
<form action="{{ url_for("article.publish") }}" method="post">
<p><input type="text" name="title" placeholder="文章标题"></p>
<p><textarea cols="50" rows="10" name="content" placeholder="输入文章内容"></textarea></p>
<p>
user:
<select name="uid">
<option value="0">please select user</option>
{% for user in users %}
<option value="{{ user.id }}">{{ user.username }}</option>
{% endfor %}
</select>
</p>
<p><input type="submit" value="add article"></p>
</form>
article/all.html
<!--文章->查user.username-->
{% for article in articles %}
<div id="article">
<p>
<h3>{{ article.title }}</h3>
<div>作者:{{ article.user.username }}</div>
{# backref 反向引用 #}
<p>
{{ article.content }}
</p>
<div>{{ article.pdatetime }}</div>
</p>
</div>
{% endfor %}
article/all1.html
{#根据用户id找文章#}
{% for article in users.articles %}
<div id="article">
<h1>
{{ article.title }}
</h1>
<div>
{{ users.username }}
</div>
<p>
{{ article.content }}
</p>
<p>
{{ article.pdatetim }}
</p>
</div>
{% endfor %}
多对多
many to many
可以通过中间表建立关系
pycharm查看表关系
首先选择多个表->diagrams->show
多对多实例:用户与商品
功能:用户和商品展示,用户购卖商品,根据商品找用户,根据用户找商品
上图为实际表关系
apps/goods/model.py
from apps.user.model import User
from ext import db
class Goods(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
gname = db.Column(db.String(100), nullable=False)
price = db.Column(db.Float, nullable=False)
# back reference
users = db.relationship('User', backref='goodslist', secondary='user_goods')
# relationship 定义的字段不会填入数据库,是给view和template定义的
# users和User表建立联系但是没有外键,所以需要 secondary适用于多对多情况
# Goods可以通过 Goods.users.xxx 查users表 反向:users.googlist.xxx查Goods表
# relationship 定义在Goods表或User表都可以,只是需要改值
# 中间表 添加表不需要执行 python app.py db init 此命令尽在没有migrations文件夹时需执行,添加字段也是migrate即可
class User_goods(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
goods_id = db.Column(db.Integer, db.ForeignKey(Goods.id), nullable=False)
num = db.Column(db.Integer, default=0)
apps/user/model.py
from datetime import datetime
from ext import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), nullable=False)
password = db.Column(db.String(64), nullable=False)
phone = db.Column(db.String(11), unique=True)
email = db.Column(db.String(25))
icon = db.Column(db.String(40))
isdelete = db.Column(db.Boolean, default=False)
rdatetime = db.Column(db.DateTime, default=datetime.now)
#或者在此添加 #goods=db.relationship('Goods',backref='userlist',secondary='user_goods')
apps/goods/view.py
from flask import Blueprint, render_template, request
from apps.goods.model import Goods, User_goods
from apps.user.model import User
from ext import db
goods_bp = Blueprint('goods', __name__)
@goods_bp.route('/')
def goods_index():
users_list = User.query.all()
goods_list = Goods.query.all()
return render_template('goods/show.html', users_list=users_list, goods_list=goods_list)
@goods_bp.route('/buy')
def goods_buy():
ug = User_goods()
# 先创建 User_goods 的实例化对象
ug.user_id = request.args.get('uid')
ug.goods_id = request.args.get('gid')
db.session.add(ug)
# 添加 ug 对象到缓存中
db.session.commit()
return 'add ok'
# 根据商品找用户
@goods_bp.route('/finduser', endpoint='finduser')
def find_user():
goods_id = request.args.get('gid')
goods = Goods.query.get(goods_id)
return render_template('goods/finduser.html', goods=goods)
# 根据用户找商品
@goods_bp.route('/findgoods', endpoint='findgoods')
def find_goods():
user_id = request.args.get('uid')
users = User.query.get(user_id)
return render_template('goods/findgoods.html', users=users)
templates/goods/show.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>show</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
#goods_table tr td{
width: 100px;
background-color: cornflowerblue;
}
</style>
</head>
<body>
<div>
<form>
<p>
{#展示用户#}
<select name="uid" >
<option value="0">please select user</option>
{% for users in users_list %}
<option value="{{ users.id }}">{{ users.username }}</option>
{% endfor %}
</select>
</p>
{#展示商品#}
<table border="1px solid" cellspacing="0" id="goods_table" style="text-align: center">
<tr>
<th>index</th>
<th>goods</th>
<th>price</th>
<th>action</th>
</tr>
{% for goods in goods_list %}
<tr>
<td>{{ loop.index }}</td>
<td><a href="{{ url_for('goods.finduser') }}?gid={{ goods.id }}">{{ goods.gname }}</a></td>
{# 建立超链接 #}
<td>{{ goods.price }}</td>
<td><input type="button" class="btngoods" value="buy" tag="{{ goods.id }}"></td>
{# 通过tag标签把goods.id值传给view #}
</tr>
{% endfor %}
</table>
</form>
</div>
<script>
$('.btngoods').click(function () {
goods_id = $(this).attr('tag');
user_id = $("select[name='uid']").val();
{# css选择器 选择具有值为uid的name属性的select标签 注意引号 #}
location.href='/buy?uid='+user_id+'&'+'gid='+goods_id;
})
</script>
</body>
</html>
templates/goods/finduser.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>finduser</title>
</head>
<body>
<div>
<h3>{{ goods.gname }}</h3>
<br>
{% for user in goods.users %}
<p>
<a href="{{ url_for('goods.findgoods') }}?uid={{ user.id }}">{{ user.username }}</a>
</p>
{% endfor %}
</div>
</body>
</html>
templates/goods/findgoods.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>findgoods</title>
</head>
<body>
<div>
<h3>{{ users.username }}</h3>
</div>
{% for goods in users.goodslist %}
<p>{{ goods.gname }}------{{ goods.price }}</p>
{% endfor %}
</body>
</html>
app.py
from apps.user.model import User
from apps.article.model import Article
from apps.goods.model import *
# 一定要将表对象在此引用否则无法在数据库生成表
多对多实例2:用户,文章,评论
from datetime import datetime
from apps.user.model import User
from ext import db
class Atype(db.Model):
__tablename__ = 'A_type'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
type_name = db.Column(db.String(25), nullable=False)
articles = db.relationship('Article', backref='typename')
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
pdatetime = db.Column(db.DateTime, default=datetime.now)
click_num = db.Column(db.Integer, default=0)
save_num = db.Column(db.Integer, default=0)
love_num = db.Column(db.Integer, default=0)
type_id = db.Column(db.Integer, db.ForeignKey('A_type.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
comments = db.relationship('Comment', backref='articles')
class Comment(db.Model):
# 自定义表名
__tablename__ = 'comment'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
comment = db.Column(db.String(255), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
article_id = db.Column(db.Integer, db.ForeignKey('article.id'))
# 注意 db.ForeignKey() 格式
cdatetime = db.Column(db.DateTime, default=datetime.now)
def __str__(self):
return self.comment
注意:用pycharm查看时 atype 没有连起来(可能没成功建立起外键关系),试了很多次都是这样
ELSE
补充蓝图
#apps/user/__init__.py
user_bp = Blueprint('user', __name__, url_prefix='/user')
@user_bp.route('/', endpoint='index')
def index():
return 'index'
#添加url_prefix后
Map([<Rule '/user/' (HEAD, OPTIONS, GET) -> user.index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
# url_prefix='/user' 必须是加 /
ValueError: urls must start with a leading slash
数据加密
除了 hashlib 库,Flask 自带了hash加盐加密方法
加密:generate_password_hash
from werkzeug.security import generate_password_hash
#generate_password_hash(password,method='pbkdf2:sha256',salt_length=*)
user.password = generate_password_hash(password)
# 数据格式:method$salt$hash
注意:加密后长度是变化的,所以数据库 password 字段要注意长度
Flask 由于封装问题修改数据字段长度在进行 migrate 会显示 not update,Flask 只能是添加删除数据库字段可会显示有更新,可以通过 pycharm 更改
Django 都可以
密码匹配:check_password_hash
@user_bp.route('/login', methods=['GET', 'POST'], endpoint='login')
def user_login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
users = User.query.filter(User.username == username).all()
for user in users:
flag = check_password_hash(user.password, password)
# flask 封装检查password函数,返回值bool类型,匹配为Ture,否则为False
#user.password 为加密过的密码,password 为未加密密码
if flag:
return jsonify(msg='登录成功', code=200)
else:
return jsonify(msg='登录失败', code=400)
else:
return render_template('user/login.html')
会话机制
记录用户登陆状态
HTTP是无状态协议
Cookie,Session
Cookie
保存
# 通过 response 对象保存
response = redirect(xxx)
response = render_template(xxx)
response = Response()
response = make_response(xxx)
response = jsonify(xxx)
# 通过对象调用方法
response.set_cookie(key,value,max_age)
set_cookie(属性)
#属性
name cookie的名称
value cookie的值
expire 过期时间
path 有效路径
domain 域名
secure https专用 true为安全传输
httponly 仅通过http协议访问,不能通过js
#例:
@user_bp.route('/login', methods=['GET', 'POST'], endpoint='login')
def user_login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
users = User.query.filter(User.username == username).all()
for user in users:
flag = check_password_hash(user.password, password)
# flask 封装检查password函数,匹配为Ture,否则为False
if flag:
response = redirect(url_for('user.index'))
# 需要先创建一个 response 对象
response.set_cookie(key='username', value=username, max_age=1800)
return response
else:
return render_template('user/login.html', msg='登陆失败')
else:
return render_template('user/login.html')
获取
# 通过 request 对象获取
request.form.get()
request.args.get
cookie也在 request 对象中
request.cookies.get(key) ---> value
#例:
@user_bp.route('/', endpoint='index')
def index():
username = request.cookies.get('username')
return render_template('user/index.html', username=username)
删除
# 通过 response 对象删除
response = redirect(xxx)
response = render_template(xxx)
response = Response()
response = make_response(xxx)
response = jsonify(xxx)
# 通过对象调用方法
response.delete_cookie(key)
#例:
@user_bp.route('/logout',endpoint='logout')
def user_logout():
response=redirect(url_for('user.login'))
response.delete_cookie('username')
return response
Session
在服务器保存,字典类型
cookie,session结合使用,cookie存储session_id,具体数据存储在session
需要设置 setting.py
SECRET_KEY = 'xxxxx'
xx这个值自己定义即可,目的是用于 sessionid 的加密
#例:settings.py
class Config:
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@127.0.0.1:3306/flask'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True
SECRET_KEY='fdsajfidposajgiopds'
设置:
若果要使用session,需要直接导入:
from flask import session
session[key]=value
会将key=value保存到session内存空间中
#例
@user_bp.route('/login', methods=['GET', 'POST'], endpoint='login')
def user_login():
......
session['username'] = username
return redirect(url_for('user.index'))
......
获取
value = session[key] 或 value = session.get(key)
#例:
@user_bp.route('/', endpoint='index')
def index():
username = session['username']
return render_template('user/index.html', username=username)
清除
session.clear() 删除session内存空间和删除cookie,(常用)
del session[key] 只会删除session中的这个键值对,不会删除session空间和cookie
# 例
@user_bp.route('/logout', endpoint='logout')
def user_logout():
session.clear()
return redirect(url_for('user.index'))
手机验证码
云短信服务 SMS(Short Message Service,SMS)
可以找各家提供的免费赠送的部分,有些是产生验证码返回给后台,有的是后台产生验证码发送给 SMS,再发送给前端
网易开发文档:support.dun.163.com/documents/2…
查看凭证
签名需要实名认证审核