1. Model
Create a blog post model and establish a foreign key association with 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, = True index, default = DateTime.UtcNow) the author_id = db.Column (db.INTEGER, db.ForeignKey ( ' users.id ' )) # foreign key body_html = db.Column (db.Text) # converted body Field, HTML format, you can directly call def __repr__ (self): return self.body # in the templateUser model class User (UserMixin, db.Model): # to add a User attribute posts, add an author attributes to the Post, dynamic ban to run automatically posts = db.relationship ( ' Post ' , backref = ' author ' , lazy = ' dynamic ' )
2. Routing
Add a route to display the post on the home page
@ 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 is provided by flask-login, and like all contexts, db.session.add (post) db.session.commit is implemented through the proxy object in the thread () return redirect (url_for ( ' .index' )) # Posts = Post.query.order_by (Post.timestamp.desc ()). All () # add page navigation Page request.args.get = ( ' Page ' ,. 1, type = int) # request rendered obtained from request.args pages, the default 1 # data for display in a page, put all () is converted into paginate flask_SQLALchemy provided () method # first parameter page is required, default per_page 20, error_out: If the number of pages exceeds the range, an empty list is returned. # The return value of the paginate () method is an object of the Paginate class. This class is defined in flask_SQLALchemy and is used to generate pagination links in the template 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. View
{% 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. Page navigation
The return value of the paginate () method is a Paginate class object. This class is defined in flask_SQLALchemy and is used to generate pagination links in the template.
Paginate class object properties:
items: records on the current page
query: paginated source query
page: current page number
prev_num: number of pages on the previous page
next_num
has_next: There is a next page for joining, return True
has_prev
pages: total number of pages
per_page: the number of records displayed per page
total: the number of records returned by the query
Paginate class object method:
iter_pages (): an iterator that returns a list of pages displayed in the page navigation
prev (): the paging object of the previous page
next()
Implement page navigation in the form of jinja2 macro:
{% 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. Use Markdown and Flask-PageDown to support rich text articles
The Flask-PageDown extension defines a PageDownField class, which is consistent with the TextAreaField interface in WTForms
(1) Initialization
from flask_pagedown import PageDown pagedown = PageDown() pagedown.init_app(app)
(2) Modify the form
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 preview
Markdown previews are generated using the PageDown library. Flask-PageDown simplifies this process and provides a template macro to load required files from the CDN
{% block scripts% } {{super ()}} {{moment.include_moment ()}} { # Markdown article preview is generated using the PageDown library, the following macro is loaded from the CDN, to be networked #} {{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. Process rich text on the server
# 文章 模型 class Post (db.Model): __tablename__ = ' posts ' id = db.Column (db.INTEGER, primary_key = True) body = db.Column (db.TEXT) timestamp = db.Column (db.DATETIME, = True index, default = DateTime.UtcNow) the author_id = db.Column (db.INTEGER, db.ForeignKey ( ' users.id ' )) # foreign key body_html = db.Column (db.Text) # converted body Field, HTML format, you can directly call def __repr__ (self): return self.body @staticmethod in the template # On_changed_body () registered in the body field, is the listeners SQLALchemy 'set' event, as long as the body field set a new value, it will automatically call the function # This function is the body of the text field rendering into html format and save In body_html, complete the conversion of markdown text to HTML def on_changed_body (target, value, oldvalue, initiator): allowed_tags = [ ' a ' , ' abbr ' , ' b ' , ' blockquote ' , ' code ' , ' em ' , ' i ' , ' li ' , 'the' , ' ol ' , ' pre ' , ' strong ' , ' h1 ' , ' h2 ' , ' h3 ' , ' p ' , ' acronym ' ] # 1markdown () Convert markdown text to html # 2bleach.clean () Delete tags that are not in allowed_tags # 3bleach.linkify () converts plain text URLs to <a> </a> links, because markdown officially does not provide support for automatic link generation target.body_html = bleach.linkify (bleach.clean( markdown(value, output_format='html'), tags=allowed_tags, strip=True ))