提交和显示博客文章

app/models.py 文章模型

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'))
class User(UserMixin,db.Model):
posts = db.relationship('Post',backref = 'author',lazy='dynamic')

app/main/form.py 博客文章表单

class PostForm(FlaskForm):
body = TextAreaField('你在想什么?',validators=[DataRequired()])
submit = SubmitField('提交')

app/main/views.py  处理博客文章的首页路由 把以前发布的文章列表传给魔板

# 使用蓝本自定义路由
@main.route('/', methods=['get', 'post'])
def index():
  form = PostForm()
# 检查用户是否有写文章的权限并检查是否可以通过验证
if current_user.can(Permission.WRITE_ARTICLES) and form.validate_on_submit():
# current_user._get_current_object() 新文章对象,内含真正的用户对象
post = Post(body = form.body.data,author=current_user._get_current_object())
db.session.add(post)
return redirect(url_for('.index'))
posts = Post.query.order_by(Post.timestamp.desc()).all()
return render_template('index.html',form=form,posts=posts)

index.html 显示博客文章首页模板

{% block page_content %}
<div class="page-header">
<h1>Hello,
{% if current_user.is_authenticated %}{{ current_user.username }}{% else %}访客{% endif %}!</h1>
</div>
<div>
{% if current_user.can(Permission.WRITE_ARTICLES) %} # 如果没有对应权限,则不会显示文章列表
{{ wtf.quick_form(form) }}
{% endif %}
</div>
{% include '_posts.html' %} # 引导局部模板

styles.css

.profile-thumbnail {
position: absolute;
}
.profile-header {
min-height: 260px;
margin-left: 280px;
}
ul.posts {
list-style-type: none;
padding: 0px;
margin: 16px 0px 0px 0px;
border-top: 1px solid #e0e0e0;
}
ul.posts li.post {
padding: 8px;
border-bottom: 1px solid #e0e0e0;
}
ul.posts li.post:hover {
background-color: #f0f0f0;
}
div.post-date {
float: right;
}
div.post-author {
font-weight: bold;
}
div.post-thumbnail {
position: absolute;
}
div.post-content {
margin-left: 48px;
min-height: 48px;
}

_post.html  降被以{% include '_posts.html' %}引入

<ul class="posts">
{% for post in posts %}
<li class="post">
<div class="profile-thumbnail">
<a href="{{ url_for('.user',username=post.author.username) }}"><img class="img-rounded profile-thumbnail" src="{{ post.author.gravatar(size=40) }}"></a>
</div>
<div class="post-content">
<div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
<div class="post-author">
<a href="{{ url_for('.user',username=post.author.username) }}">{{ post.author.username }}</a>
</div>
<div class="post-body">{{ post.body }}</div>
</div>
</li>
{% endfor %}
</ul>

在资料页显示博客文章

main/views.py

@main.route('/user/<username>')
def user(username):
user = User.query.filter_by(username=username).first()
#user = User.query.filter_by(username=username).first_or_404()
if user is None:
abort(404)
posts =user.posts.order_by(Post.timestamp.desc()).all()
return render_template('user.html',user=user,posts=posts)

user.html 显示有博客文章的资料页模板

<h3>发表自 {{ user.username }}</h3>
{% include '_posts.html' %}

分页显示长博客文章列表

创建虚拟博客文章数据

pip install forgerypy

整合目录

dev.txt

-r common.txt
Forgerypy==0.1

app/models.py 生成虚拟用户和博客文章

class User(UserMixin,db.Model):
@staticmethod
def generate_fake(count = 100):
from sqlalchemy.exc import IntegrityError
from random import seed
import forgery_py seed()
for i in range(count):
u = User(email=forgery_py.internet.email_address(),
username = forgery_py.internet.user_name(True),
password = forgery_py.lorem_ipsum.word(),
confirmed=True,
name = forgery_py.name.full_name(),
location=forgery_py.address.city(),
about_me=forgery_py.lorem_ipsum.sentence(),
member_since=forgery_py.date.date(True))
db.session.add(u)
try:
db.session.commit()
# 邮箱和用户名如果随机出重复的数据,则回滚到之前的对话,并不会写入到数据库
except IntegrityError:
db.session.rollback()
class Post(db.Model):
@staticmethod
def generate_fake(count=100):
from random import seed,randint
import forgery_py seed()
user_count=User.query.count()
for i in range(count):
# 为每篇文章随机制定一个用户,offset 会跳过参数中制定的记录数量,设定一个随机的偏移值
u = User.query.offset(randint(0,user_count -1)).first()
p=Post(body=forgery_py.lorem_ipsum.sentences(randint(1,3)),
timestamp=forgery_py.date.date(True),
author=u,)
db.session.add(p)
db.session.commit()

manage.py 绑定post类

from app.models import User,Role,Post

def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role,Post=Post)

生成虚拟用户和文章

python manage.py shell

User.generate_fake(100)
Post.generate_fake(100)

在页面只渲染数据

app/main/views.py 分页显示博客文章列表

@main.route('/', methods=['get', 'post'])
def index():
form = PostForm()
# 检查用户是否有写文章的权限并检查是否可以通过验证
if current_user.can(Permission.WRITE_ARTICLES) and form.validate_on_submit():
# current_user._get_current_object() 新文章对象,内含真正的用户对象
post = Post(body = form.body.data,author=current_user._get_current_object())
db.session.add(post)
return redirect(url_for('.index'))
#posts = Post.query.order_by(Post.timestamp.desc()).all()
# 分页显示博客文章列表
# 页数请求从查询字符串中获取,如果没有制定默认为第一页
page = request.args.get('page',1,type=int)
# 显示分页需要用到sqlachemy提供的paginate方法
pagination=Post.query.order_by(Post.timestamp.desc()).paginate(page,per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],error_out=False)
# 显示当前页面的记录
posts = pagination.items
return render_template('index.html',form=form,posts=posts,pagination=pagination)

添加分页导航

Flask-SQLAlchemy 分页对象的属性简介

Flask-SQLAlchemy 分页对象的方法简介

构建分页导航

templates/_macros.html 分页模版宏

{% 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 %}">
&laquo;
</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="#">&hellip;</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 %}">
&raquo;
</a>
</li>
</ul>
{% endmacro %}

app/templates/index.html 在博客文章列表下面添加分页导航

{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% import '_macros.html' as macros %}
。。。
<div class="pagination">
{{ macros.pagination_widget(pagination,'.index') }}
</div>

使用Markdown和Flask-PageDown支持富文本文章

Pagedown:使用JavaScript实现的客户端Markdown到Html的转换程序

Flask-PageDown: 为Flask包装的Pagedown 把Pagedown集成到Form表单中

Markdown: 使用python实现的从服务器端的MarkDOWN到HTML的转换程序

Bleach:使用python实现的HTML清理器

pip install flask-pagedown markdown bleach

使用flask-pagedown

app/__init__.py 初始化flask-pagedown

from flask_pagedown import PageDown

pagedown = PageDown()

pagedown.init_app(app)

flask-pagedown 扩展定义的PageDownField类和WTForm中的TextAreaField接口一致

把多行文本空间转换成Markdown富文本编辑器,需要修改body字段

app/main/forms.py 启用markdown的文章表单

from flask_pagedown.fields import PageDownField

class PostForm(FlaskForm):
body = PageDownField('你在想什么?',validators=[DataRequired()])
submit = SubmitField('提交')

markdown的预览生成直接调用flask-pagedown提供的模板宏即可

app/index.html flask-pagedown 模板声明

{% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
{% endblock %}

在服务器上处理富文本

app/models.py 在post模型中处理Markdown文本

from markdown import markdown
import bleach class Post(db.Model):
body_html =db.Column(db.Text)
@staticmethod
def on_changed_body(target,value,oldvalue,initiator):
allowed_tags=['a','abbr','acronym','b','blockquote','code','em','i','li','ol','pre','strong','ul','h1','h2','h3','p']
     # 把body字段中的值渲染成HTML格式,结果保存到body_html中,clean函数删除不在名单里的标签,linkify函数把纯文本中的url转换成适当的<a>标签
     target.body_html=bleach.linkify(bleach.clean(markdown(value,output_format='html'),tags=allowed_tags,strip=True))

# 把on_changed_body 注册在body字段上,用set监听程序,只要body字段更新,函数就会被调用
db.event.listen(Post.body,'set',Post.on_changed_body)

如果post.body_html字段存在,就把post.body换成post.body_html

templates/_posts.html 在模板中使用文章内容的HTML格式

<div class="post-body">
{% if post.body_html %}
{{ post.body_html | safe }}
{% else %}
{{ post.body }}
{% endif %}
</div>

博客文章固定链接

app/main/views.py 文章的固定链接

# 文章固定链接
@main.route('/post/<int:id>')
def post(id):
post=Post.query.get_or_404(id)
return render_template('post.html',post=[post])

templates/_posts.html

<div class="post-footer">
<a href="{{ url_for('.post',id=post.id) }}">
<span class="label label-default">永久链接</span>
</a>
</div>

在post.html中引入固定链接模板

{% extends "base.html" %}
{% block title %}Flasky - 文章{% endblock %} {% block page_content %}
{% include '_posts.html' %}
{% endblock %}

博客文章编辑器

templates/edit_post.html 编辑博客文章的模板

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky - 编辑文章{% endblock %} {% block page_content %}
<div class="page-header">
<h1>编辑你的文章</h1>
</div>
<div>
{{ wtf.quick_form(form) }}
</div>
{% endblock %} {% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
{% endblock %}

app/main/views.py 编辑博客文章的路由

# 编辑博客路由
@main.route('/edit/<int:id>',methods=['get','post'])
@login_required
def edit(id):
post=Post.query.get_or_404(id)
if current_user != post.author and not current_user.can(Permission.ADMINISTER):
abort(403)
form = PostForm()
if form.validate_on_submit():
post.body=form.body.data
db.session.add(post)
flash('文章已经更新')
return redirect(url_for('.post',id=post.id))
form.body.data=post.body return render_template('edit_post.html',form=form)

templates/_posts.html 编辑博客文章的链接

<div class="post-footer">
<a href="{{ url_for('.post',id=post.id) }}">
<span class="label label-default">永久链接</span>
</a>
{% if current_user ==post.author %}
<a href="{{ url_for('.edit',id=post.id) }}"><span class="label label-primary">编辑</span></a>
{% elif current_user.is_administrator() %}
<a href="{{ url_for('.edit',id=post.id) }}">
<span class="label label-danger">编辑[管理员]</span>
</a>
{% endif %}
</div>

Flask 学习 十 博客文章的更多相关文章

  1. [Python学习] 简单网络爬虫抓取博客文章及思想介绍

            前面一直强调Python运用到网络爬虫方面很有效,这篇文章也是结合学习的Python视频知识及我研究生数据挖掘方向的知识.从而简介下Python是怎样爬去网络数据的,文章知识很easy ...

  2. Java EE 学习(9):IDEA + maven + spring 搭建 web(5)- 博客文章管理

    转载:Gaussic(一个致力于AI研究却不得不兼顾项目的研究生) . 注:在阅读本文前,请先阅读: Java EE 学习(5):IDEA + maven + spring 搭建 web(1) Jav ...

  3. 用 Flask 来写个轻博客 (22) — 实现博客文章的添加和编辑页面

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 新建表单 新建视图函数 新建模板 在博客文章页面添加 New 和 Ed ...

  4. SQL Sever 博客文章目录(2016-07-06更新)

    SQL Server方面的博客文章也陆陆续续的写了不少了,顺便也将这些知识点整理.归纳一下下.方便自己和他人查看. MS SQL 数据类型 三大数据库对比研究系列--数据类型 MS SQL 表和视图 ...

  5. 年度十佳 DevOps 博客文章(后篇)

    如果说 15 年你还没有将 DevOps 真正应用起来,16 年再不实践也未免太落伍了.在上篇文章中我们了解到 15 年十佳 DevOps 博客文章的第 6-10 名,有没有哪一篇抓住了您的眼球,让您 ...

  6. 年度十佳 DevOps 博客文章(前篇)

    如果说 15 年你还没有将 DevOps 真正应用起来,16 年再不实践也未免太落伍了.国内 ITOM 领军企业 OneAPM 工程师为您翻译整理了,2015 年十佳 DevOps 文章,究竟是不是深 ...

  7. Android学习之 博客专栏 与 资料

    android | Android Developers Android学习系列 - 谦虚的天下 - 博客园 android基础 - 生如夏花之灿烂 - 博客园 Android开发 - 皓月繁星 - ...

  8. 一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等

    本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用.各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流.学习.提高 ...

  9. Ruby:多线程队列(Queue)下载博客文章到本地

    Ruby:多线程下载博客文章到本地的完整代码 #encoding:utf-8 require 'net/http' require 'thread' require 'open-uri' requir ...

随机推荐

  1. [Luogu2991][USACO10OPEN]水滑梯Water Slides

    题面戳我 题面描述 受到秘鲁的马丘比丘的新式水上乐园的启发,Farmer John决定也为奶牛们建一个水上乐园.当然,它最大的亮点就是新奇巨大的水上冲浪. 超级轨道包含 E (1 <= E &l ...

  2. [APIO2015]八邻旁之桥

    题面在这里 sol 这是一个\(Splay\)的题解 首先,如果一个人的家和办公室在同一侧,我们可以直接预处理; 如果不在同一侧,也可以加上1(当然要过桥啦) 当k==1时 我们设桥的位置为\(pos ...

  3. Frogger POJ - 2253

    题意 给你n个点,1为起点,2为终点,要求所有1到2所有路径中每条路径上最大值的最小值. 思路 不想打最短路 跑一边最小生成树,再扫一遍1到2的路径,取最大值即可 注意g++要用%f输出!!! 常数巨 ...

  4. ls-grep-find组合命令解决企业问题实战

    ls -l|grep “^d”  以d开头 ls -lF  给不同文件类型加不同标志 ls -lF|grep  “/$” 以/结尾 ls -lF|grep  / find ./ -type d fin ...

  5. 斐波拉契数列(Fibonacci)--用生成器生成数列

    斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为"兔子数列&qu ...

  6. sqlserver存储过程及临时表在统计中的应用

    use ResourceShare --统计使用情况 alter PROCEDURE StaSheryUse @start datetime, @end datetime, @orgId int AS ...

  7. 视频直播技术(七):Ijkplayer切换网络时停止播放的问题处理

    问题起因: 在进行ijkplayer播放器的测试时,发现ijkplayer播放器在切换网络时出现直播画面停止的问题. 问题分析: 抓取日志发现:tv.danmaku.ijk.media.player. ...

  8. jemeter 实现接口自动化回归测试

    jemeter做接口自动化测试的优点: 1.首先我认为最重要的是不需要编程基础,很多一直想做接口测试但一直徘徊在门边的原因可能就是不想写代码 2.可以更快的上手,能让测试人员更好的理解什么是接口测试, ...

  9. wpf动态增加删除控件

    我在xaml中定义了一个名字为morepictureWrapPan为WrapPanel,然后将控件添加在此WrapPanel中.由于要实现控件的删除功能,所以增加的textbox和button的名字都 ...

  10. 笔记:Maven 插件配置 - maven-jar-plugin

    在项目中内部重用某个模块的测试代码很常见的需求,可能某个底层模块的测试代码中包含一些常用的测试工具类,或者一些高质量的测试基类供继承,这个时候Maven用户就需要通过配置此插件将测试类打包,插件配置代 ...