# 博客项目
### 昨日内容完善
- 密码加密存储
```python
from werkzeug.security import generate_password_hash, check_password_hash
# 用户模型
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True)
password_hash = db.Column(db.String(128))
email = db.Column(db.String(32), unique=True)
confirmed = db.Column(db.Boolean, default=False)
@property
def password(self):
raise AttributeError('你想干啥,密码不可读')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
# 密码校验
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
```
- 带有效期的token
```python
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import SignatureExpired, BadSignature
@main.route('/generate/')
def generate():
s = Serializer(current_app.config['SECRET_KEY'], expires_in=10)
token = s.dumps({'id': 250})
return token
@main.route('/check/<token>/')
def check(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired as e:
return 'token已过期'
except BadSignature as e:
return '无效token'
return str(data['id'])
```
### 用户信息管理
- 用户详情
- 练习:修改密码、修改邮箱、找回密码
- 修改头像
- 添加点击跳转的逻辑
- 添加flask-uploads扩展
- 设计上传文件表单,创建并渲染
- 校验处理上传文件,保存图片
- 生成随机文件名
- 生成缩略图
- 将新的头像保存到数据库中
- 删除原来的头像文件
### 博客管理
- 发表博客
- 设计博客模型,并且完成迁移
- 设计发表博客表单类,并创建渲染
- 提交后的校验(创建博客对象,保存到数据库)
- 列表显示发表的博客(查询数据,分配到模板,然后遍历渲染)
- 分页显示发表的博客(分页查询)
```
方法:paginate,分页查询
参数:
page:当前的页码
per_page:每页的条数
error_out:当查询出错时是否报错
返回值:
Pagination:分页对象,包含了所有的分页信息
Pagination:
属性:
page:当前页码
per_page:每页的条数,默认为20条
pages:总页数
total:总条数
prev_num:上一页的页码
next_num:下一页的页码
has_prev:是否有上一页
has_next:是否有下一页
items:当前页的数据
方法:
iter_pages:返回一个迭代器,在分页导航条上显示的页码列表,显示不完的时返回None
prev:上一页的分页对象
next:下一页的分页对象
```
- 封装分页显示的宏
```html
{% macro show_pagination(pagination, endpoint) %}
<nav aria-label="Page navigation">
<ul class="pagination">
{# 上一页 #}
<li {% if not pagination.has_prev %}class="disabled"{% endif %}>
<a href="{% if pagination.has_prev %}{{ url_for(endpoint, page=pagination.prev_num, **kwargs) }}{% else %}#{% endif %}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{# 中间页码 #}
{% for p in pagination.iter_pages() %}
{% if p %}
<li {% if pagination.page == p %}class="active"{% endif %}><a href="{{ url_for(endpoint, page=p, **kwargs) }}">{{ p }}</a></li>
{% else %}
<li><a href="#">…</a></li>
{% endif %}
{% endfor %}
{# 下一页 #}
<li {% if not pagination.has_next %}class="disabled"{% endif %}>
<a href="{% if pagination.has_next %}{{ url_for(endpoint, page=pagination.next_num, **kwargs) }}{% else %}#{% endif %}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
{% endmacro %}
```
### 练习:
- 点击用户头像或用户名,跳转到该用户发表的所有博客展示页码,显示不完分页展示
- 点击博客内容跳转到该篇博客的详情页面(博客信息、回复表单、所有回复(可以分页))
- 将导航条上的'板块一'改为'我的发表'