A tutorial on the implementation of paging in the Python flask framework _python

Source: Internet
Author: User
Tags commit python list

The submission of Blog posts

Let's start with the simple. There must be a form on the home page where a user submits a new post.

First we define a single domain form object (fileapp/forms.py):

Class Postform (Form):
  post = TextField (' post ', validators = [Required ()])

Next, we add this form to template (fileapp/templates/index.html):

<!--extend Base layout-->
{% extends ' base.html '%}
 
{% block content%}
 
 

So far nothing new, as you can see, we just added another form, just like we did last time.

Finally, the function attempts to link everything together and is extended to process the form (fileapp/views.py):

From the forms import LoginForm, EditForm, postform from
models import User, Role_user, Role_admin, Post
 
@app. Route (' /', methods = [' Get ', ' post ']
@app. Route ('/index ', methods = [' Get ', ' post '])
@login_required
def index ():
  form = Postform ()
  if Form.validate_on_submit ():
    post = post (BODY = form.post.data, timestamp = Datetime.utcnow (), author = g.user)
    Db.session.add (POST)
    db.session.commit ()
    Flash (' Your post is now live! ')
    Return Redirect (Url_for (' index '))
  posts = [
    {
      ' author ': {' nickname ': ' John '},
      ' body ': ' Beautiful Day in portland! '
    },
    {
      ' author ': {' nickname ': ' Susan '},
      ' body ': ' The Avengers movie is so cool! '
    }
  ]
  Return render_template (' index.html ',
    title = ' Home ',
    form = form,
    posts = posts)

Let's take a look at the changes in this feature:

    • We have imported post and Postform classes
    • We received the POST request for index and view from two paths, because that is how we receive the submitted request.
    • When we submit the form to the functional view, we will enter the new post record into the database. Then, just like you did before, access it through a regular GET request.
    • Templat will receive an extra piece of content-the form, so it will be submitted to the text field.

One last caveat before we go: Notice how we add a new POST request to the database:

Return Redirect (Url_for (' index '))

We can easily jump over the orientation, and allow it to jump to the template rendering part, and more efficient. Because all redirects are returned to the same feature view after they have been passed through a Web browser.

So why do you choose to redirect? Considering that when the user writes down a blog post request, it simply submits and clicks the Browser refresh button. What can the "Refresh" command do? The browser will resend the last published request as a result of a "Refresh" command. (Translator Note: Because of the limited level of personal, if you find the translation of the discrepancy between the original text and please correct me.) Thank you! )


If there is no redirection, then the final submission to the form is the POST request, so a "Refresh Action" will resubmit the form, which will cause the second submitted post record to be the same as the first time the database is written. Such behavior is not good.

With redirection, we can force the browser to make another request after the form is submitted, which captures the redirected page. This is a simple "get" request, so the "Refresh" action repeats the "get" request instead of submitting the form again.

This simple trick prevents users from accidentally refreshing the page after submitting a blog post request, resulting in repeated writes to the POST request.

Show blog POST request

Let's say something interesting here. We want to crawl the blog post request from the database and lose it.

If you recall some of the previous articles, we created many so-called "false" requests and showed them for a long time on the front page. These "false" objects are created as a Python list in an indexed view.

posts = [
    {'
      author ': {' nickname ': ' John '},
      ' body ': ' Beautiful Day in portland! '
    },
    {
      ' author ' : {' nickname ': ' Susan '},
      ' body ': ' The Avengers movie is so cool! '
    }
  ]

But in the last article, we created a query that allows us to get all the requests from the "people of concern," so we can replace the previous one with the following statement (fileapp/views.py):

Posts = G.user.followed_posts (). All ()

Then when you run this application, you will see the Bolg POST request that is crawled into the database.


The Followed_posts method of the user class returns a SQL query statement that captures the request we are interested in. In this query, Callingall () retrieves all the requests into a list, so we end up with this structure that resembles the "false" request we have been using so far. They were so alike that they did not even notice the template.

At this point you can play freely on this application. You can create multiple users, let them follow others, and then post a message to see how each user sees its Bolg POST request data stream.

Paging


Our program is becoming more and more presentable, but we face another problem. We show all the followed post on the front page. What happens if a user has a waking followed post? Or 1 million pieces? As we can imagine, it's very inefficient to crawl and process such a large list of objects.

We can display this large number of post groupings to display, or pagination.

Flask-sqlalchemy can be a good support for paging. For example, we can easily get the first 3 pieces of followed posts for a user by using the following methods:

Posts = G.user.followed_posts (). Paginate (1, 3, False). Items

The paging method can be invoked by any query object. It accepts 3 parameters:

    1. Page number, starting from 1
    2. Number of records displayed per page
    3. Error mark. If true, 404 error requests are automatically fed back to the client browser if a page request exceeding the record range is received. If False, an empty list is returned without displaying an error.

The return value of the paginate is a pagination object. The members of this object include a list of the records in the request page. Later, we'll explore some of the other useful things in the pagination object.

Now let's think about how to implement pagination in our view function. We can add some configuration information to our application first, including how many records we need to display per page (fileconfig.py):

# pagination
posts_per_page = 3

Using a global configuration file to change our application is a good idea, because we just need to go to a place where we can modify all the configurations.

In the final application, we will of course use a larger number than 3, but as a test, it is more efficient to use small numbers.

After that, let's see how URLs are judged to request different pages. We have previously known that Flask's routes can accept parameters, so we can add a suffix to the URL to indicate the page we want:

Http://localhost:5000/     <--page #1 (default)
http://localhost:5000/index  <--page #1 (default)
http://localhost:5000/index/1 <--page #1
http://localhost:5000/index/2 <--page #2

We can easily implement the format of the URLs and add new route to our view function (fileapp/views.py):


From config import posts_per_page
 
@app. Route ('/", methods = [' Get ', ' POST '])
@app. Route ('/index ', methods = [' Get ', ' post ']
@app. Route ('/index/<int:page> ', methods = [' Get ', ' post '])
@login_required
def Index (page = 1):
  form = Postform ()
  if Form.validate_on_submit ():
    post = post (BODY = Form.post.data, timestamp = Datetime.utcnow (), author = g.user)
    Db.session.add (POST)
    db.session.commit ()
    Flash (' Your Post is now live! ')
    Return Redirect (Url_for (' index '))
  posts = g.user.followed_posts (). Paginate (page, Posts_per_page, False). Items return
  render_template (' index.html ',
    title = ' Home ',
    form = form,
    posts = posts)

Our new route accepts the paging parameter and defines the parameter as an integer. We also need to add paging parameters to the index function and give them a default value, because two of the three route have no paging parameters, so these route must use the default values.


Now we've got a page number parameter that can be easily applied to our followed_post query, and there's a configuration constant we've defined before: Posts_per_page.

We can note how simple these changes are, and when we change a page, very few code will be affected. We try to write each part of the application without assuming that the other parts work, so that we can write modular and robust applications that make testing simpler and less error-prone or flawed.

Now you can try typing the page number in the URL in your browser's address bar. Make sure you have more than 3 pots, so you can see more than one page.

Page navigation

Now we need to add links so that our users can view the previous/next page, luckily, this feature is easy to implement, Flask-sqlalchemy has done most of the work for us.

We'll now change a small portion of the code to the view function. We modified in the Paginate method:

Posts = G.user.followed_posts (). Paginate (page, Posts_per_page, False). Items

We only keep the item members of the pagination object returned by the Paginate method. But this object contains some very useful information in it, so we change to preserve the pagination object (fileapp/views.py):

Posts = G.user.followed_posts (). Paginate (page, Posts_per_page, False)


To complete this change, we also need to modify the template (fileapp/templates/index.html):

<!--posts is a Paginate object-->
{% to post in posts.items%}
<p>
 {post.author.nickname}} say S: <b>{{post.body}}</b>
</p>
{% endfor%}


What we do is to apply the full set of pagination objects to our template. The paging object will use the following members:

    • Has_next: Returns True if there are more than one paging after this page
    • Has_prev: Returns True if there is more than one paging before this page
    • Next_num: Returns the page number of the next page
    • Prev_num: Returns the page number of the previous page

To apply these members, we can generate the following code (app/templates/index.html):

<!--posts is a Paginate object-->
{% to post in posts.items%}
<p>
 {post.author.nickname}} say S: <b>{{post.body}}</b>
</p>
{% endfor%}
{% if Posts.has_prev%}<a href= ' {{url_for ( ' index ', page = Posts.prev_num}} ' ><< newer posts</a>{% else%}<< newer posts{%%} |
{% if Posts.has_next%}<a href= ' {{url_for (' index ', page = posts.next_num)} ' >older posts >></a>{% Else %}older posts >>{% endif%}


Now we have two hyperlinks. The first is called "newer posts", which links to the previous page (remember, we are putting the latest post on top, so the post on the previous page is a newer post). Instead, "older posts" points to the next page.

When we are on the first page, we do not want to display the link to the previous page because it does not exist. We can easily detect it because the Posts.has_prev returns false. In this case, we can display only the text of the previous page without the link property. And so is the next page.


Implementing post Child Templates

Back to the article we added the Avatar picture, we used HTML to define a child template for a separate post. The reason is that we just need to create one at a time to use more than one place to avoid duplicate code. Now we're going to apply this template to our index page. As we do today, this is a very simple thing (app/templates/index.html):

<!--posts is a Paginate object-->
{% to post in posts.items%}
  {% include ' post.html '%}
{% endfor%}

Amazing, huh? We just replace the old rendering code with the handle template. Now we have a post page that contains the avatar image for the user.
User's personal page

We have completed the development of the index page. But we also introduced posts on the user's personal page, which lists only the post that the user has posted. Accordingly, the user individual page also needs to follow the design of the index to change.

Similar to the changes to the index page, here are a few things we should do:

Add a route that accepts page numbers
Adds a page number parameter to the view function with a default value of 1
Replace the post list with the appropriate database queries and paging code
Update a template with a paging object


The following is the updated view function (app/views.py):

@app. Route ('/user/<nickname> ')
@app. Route ('/user/<nickname>/<int:page> ')
@login_ Required
def user (nickname, page = 1):
  user = User.query.filter_by (nickname = nickname). A ()
  if user = No NE:
    Flash (' User ' + nickname + ' not found. ')
    Return Redirect (Url_for (' index '))
  posts = user.posts.paginate (page, Posts_per_page, False) return
  Render_ Template (' user.html ',
    user = user,
    posts = posts)

We notice that this function already contains a parameter (the user's nickname), so we add the page number to the second argument.

Changing the template is also fairly straightforward (app/templates/user.html):

<!--posts is a Paginate object-->
{% to post in posts.items%}
  {% include ' post.html '%}
{% endfor%}
{% If Posts.has_prev%}<a href= ' {{url_for (' user ', nickname = user.nickname, page = Posts.prev_num)}} ' ><< ; Newer posts</a>{% else%}<< newer posts{% endif%} |
{% if Posts.has_next%}<a href= ' {{url_for (' user ', nickname = user.nickname, page = Posts.next_num)}} ' >older >></a>{% Else%}older posts >>{% endif%}

Conclusion

Below I will pack and download the mini blog application code developed by this article to you:

Download: Microblog-0.9.zip.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.