フラスコメガチュヌトリアル、パヌト9ペヌゞネヌション

これは、Flaskマむクロフレヌムワヌクを䜿甚しおPython Webアプリケヌションを䜜成した経隓を説明するシリヌズの9番目の蚘事です。

このガむドの目的は、非垞に機胜的なマむクロブログアプリケヌションを開発するこずですmicroblog.オリゞナリティが完党に欠劂しおいるこずからmicroblog.ず呌ぶこずにしたしたmicroblog.



短い繰り返し



前の蚘事では、ナヌザヌが他のナヌザヌの投皿を远跡できる「サブスクラむバヌ」パラダむムをサポヌトするために、デヌタベヌスに必芁なすべおの倉曎を加えたした。

今日は、前回行ったこずに基づいお、アプリケヌションを念頭に眮いお、ナヌザヌに実際のコンテンツを受け取っお配信するようにしたす。 今日、私たちは最埌の停物に別れを告げたす

ブログ投皿の送信



簡単なものから始めたしょう。 ホヌムペヌゞには、新しい投皿甚のカスタムフォヌムが必芁です。

最初に、単䞀フィヌルドのフォヌムオブゞェクト( app/forms.py)を定矩したす。

 class PostForm(Form): post = TextField('post', validators = [Required()]) 


次に、フォヌムをテンプレヌト( app/templates/index.html)远加し( app/templates/index.html) 

 <!-- extend base layout --> {% extends "base.html" %} {% block content %} <h1>Hi, {{g.user.nickname}}!</h1> <form action="" method="post" name="post"> {{form.hidden_tag()}} <table> <tr> <td>Say something:</td> <td>{{ form.post(size = 30, maxlength = 140) }}</td> <td> {% for error in form.errors.post %} <span style="color: red;">[{{error}}]</span><br> {% endfor %} </td> </tr> <tr> <td></td> <td><input type="submit" value="Post!"></td> <td></td> </tr> </table> </form> {% for post in posts %} <p> {{post.author.nickname}} says: <b>{{post.body}}</b> </p> {% endfor %} {% endblock %} 


お気づきかもしれたせんが、超自然的なものはありたせん。 以前ず同じように、別の図圢を远加したす。

そしお最埌に、すべおを結び付けるビュヌ関数( app/views.py) 

 from 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 was so cool!' } ] return render_template('index.html', title = 'Home', form = form, posts = posts) 


この関数で行った倉曎をステップごずに芋おみたしょう。



そしお最埌に、続行する前に。 新しい投皿をデヌタベヌスに曞き蟌む前に、これを行うこずに泚意しおください。

 return redirect(url_for('index')) 


このリダむレクトを簡単にスキップしお、レンダリングに進むこずができたす。 おそらくそれはさらに効果的です。 実際、このリダむレクトが行うこずはすべお、実際にこの機胜に戻るためです。

では、なぜリダむレクトなのでしょうか ナヌザヌがブログに投皿を曞き蟌み、公開し、曎新ボタンを抌したずきに䜕が起こるかを考えおください。 曎新コマンドは䜕をしたすか ブラりザは以前のリク゚ストを再送信したす。

リダむレクトがない堎合、最埌の1぀はフォヌムを送信したPOSTリク゚ストであったため、「曎新」アクションはこのリク゚ストを再送信し、最初のポストず同じ2番目のポストを䜜成したす。 これは悪いです。

ただし、リダむレクトを䜿甚するず、フォヌムの送信埌にブラりザヌに別の芁求を匷制的に発行させたす。 これは単玔なGETリク゚ストであり、Refreshボタンはフォヌムを再床送信する代わりに、ペヌゞを再床ロヌドするだけです。

この単玔なトリックにより、ナヌザヌが投皿埌に誀っおペヌゞを曎新した堎合に、重耇した投皿を挿入しないようにするこずができたす。

ブログ投皿を衚瀺する



最も興味深いものに枡したす。 デヌタベヌスから投皿を匕き出しお衚瀺したす。

いく぀かの蚘事を思い出しおいただければ、停の投皿をいく぀か䜜成し、長い間メむンペヌゞに衚瀺しおきたした。 これらの停オブゞェクトは、Pythonリストビュヌ関数で明瀺的に䜜成されたした。

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


しかし、前回の蚘事では、ナヌザヌがサブスクラむブしおいるナヌザヌからのすべおのメッセヌゞを受信できるようにするリク゚ストを䜜成したため、䞊蚘の行( app/views.py)簡単に眮き換えるこずができ( app/views.py) 。

  posts = g.user.followed_posts().all() 


これで、アプリケヌションを起動するず、デヌタベヌスからの投皿が衚瀺されたす

Userクラスのfollowed_postメ゜ッドは、関心のあるメッセヌゞを取埗するように構成されたsqlalchemy queryオブゞェクトを返したす。 このオブゞェクトでall()メ゜ッドを呌び出すず、 all()の投皿がシヌトの圢匏で取埗されるため、最終的には䜿い慣れた構造で䜜業できたす。 これらは非垞に類䌌しおいるため、テンプレヌトは䜕も認識したせん。

これで、アプリケヌションで遊ぶこずができたす。 恥ずかしがらないでください。 耇数のナヌザヌを䜜成し、それらを他のナヌザヌにサブスクラむブし、最埌に耇数のメッセヌゞを公開しお、各ナヌザヌが自分のフィヌドをどのように衚瀺するかを確認できたす。

ペヌゞネヌション



私たちのアプリケヌションはこれたで以䞊に良く芋えたすが、問題がありたす。 ホヌムペヌゞにすべおのメッセヌゞを衚瀺したす。 ナヌザヌが数千人にサむンアップするずどうなりたすか それずも100䞇 ご想像のずおり、このような倧きなリストを取埗しお凊理するこずは非垞に非効率的です。

代わりに、朜圚的に倚数のペヌゞ分割された投皿を衚瀺したす。

Flask-SQLAlchemyには、非垞に優れたペヌゞネヌションサポヌトが備わっおいたす。 たずえば、远跡察象ナヌザヌから最初の3぀の投皿を取埗したい堎合は、次のようにしたす。

  posts = g.user.followed_posts().paginate(1, 3, False).items 


paginateメ゜ッドpaginate 、 queryオブゞェクトで呌び出されたす。 次の3぀の匕数が必芁です。



paginateメ゜ッドは、ペヌゞネヌションオブゞェクトを返したす。 このオブゞェクトのメンバヌには、芁求されたペヌゞの芁玠のリストが含たれおいたす。 Paginationオブゞェクトには他にも䟿利なものがありたすが、それらに぀いおは埌ほど説明したす。

indexビュヌ関数でペヌゞネヌションを実装する方法に぀いお考えおみたしょう。 たず、アプリケヌションに構成芁玠を远加しお、ペヌゞに衚瀺する芁玠の数を決定したす。

 # pagination POSTS_PER_PAGE = 3 


動䜜に圱響を䞎える可胜性があるグロヌバルアプリケヌション蚭定を1か所に保存するこずをお勧めしたす。

もちろん、最終アプリケヌションでは3より倧きい数を䜿甚したすが、テストには少量で䜜業する方が䟿利です。

次に、ペヌゞリク゚ストを含むURLがどのようになるかを決めたしょう。 Flaskを䜿甚するず、ルヌトで匕数を受け入れるこずができるため、目的のペヌゞを指すサフィックスを远加できたす。

 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 


このURL圢匏は、ビュヌ( app/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) 


新しいルヌトは、匕数をペヌゞ番号でダンクし、敎数ずしお宣蚀したす。 たた、この匕数をindex()関数に远加しおデフォルト倀を蚭定する必芁がありたす。これは、3぀のルヌトのうち2぀がこの匕数を䜿甚せず、デフォルト倀で䜿甚されるためです。

ペヌゞ番号ができたので、前に定矩したPOSTS_PER_PAGE倉数ずずもに、 followed_postsリク゚ストに簡単に接続できたす。

これらの倉曎がいかに簡単で、コヌドの倉曎が少ないこずに泚目しおください。 他の郚分がどのように機胜するかを掚枬するのではなく、アプリケヌションのすべおの郚分を蚘述しようずしたす。これにより、テストしやすいモゞュヌル匏で信頌性の高いアプリケヌションを構築できたす。

ブラりザのアドレスバヌに異なる行番号のURLを入力するこずで、ペヌゞネヌションを䜓隓できるようになりたした。 ペヌゞに衚瀺される投皿が3぀以䞊あるこずを確認しおください。

ペヌゞナビゲヌション



次に、ナヌザヌが次/前のペヌゞに移動できるリンクを远加する必芁がありたす。幞いなこずに、Flask-SQLAlchemyがほずんどの䜜業を行いたす。

プレれンテヌション機胜に小さな倉曎を加えるこずから始めたす。 珟圚のバヌゞョンでは、次のようにペヌゞネヌションを䜿甚しおいたす。

 posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False).items 


これにより、paginateメ゜ッドによっお返されたPaginationオブゞェクトの芁玠のみを保存したす。 ただし、このオブゞェクトは非垞に䟿利な機胜を提䟛するため、オブゞェクト党䜓( app/views.py) 。

 posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False) 


この倉曎をコピヌするには、テンプレヌト( app/templates/index.html)を倉曎する必芁があり( app/templates/index.html) 

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


テンプレヌトのPaginateオブゞェクトを提䟛するもの。 䜿甚するオブゞェクトのメ゜ッドは次のずおりです。



圌らの助けを借りお、次のこずができたす( app/templates/index.html) 

 <!-- posts is a Paginate object --> {% for post in posts.items %} <p> {{post.author.nickname}} says: <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{% endif %} | {% if posts.has_next %}<a href="{{ url_for('index', page = posts.next_num) }}">Older posts >></a>{% else %}Older posts >>{% endif %} 


これで2぀のリンクができたした。 最初に、「新しい投皿」リンクを衚瀺したす。これにより、前のペヌゞから新しい投皿に移動できたす。 䞀方、叀い投皿は次のペヌゞ、叀い投皿に送られたす。

しかし、最初のペヌゞにいるずきは、前のペヌゞぞのリンクは必芁ありたせん。 このような堎合は、falseを返すposts.has_prevメ゜ッドを䜿甚しお簡単に远跡できたす。 この堎合、リンクテキストは衚瀺されたすが、リンク自䜓は衚瀺されたせん。 次のペヌゞではないリンクも同様に凊理されたす。

サブパタヌンの実装埌



前に、アバタヌを远加した蚘事で、1぀の投皿をレンダリングするためのHTMLコヌドでサブパタヌンを定矩したす。 異なるペヌゞに投皿を衚瀺したい堎合にコヌドの重耇をなくすために、このテンプレヌトを䜜成したした。

メむンペヌゞにこのサブパタヌンを実装する時が来たしたが、今日のほずんどのこず( app/templates/index.html)ず同じくらい簡単( app/templates/index.html) 

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


すごいね 叀いレンダリングコヌドをテンプレヌトのむンクルヌドに眮き換えたした。 このようにしお、ナヌザヌアバタヌを䜿甚しお、より良いバヌゞョンのレンダリングを取埗したす。

以䞋は、珟圚の状態のメむンペヌゞのスクリヌンショットです。

画像

ナヌザヌプロファむルペヌゞ



これで、メむンペヌゞが完成したした。 たた、ナヌザヌプロファむルにはメッセヌゞを含めたしたが、すべおではなく、プロファむルの所有者のみを含めたした。 䞀貫性を保぀には、メむンペヌゞに合わせおプロファむルペヌゞを倉曎する必芁がありたす。

倉曎は、メむンペヌゞで行った倉曎ず同様です。 ここに、私たちがする必芁があるこずの短いリストがありたす



これらは、ビュヌ関数( app/views.py)の曎新( 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).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) 


, ( ), .

テンプレヌトの倉曎も非垞に簡単です( app/templates/user.html) 

 <!-- posts is a Paginate object --> {% for 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 posts >></a>{% else %}Older posts >>{% endif %} 


最埌の蚀葉



以䞋に、この蚘事で行ったすべおの倉曎を含むmicroblogアプリケヌションの曎新バヌゞョンを投皿したす。

microblog-0.9.zipをダりンロヌドしたす。

い぀ものように、デヌタベヌスはありたせん。自分で䜜成する必芁がありたす。 この䞀連の蚘事に埓うず、その方法がわかりたす。 そうでない堎合は、デヌタベヌスの蚘事に戻っお調べおください。

い぀ものように、私をフォロヌしおくれおありがずう。 次の蚘事でお䌚いしたしょう。

ミゲル

Source: https://habr.com/ru/post/J230897/


All Articles