11章 博客文章
Posted cc-world
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11章 博客文章相关的知识,希望对你有一定的参考价值。
1、模型
创建博客文章模型,与User建立外键关联
# 文章模型 class Post(db.Model): __tablename__ = ‘posts‘ id = db.Column(db.INTEGER, primary_key=True) body = db.Column(db.TEXT) timestamp = db.Column(db.DATETIME, index=True, default=datetime.utcnow) author_id = db.Column(db.INTEGER, db.ForeignKey(‘users.id‘))#外键关联 body_html = db.Column(db.TEXT)#转换后的body字段,HTML格式,在模板中可以直接调用 def __repr__(self): return self.body # 用户模型 class User(UserMixin, db.Model): # 给User添加一个posts属性,给Post添加一个author属性, dynamic禁止自动执行查询 posts = db.relationship(‘Post‘, backref=‘author‘, lazy=‘dynamic‘)
2、路由
在首页添加显示post的路由
@main.route(‘/‘, methods=[‘GET‘, ‘POST‘]) def index(): form = PostForm() if current_user.can(Permission.WRITE_ARTICLES) and form.validate_on_submit(): post = Post(body=form.body.data, author=current_user._get_current_object())#current_user由flask-login提供,和所有上下文一样,通过线程内的代理对象实现 db.session.add(post) db.session.commit() return redirect(url_for(‘.index‘)) # posts = Post.query.order_by(Post.timestamp.desc()).all() # 添加分页导航 page = request.args.get(‘page‘, 1, type=int)#请求渲染的页数从request.args中获取,默认1 #为了显示某页中的数据,要把all()转换成flask_SQLALchemy提供的paginate()方法 #第一个参数page是必须的,per_page默认20,error_out:页数超过范围是返回一个空列表 #paginate()方法的返回值是一个Paginate类对象,这个类在flask_SQLALchemy中定义,用于在模板中生成分页链接 pagination = Post.query.order_by(Post.timestamp.desc()).paginate( page, per_page=current_app.config[‘FLASK_POSTS_PER_PAGE‘],error_out=False ) posts = pagination.items return render_template(‘index.html‘, form=form, posts=posts, pagination=pagination)
3、视图
{% extends "base" %} {% import "bootstrap/wtf.html" as wtf %} {% import "_macros.html" as macros %} {% block title %}Flasky{% endblock %} {% block page_content %} <div class="page-header"> <h1>Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!</h1> </div> <div> {% if current_user.can(Permission.WRITE_ARTICLES) %} {{ wtf.quick_form(form) }} {% endif %} </div> {% include ‘_posts.html‘ %} {% if pagination %} <div class="pagination"> {{ macros.pagination_widget(pagination, ‘.index‘) }} </div> {% endif %} {% endblock %}
4、分页导航
paginate()方法的返回值是一个Paginate类对象,这个类在flask_SQLALchemy中定义,用于在模板中生成分页链接。
Paginate类对象属性:
items:当前页面中的记录
query:分页的源查询
page:当前页数
prev_num:上一页的页数
next_num
has_next:入伙有下一页,返回True
has_prev
pages:总页数
per_page:每页显示的记录数量
total:查询返回的记录数量
Paginate类对象方法:
iter_pages():一个迭代器,返回一个在分页导航中显示的页数列表
prev():上一页的分页对象
next()
以jinja2宏的形式实现分页导航:
{% macro pagination_widget(pagination, endpoint) %} <ul class="pagination"> <li {% if not pagination.has_prev %} class="disabled" {% endif %}> <a href=" {% if pagination.has_prev %} {{ url_for(endpoint,page=pagination.page-1, **kwargs) }} {% else %} # {% endif %}">«</a> </li> {% for p in pagination.iter_pages() %} {% if p %} {% if p == pagination.page %} <li class="active"> <a href="{{ url_for(endpoint, page=p, **kwargs) }}">{{ p }}</a> </li> {% else %} <li> <a href="{{ url_for(endpoint, page=p, **kwargs) }}">{{ p }}</a> </li> {% endif %} {% else %} <li class="disabled"><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.page+1, **kwargs) }} {% else %} # {% endif %}">»</a> </li> </ul> {% endmacro %}
5、使用Markdown和Flask-PageDown支持富文本文章
Flask-PageDown扩展定义了一个PageDownField类,这个类和WTForms中的TextAreaField接口一致
(1)初始化
from flask_pagedown import PageDown pagedown = PageDown() pagedown.init_app(app)
(2)修改表单
class PostForm(FlaskForm): # body = TextAreaField(‘what is your mind?‘, validators=[DataRequired()]) body = PageDownField(‘what is your mind?‘, validators=[DataRequired()])#启用MarkDown的文章表单 submit = SubmitField(‘Submit‘)
(3)Markdown预览
Markdown预览使用PageDown库生成,Flask-PageDown简化了这个过程,提供了一个模板宏,从CDN中加载所需文件
{% block scripts %} {{ super() }} {{ moment.include_moment() }} {#Markdown文章预览使用PageDown库生成,下面的宏是从CDN中加载,要联网#} {{ pagedown.include_pagedown() }} {% endblock %}
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/pagedown/1.0/Markdown.Converter.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/pagedown/1.0/Markdown.Sanitizer.min.js"></script>
6、在服务器上处理富文本
# 文章模型 class Post(db.Model): __tablename__ = ‘posts‘ id = db.Column(db.INTEGER, primary_key=True) body = db.Column(db.TEXT) timestamp = db.Column(db.DATETIME, index=True, default=datetime.utcnow) author_id = db.Column(db.INTEGER, db.ForeignKey(‘users.id‘))#外键关联 body_html = db.Column(db.TEXT)#转换后的body字段,HTML格式,在模板中可以直接调用 def __repr__(self): return self.body @staticmethod #on_changed_body()注册在body字段上,是SQLALchemy ‘set‘ 事件的监听程序,只要body字段设了新值,就会自动调用该函数 #该函数把body字段中的文本渲染成html格式,并保存在body_html中,完成markdown文本到html的转换 def on_changed_body(target, value, oldvalue, initiator): allowed_tags = [‘a‘, ‘abbr‘, ‘b‘, ‘blockquote‘, ‘code‘, ‘em‘, ‘i‘, ‘li‘, ‘ul‘, ‘ol‘, ‘pre‘, ‘strong‘, ‘h1‘, ‘h2‘, ‘h3‘, ‘p‘, ‘acronym‘] # 1markdown()把markdown文本转换成html # 2bleach.clean()删除不在allowed_tags中的标签 # 3bleach.linkify()把纯文本的url转换成<a></a>链接,因为markdown官方没有为自动生成链接提供支持 target.body_html = bleach.linkify(bleach.clean( markdown(value, output_format=‘html‘), tags=allowed_tags, strip=True ))
以上是关于11章 博客文章的主要内容,如果未能解决你的问题,请参考以下文章
错误记录Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | Android Studio 降级 )(代码片段
错误记录Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | Android Studio 降级 )(代码片段