zoukankan      html  css  js  c++  java
  • Flask 学习 十 博客文章

    提交和显示博客文章

    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;
    }
    View Code

    _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>
  • 相关阅读:
    make、make clean、make install、make uninstall、make dist、make distcheck和make distclean
    Eclipse开发C/C++ 安装配置
    没有文件扩展“.js”的脚本引擎问题解决
    Linux目录架构详解
    Linux查看硬件信息以及驱动设备的命令
    23种设计模式彩图
    ACE在Linux下编译安装
    void及void指针含义的深刻解析
    centos7.x设置nginx开机自启动
    Centos7防火墙关闭和启用iptables操作
  • 原文地址:https://www.cnblogs.com/Erick-L/p/6904431.html
Copyright © 2011-2022 走看看