Tutorial on implementing paging in the flask framework of Python

Source: Internet
Author: User
submission of Blog posts

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

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

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

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

 
  {% extends "base.html"%} {% block content%}

Hi, {{g.user.nickname}}!

{% for post in posts%}

{{Post.body}}

{% ENDFOR%} {% Endblock%}

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

Finally, the feature tries to link everything together and is expanded to handle this form (fileapp/views.py):

From forms import LoginForm, editform, Postformfrom models import User, Role_user, Role_admin, Post @app. Route ('/', method s = [' get ', ' post ']) @app. Route ('/index ', methods = [' Get ', ' post ']) @login_requireddef 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 the post and Postform classes
    • We received a POST request from index and view from two paths, because that is how we receive the submitted request.
    • After we submit the form to the feature view, we will enter the new post record into the database. Then, as you did before, access it through a regular GET request.
    • Templat will receive an additional content-form, so it will be submitted to the text field.

Before we go on, there's one final reminder: Note how we can add a new post request to the database in the following way:

Return Redirect (Url_for (' index '))

We can easily jump over redirects and allow it to jump to the template rendering section and be more efficient. Because all of the redirects are returned to the same feature view after the Web browser.

So, why choose redirection? Considering that when a user writes a POST request for a blog, it simply commits 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: Due to the limited personal level, if you find that the translation and the original discrepancy please correct.) Thank you! )


If there is no redirection, then the last one submitted to the form is the POST request, so a "Refresh Action" will resubmit that form, which will result in the same post record for the second commit and the first write to the database. Such behavior does not good.

In the case of redirection, we can force the browser to make another request after the form commits, crawling 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 POST request to a blog, causing the post request to be repeatedly written.

Show blog POST request

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

If you recall some of the previous articles, we have created many so-called "fake" requests and displayed them on the home page for a long time. These "false" objects are created as Python lists in the indexed view.

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

But in the previous article, we created a query that allowed us to get all the requests from the "followers", so we could replace the above with the following statement (fileapp/views.py):

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

Then when you run the app, you'll see the Bolg POST request fetched in the flush database.


The Followed_posts method of the user class returns a SQL query statement that crawls our requests for interest. In this query, Callingall () retrieves all the requests into a list, so we end up with a structure that is much like the "false" request we have been using so far. They were so similar that even the template didn't notice.

At this point you can play freely on this application. You can create multiple users, let them follow other people, and then post some information to see how each user sees it BOLG the POST request data stream.

Page out


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

We can display such a large number of post groupings to display, or paging.

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

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

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

    1. Page number, starting from 1
    2. The number of records displayed per page
    3. The error token. If true, a 404 error request will be automatically fed back to the client browser if a page request is received that exceeds the range of records. If False, an empty list will be returned without an error.

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

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

# paginationposts_per_page = 3

Using a global profile to change our application is a good idea because we can modify all of the configurations just by going to one place.

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

After that, let's take a look at how URLs judge the request for a different page. We have previously known that Flask's routes can accept parameters, so we can add suffixes 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 #1http://LOCALHOST:5000/INDEX/2 <--page #2

We can easily implement the URLs format 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/
 
  
   
  ', methods = [' GET ', ' POST ']) @login_requireddef 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 takes a 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 there are two route parameters in the three route, so the route must use the default values.


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

We can notice how simple these changes are, and when we change a page, very little code is affected. We try to write each part of the application without assuming that the rest of the work is working, which allows us to write a modular and robust application that makes testing simpler and less error-prone or flawed.

Now you can try to enter the page number in your URL in the address bar of your browser. Make sure you have more than 3 pots, so you can see multiple pages.

Page navigation

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

We 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 member of the pagination object returned by the Paginate method. But this object contains some very useful information inside, so we changed to keep the pagination object (fileapp/views.py):

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


In order to complete this modification, we also need to modify the template (fileapp/templates/index.html):

 
  {% for post in posts.items%}

{{Post.body}}

{% ENDFOR%}


All we do is to apply the full set of paging objects to our templates. The paging object uses the following members:

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

    When we are on the first page, we do not want to show this as well.


    Implementing the Post sub-template

    Back to the article where we added Avatar pictures, we used HTML to define a sub-template for a single post. The reason is that we can use it in multiple places just once and avoid repeating the 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):

     
        {% for post in posts.items%}  {% include ' post.html '%} {% ENDFOR%}

    Magical, huh? We're just replacing the old rendering code with the template. Now we have the post page that contains the avatar picture of 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 posts that the user has published. Accordingly, the user's personal page also needs to be changed according to the design of index.

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

    Add a route that accepts page numbers
    Add a page number parameter to the view function with a default value of 1
    Replace the post list with the appropriate database query and paging code
    To update a template by using a paging object


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

    @app. Route ('/user/
     
        
         
      ') @app. Route ('/user/
      
         
          
       /
       
          
           
        ') @login_requireddef User ( Nickname, page = 1):  user = User.query.filter_by (nickname = nickname). First ()  if user = = None:    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 parameter.

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

     
        {% for post in posts.items%}  {% include ' post.html '%} {% ENDFOR%} {% if Posts.has_prev%}<< newer posts{% else%}<< newer posts{% endif%} | {% if Posts.has_next%} Older posts >>{% Else%}older posts >>{% endif%}

    Conclusion

    The following I have developed this article with the pagination of the Micro-blog application code package and provide everyone to download:

    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.