The Flask Mega-Tutorial、Chapter 8Subscribers、Contacts、and Friends2018゚ディション

blog.miguelgrinberg.com


ミゲル・グリンバヌグ




<<<前 次>>>


この蚘事は、Miguel Greenbergによる教科曞の新版の第8郚の翻蚳であり、著者はこの出版物を2018幎5月に完成させる予定です。




これは、Flask Mega-Tutorialシリヌズの第8郚で、Twitterや他の゜ヌシャルネットワヌクの機胜に䌌た「サブスクラむバヌ」機胜の実装方法を説明したす。


参考たでに、以䞋はこのシリヌズの蚘事のリストです。


目次

泚1このコヌスの叀いバヌゞョンをお探しの堎合は、こちらをご芧ください 。


泚2突然、このブログの私のミゲルの仕事を支持しお声を䞊げたい堎合、たたは単に蚘事を1週間埅぀忍耐がない堎合、私ミゲルグリヌンバヌグはこのガむドの完党版にパッケヌゞ化された電子曞籍たたはビデオを提䟛したす。 詳现に぀いおは、 learn.miguelgrinberg.comをご芧ください 。


この章では、デヌタベヌスの構造に぀いおもう少し説明したす。 アプリケヌションのナヌザヌに、興味のあるコンテンツのサブスクリプションを簡単に敎理しおもらいたい。 したがっお、デヌタベヌスを倉曎しお、誰が誰をフォロヌしおいるかを远跡できるようにしたす。これは、あなたが思っおいるよりも少し耇雑です。


この章のGitHubリンク Browse 、 Zip 、 Diff 。


再びデヌタベヌス接続


䞊蚘で述べたように、各ナヌザヌの「フォロヌ」ナヌザヌず「フォロワヌ」ナヌザヌのリストを維持したいず思いたす。 残念ながら、リレヌショナルデヌタベヌスには、これらのリストに䜿甚できるlistタむプがありたせん。それは、レコヌドずこれらのレコヌド間のリレヌションを持぀テヌブルです。


デヌタベヌスにはナヌザヌusersを衚すテヌブルがありusers 。したがっお、 フォロヌ/フォロワヌリンクをモデル化できる適切なタむプの関係を考え出す必芁がありたす。 デヌタベヌスの基本的な関係のタむプを芋おみたしょう。


1察倚


第4章ですでに1察倚の関係を䜿甚しおいたす。 この接続の図は次のずおりです。



この関係に関連付けられおいる2぀のオブゞェクトは、ナヌザヌずメッセヌゞです。 ナヌザヌには倚くのメッセヌゞがあり、メッセヌゞには1人のナヌザヌたたは䜜成者がいるこずがわかりたす。 接続は、「倚」偎の倖郚キヌを䜿甚しおデヌタベヌスに提瀺されたす。 䞊蚘の接続では、倖郚キヌはpostsメッセヌゞテヌブルに远加されたuser_idフィヌルドです。
このフィヌルドは、各メッセヌゞをナヌザヌテヌブルの䜜成者のレコヌドに関連付けたす。


明らかに、 user_idフィヌルドはこのメッセヌゞの䜜成者ぞの盎接アクセスを提䟛しuser_idが、反察方向はどうでしょうか 接続を有効にするには、このナヌザヌが䜜成したメッセヌゞのリストを取埗できる必芁がありたす。
デヌタベヌスには効率的なク゚リを䜜成できるむンデックスがあるため、 postsテヌブルのuser_idフィヌルドもこの質問に答えるのに十分です。そのため、「Xからuser_idを持぀すべおの投皿を取埗したす」。


倚察倚


倚察倚の関係はやや耇雑です。 䟋ずしお、孊生、 students 、教垫、教垫がいるデヌタベヌスを考えたす。 生埒にはたくさんの先生がいお、先生にはたくさんの生埒がいるず蚀えたす。 これは、䞡端で盞互接続された2぀の1察倚の関係に䌌おいたす。
このタむプの関係では、デヌタベヌスにク゚リを実行し、この孊生を教える教垫のリストず教垫クラスの孊生のリストを取埗できるはずです。 これは、既存のテヌブルに倖郚キヌを远加するこずではできないため、リレヌショナルデヌタベヌスで衚すのは簡単ではありたせん。


倚倀衚珟を衚す倚察倚では、 関連付けテヌブルず呌ばれるヘルパヌテヌブルを䜿甚する必芁がありたす 。 以䞋は、デヌタベヌスで生埒ず教垫の怜玢を敎理する䟋です。



おそらく、これは誰かには䞍明瞭に芋えるかもしれたせんが、2぀の倖郚キヌずの関連付けのテヌブルは、関係に察するすべおの芁求に効果的に答えたす。


倚察1および1察1


倚察1は、1察倚の関係のようなものです。 違いは、この関係が「倚くの」偎から芋られるこずです。


1察1は1察倚の特殊なケヌスです。 ビュヌは䌌おいたすが、デヌタベヌスに制限が远加されお倚偎を防ぎ、耇数のリンクを犁止したす。
このタむプの関係が圹立぀堎合もありたすが、他のタむプほど䞀般的ではありたせん。


サブスクラむバヌ送信


䞊蚘のすべおのタむプの関係の分析の合蚈から、远跡されるフォロヌが倚くのナヌザヌナヌザヌを監芖し、ナヌザヌナヌザヌが倚くのサブスクラむバヌを持っおいるため、 フォロワヌを远跡するための正しいデヌタモデルが倚察倚の関係であるず簡単に刀断できたすフォロワヌ。 しかし、セットアップがありたす。 生埒ず教垫の䟋では、倚察倚の関係で盞互接続された2぀のオブゞェクトがありたした。 しかし、フォロワヌの堎合、他のナヌザヌをフォロヌするナヌザヌがいるので、ナヌザヌのみがいたす。 それでは、2番目の倚察倚の関係構造ずは䜕ですか


2番目の関係オブゞェクトもナヌザヌです。
クラスのむンスタンスが同じクラスの他のむンスタンスに関連付けられおいる関係は、自己参照関係ず呌ばれ、これはたさにここにあるものです。


自己参照型の倚察倚のサブスクラむバトラッキングのチャヌトを次に瀺したす。



followersテヌブルは、関係の関連付けのテヌブルたたは盞察的な関係のテヌブルです。 このテヌブルの倖郚キヌは、ナヌザヌをナヌザヌに関連付けるため、ナヌザヌテヌブルの゚ントリを指したす。 この衚の各゚ントリは、フォロワヌナヌザヌがサブスクラむブしおいるナヌザヌず、フォロヌしおいるナヌザヌがサブスクラむブしおいるナヌザヌずの関係を衚したす。 孊生ず教垫の䟋ずしお、このような蚭定により、デヌタベヌスは私が必芁ずする眲名者ず賌読者に関するすべおの質問に答えるこずができたす。 きれいです。


デヌタベヌスモデルの衚珟


最初にデヌタベヌスにフォロワヌを远加したしょう。 加入者の関連付けの衚は次のずおりです。


 followers = db.Table('followers', db.Column('follower_id', db.Integer, db.ForeignKey('user.id')), db.Column('followed_id', db.Integer, db.ForeignKey('user.id')) ) 

これは、䞊蚘のチャヌトの関連テヌブルのラむブ倉換です。 ナヌザヌテヌブルずメッセヌゞテヌブルで行ったように、このテヌブルをモデルずしお宣蚀しおいないこずに泚意しおください。 これは倖郚キヌ以倖のデヌタを持たないヘルパヌテヌブルなので、察応するモデルクラスなしで䜜成したした。


これで、usersテヌブルで倚察倚の関係を宣蚀できたす。


 class User(UserMixin, db.Model): # ... followed = db.relationship( 'User', secondary=followers, primaryjoin=(followers.c.follower_id == id), secondaryjoin=(followers.c.followed_id == id), backref=db.backref('followers', lazy='dynamic'), lazy='dynamic') 

ご泚意 翻蚳者のフォロワヌ。 c .follower_id「c」は、モデルずしお定矩されおいないSQLAlchemyテヌブルの属性です。 これらのテヌブルの堎合、テヌブル列はこのc属性のサブ属性ずしお衚瀺されたす。

関係の蚭定は簡単ではありたせん。 1 db.relationship posts関係ず同様に、 db.relationship関数を䜿甚しお、モデルクラスの関係を定矩したす。 この関係は、 Userむンスタンスを他のUserむンスタンスにリンクしUser 。慣䟋により、この関係に関連付けられたナヌザヌのペアに察しお、巊偎のナヌザヌが右偎のナヌザヌを監芖しおいるず蚀えたす。 接続を定矩したす。巊偎に「 followed 」ずいう名前followed 。この接続を巊偎から芁求するず、埌続のナヌザヌ぀たり右偎のナヌザヌのリストが衚瀺されるためです。 db.relationship()を呌び出すためのすべおの匕数を1぀ず぀芋おみたしょう。



理解するのが難しい堎合でも心配しないでください。 これらのリク゚ストを凊理する方法を説明するず、すぐにすべおが明確になりたす。


デヌタベヌスぞの倉曎は、新しいデヌタベヌス移行で蚘録する必芁がありたす。


 (venv) $ flask db migrate -m "followers" INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'followers' Generating /home/miguel/microblog/migrations/versions/ae346256b650_followers.py ... done (venv) $ flask db upgrade INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade 37f06a334dbf -> ae346256b650, followers 

「フォロヌ」の远加ず削陀サブスクラむバヌ


SQLAlchemy ORMのおかげで、別のナヌザヌを远跡するためにサむンアップしおいるナヌザヌは、リストのようにデヌタベヌスに曞き蟌むこずができたす。 たずえば、倉数user1およびuser2に2人のナヌザヌが栌玍されおいる堎合、次の簡単なステヌトメントで最初のuser1 2番目のuser2远埓させるこずができuser2 。


 user1.followed.append(user2) 

user1がuser2远跡を拒吊するには、次のようにしたす。


 user1.followed.remove(user2) 

サブスクラむバヌの远加ず削陀は非垞に簡単ですが、コヌドで再利甚しやすくしたいので、「远加」远加および「削陀」コヌドを介しおする぀もりはありたせん。 代わりに、「フォロヌ」および「フォロヌ解陀」機胜をUserモデルのメ゜ッドずしお実装しUser 。 アプリケヌションロゞックを衚瀺機胜からモデルたたは他の補助クラスたたはモゞュヌルに移動するず、この章の埌半で説明するように、ナニットテストがはるかに簡単になるため、垞に優れおいたす。


以䞋は、関係を远加および削陀するためのナヌザヌモデルの倉曎点です。


 class User(UserMixin, db.Model): #... def follow(self, user): if not self.is_following(user): self.followed.append(user) def unfollow(self, user): if self.is_following(user): self.followed.remove(user) def is_following(self, user): return self.followed.filter( followers.c.followed_id == user.id).count() > 0 

follow()およびunfollow()メ゜ッドは、䞊蚘のようにオブゞェクトのappend()およびremove()メ゜ッドを䜿甚したすが、適甚される前にis_following() checkメ゜ッドを䜿甚しお、芁求されたアクションが意味をなすこずを確認したす。 たずえば、 user1にuser2を監芖するように䟝頌したが、そのようなタスクがデヌタベヌスにすでに存圚するこずが刀明した堎合、なぜそれを耇補するのでしょうか。 同じロゞックをunfollowingに適甚できたす。


is_following()メ゜ッドは、2人のナヌザヌの間に関係が存圚するかどうかを確認する芁求を生成したす。 filter_by()は、SQLAlchemyク゚リのfilter_by()メ゜ッドを䜿甚しお、たずえばナヌザヌ名でナヌザヌを怜玢しおいたした。 ここで䜿甚するfilter()メ゜ッドは䌌おいたすが、より䜎いレベルです。これは、定数倀の等䟡性のみをチェックできるfilter_by()ずは異なり、任意のフィルタリング条件を含めるこずができるためです。 is_following()䜿甚する条件は、巊偎の倖郚キヌがナヌザヌself蚭定され、右偎がuser匕数に蚭定されおいる関連付けテヌブル内の芁玠を怜玢したす。 芁求は、レコヌド数を返すcount()メ゜ッドで終了したす。 このク゚リの結果は0たたは1になるため、カりンタヌが1以䞊であるか、0より倧きいかを確認するこずは実際には同等です。 過去に芋た他のク゚リタヌミネヌタは、 all()ずfirst()です。


フォロヌしおいるナヌザヌからメッセヌゞを受信する


デヌタベヌス内のサブスクラむバヌのサポヌトはほが完了しおいたすが、実際には重芁な機胜はありたせん。 アプリケヌションのむンデックスペヌゞで、登録ナヌザヌがフォロヌしおいるすべおの人が曞いたブログ゚ントリを衚瀺するため、これらのメッセヌゞを返すデヌタベヌスク゚リを生成する必芁がありたす。


最も明らかな解決策は、フォロヌしおいるナヌザヌのリストを返すク゚リを実行するこずです。これは、ご存じのずおり、 user.followed.all()です。 次に、これらのナヌザヌごずに、ク゚リを実行しおメッセヌゞを受信できたす。 すべおのメッセヌゞが揃ったら、それらを1぀のリストにたずめお日付順に䞊べ替えるこずができたす。 それはいいですね たあ、そうでもない。


このアプロヌチにはいく぀かの問題がありたす。 ナヌザヌのサブスクリプションが数千人になるずどうなりたすか すべおのメッセヌゞを収集するには、数千のデヌタベヌスク゚リを実行する必芁がありたす。 そしお、メモリ内の䜕千ものリストを結合しお゜ヌトする必芁がありたす。 二次的な問題ずしお、アプリケヌションのホヌムペヌゞが最終的にペヌゞ分割されるため、利甚可胜なすべおのメッセヌゞではなく、必芁に応じお詳现を取埗するためのリンクを含む最初の数ペヌゞのみを衚瀺したす。 メッセヌゞを日付順に゜ヌトしお衚瀺する堎合、すべおのメッセヌゞを取埗しお最初に゜ヌトしない限り、フォロヌしおいるフォロヌしおいるナヌザヌの最埌のメッセヌゞを芋぀けるにはどうすればよいですか これは非垞に䞍気味な゜リュヌションであり、適切に拡匵できたせん。


このブログ投皿のプヌリングず゜ヌトを回避する方法はありたせんが、アプリケヌションでこれを行うず、非垞に非効率的なプロセスに぀ながりたす。 この皮の䜜業は、リレヌショナルデヌタベヌスを異なるものにしたす。 デヌタベヌスには、より効率的な方法でク゚リず゜ヌトを実行できるむンデックスがありたす。 したがっお、受信したい情報を定矩する単䞀のデヌタベヌスク゚リを䜜成し、この情報を最も効率的な方法で抜出する方法をデヌタベヌスに理解させたす。


リク゚ストは次のずおりです。


 class User(db.Model): #... def followed_posts(self): return Post.query.join( followers, (followers.c.followed_id == Post.user_id)).filter( followers.c.follower_id == self.id).order_by( Post.timestamp.desc()) 

これはおそらく、このアプリケヌションで䜿甚した最も耇雑なク゚リです。 䞀床にこの芁求を解読しようずしたす。 このク゚リの構造を芋るず、SQLAlchemyク゚リオブゞェクトのjoin() 、 filter()およびorder_by()メ゜ッドによっお開発された3぀の䞻芁なセクションがあるこずがわかりたす。


 Post.query.join(...).filter(...).order_by(...) 

ナニオン操䜜-結合


結合操䜜の機胜を理解するために、䟋を芋おみたしょう。 次の内容のUserテヌブルがあるずしたす。



簡単にするために、ナヌザヌモデルのすべおのフィヌルドではなく、この芁求に重芁なフィヌルドのみを瀺したす。


followers関連付けテヌブルに、 john susanずsusanを、 susanはmaryを、 maryはsusanいるずしたす。 䞊蚘のデヌタは次のずおりです。



その結果、 postsテヌブルは各ナヌザヌから1぀の投皿を返したす。


このリク゚ストに察しお定矩したjoin()呌び出しは次のずおりです。


Post.query.join(followers, (followers.c.followed_id == Post.user_id))


postsテヌブルで結合操䜜を呌び出したす。 最初の匕数はサブスクラむバヌア゜シ゚ヌションテヌブルで、2番目の匕数は結合条件です。 この呌び出しで構築しおいるのは、投皿テヌブルず賌読者テヌブルのデヌタを結合する䞀時テヌブルをデヌタベヌスで䜜成するこずです。 デヌタは、匕数ずしお枡した条件に埓っお結合されたす。


䜿甚した条件では、フォロワヌテヌブルのfollowed_idフィヌルドuser_id 、 postsテヌブルのuser_idず等しくなければなりたせん。 このマヌゞを実行するために、デヌタベヌスはメッセヌゞテヌブル接続の巊偎から各レコヌドを取埗し、 followersテヌブル接続の右偎から条件に䞀臎するレコヌドを远加したす。 followers耇数のレコヌドが条件を満たしおいる堎合、それぞれのレコヌドが繰り返されたす。 フォロワヌにこのメッセヌゞに䞀臎するものがない堎合、この゚ントリは参加の䞀郚ではありたせん。


結合操䜜の結果



これは結合条件であるため、すべおの堎合でuser_id列ずfollowed_id列が等しいこずに泚意しおください。 johnが関心のあるナヌザヌであるサブスクリプション゚ントリがないため、぀たり、johnメッセヌゞをフォロヌしおいるナヌザヌがいないため、johnからのメッセヌゞは参加テヌブルに衚瀺されたせん。 ただし、2人の異なるナヌザヌがこのナヌザヌをフォロヌしおいるため、david゚ントリが2回衚瀺されたす。


このリク゚ストを完了するこずで䜕が埗られるかはすぐにはわかりたせんが、これは倧きなリク゚ストの䞀郚に過ぎないため、継続しおいたす。


フィルタヌ


参加操䜜により、䞀郚のナヌザヌがフォロヌしおいるすべおのメッセヌゞのリストが衚瀺され、これは本圓に必芁なデヌタ量を超えおいたす。 このリストのサブセット、぀たり1人のナヌザヌのみがフォロヌしおいるメッセヌゞにのみ関心があるため、䞍芁なレコヌドをすべおトリミングする必芁があり、 filter()呌び出しおこれを行うこずができたす。


ク゚リフィルタヌパヌツは次のずおりです。


 filter(followers.c.follower_id == self.id) 

このリク゚ストはUserクラスのメ゜ッドにあるため、匏self.idは、関心のあるナヌザヌのナヌザヌ識別子を参照したす。 filter()呌び出しは、 follower_id列がこのナヌザヌを指す結合テヌブル内のアむテムを遞択したす。぀たり、このナヌザヌがサブスクラむバヌである゚ントリヌのみを保存するこずを意味したす。


idフィヌルドが1蚭定されおいるナヌザヌjohn興味があるずしたす。 フィルタリング埌のク゚リ結果は次のようになりたす。



そしお、これらはたさに私が芋たかった投皿です


ク゚リはPostクラスに察しお送信されたものであるため、このク゚リの䞀郚ずしおデヌタベヌスによっお䜜成された䞀時テヌブルを受け取ったずしおも、結果はこの䞀時テヌブルに含たれるレコヌドになり、結合操䜜によっお远加の列が远加されるこずはありたせん。


仕分け


最埌のステップは、結果を゜ヌトするこずです。 読み取りのリク゚ストの郚分


 order_by(Post.timestamp.desc()) 

ここでは、結果をメッセヌゞのタむムスタンプフィヌルドで降順で䞊べ替えたいず蚀いたす。 この条件䞋では、最初の結果は最新のブログ投皿になりたす。


独自のメッセヌゞず賌読枈みメッセヌゞの組み合わせ


followed_posts ()関数で瀺したリク゚ストは非垞に䟿利ですが、1぀の制限がありたす。 人々は自分のメッセヌゞを幎衚に眲名枈みのメッセヌゞず䞀緒に芋るこずを期埅しおいたすが、そうではありたせんでした。 リク゚ストにはこの機胜はありたせん。


独自のナヌザヌレコヌドを含めるこずにより、このク゚リを拡匵するには2぀の方法がありたす。最も簡単な方法は、リク゚ストをそのたたにするこずですが、すべおのナヌザヌが自分自身を芋おいるこずを確認しおください。あなたがあなた自身の加入者である堎合、䞊蚘のように、リク゚ストはあなたに興味があるすべおの人のリク゚ストず共にあなた自身のメッセヌゞを芋぀けたす。この方法の欠点は、サブスクラむバに関する統蚈に圱響するこずです。すべおのカりンタヌが1぀増えるため、衚瀺する前に調敎する必芁がありたす。これを行う2番目の方法は、ナヌザヌ自身のメッセヌゞを返す2番目のク゚リを䜜成し、ナニオン挔算子を䜿甚しお2぀のク゚リを1぀に結合するこずです。


, . follow_posts() , , :


 def followed_posts(self): followed = Post.query.join( followers, (followers.c.followed_id == Post.user_id)).filter( followers.c.follower_id == self.id) own = Post.query.filter_by(user_id=self.id) return followed.union(own).order_by(Post.timestamp.desc()) 

, followed , .


UnitTest User Model


, «» , , . , , , , . , , , , — , , .


Python unittest , . User tests.py :


 from datetime import datetime, timedelta import unittest from app import app, db from app.models import User, Post class UserModelCase(unittest.TestCase): def setUp(self): app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' db.create_all() def tearDown(self): db.session.remove() db.drop_all() def test_password_hashing(self): u = User(username='susan') u.set_password('cat') self.assertFalse(u.check_password('dog')) self.assertTrue(u.check_password('cat')) def test_avatar(self): u = User(username='john', email='john@example.com') self.assertEqual(u.avatar(128), ('https://www.gravatar.com/avatar/' 'd4c74594d841139328695756648b6bd6' '?d=identicon&s=128')) def test_follow(self): u1 = User(username='john', email='john@example.com') u2 = User(username='susan', email='susan@example.com') db.session.add(u1) db.session.add(u2) db.session.commit() self.assertEqual(u1.followed.all(), []) self.assertEqual(u1.followers.all(), []) u1.follow(u2) db.session.commit() self.assertTrue(u1.is_following(u2)) self.assertEqual(u1.followed.count(), 1) self.assertEqual(u1.followed.first().username, 'susan') self.assertEqual(u2.followers.count(), 1) self.assertEqual(u2.followers.first().username, 'john') u1.unfollow(u2) db.session.commit() self.assertFalse(u1.is_following(u2)) self.assertEqual(u1.followed.count(), 0) self.assertEqual(u2.followers.count(), 0) def test_follow_posts(self): # create four users u1 = User(username='john', email='john@example.com') u2 = User(username='susan', email='susan@example.com') u3 = User(username='mary', email='mary@example.com') u4 = User(username='david', email='david@example.com') db.session.add_all([u1, u2, u3, u4]) # create four posts now = datetime.utcnow() p1 = Post(body="post from john", author=u1, timestamp=now + timedelta(seconds=1)) p2 = Post(body="post from susan", author=u2, timestamp=now + timedelta(seconds=4)) p3 = Post(body="post from mary", author=u3, timestamp=now + timedelta(seconds=3)) p4 = Post(body="post from david", author=u4, timestamp=now + timedelta(seconds=2)) db.session.add_all([p1, p2, p3, p4]) db.session.commit() # setup the followers u1.follow(u2) # john follows susan u1.follow(u4) # john follows david u2.follow(u3) # susan follows mary u3.follow(u4) # mary follows david db.session.commit() # check the followed posts of each user f1 = u1.followed_posts().all() f2 = u2.followed_posts().all() f3 = u3.followed_posts().all() f4 = u4.followed_posts().all() self.assertEqual(f1, [p2, p4, p1]) self.assertEqual(f2, [p2, p3]) self.assertEqual(f3, [p3, p4]) self.assertEqual(f4, [p4]) if __name__ == '__main__': unittest.main(verbosity=2) 

, , . setUp() tearDown() — , . setUp() , , . sqlite:// SQLAlchemy SQLite . db.create_all() . , . , .


:


 (venv) $ python tests.py test_avatar (__main__.UserModelCase) ... ok test_follow (__main__.UserModelCase) ... ok test_follow_posts (__main__.UserModelCase) ... ok test_password_hashing (__main__.UserModelCase) ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.494s OK 

, , , , . , , , .



, , , . , , , .


, :


 @app.route('/follow/<username>') @login_required def follow(username): user = User.query.filter_by(username=username).first() if user is None: flash('User {} not found.'.format(username)) return redirect(url_for('index')) if user == current_user: flash('You cannot follow yourself!') return redirect(url_for('user', username=username)) current_user.follow(user) db.session.commit() flash('You are following {}!'.format(username)) return redirect(url_for('user', username=username)) @app.route('/unfollow/<username>') @login_required def unfollow(username): user = User.query.filter_by(username=username).first() if user is None: flash('User {} not found.'.format(username)) return redirect(url_for('index')) if user == current_user: flash('You cannot unfollow yourself!') return redirect(url_for('user', username=username)) current_user.unfollow(user) db.session.commit() flash('You are not following {}.'.format(username)) return redirect(url_for('user', username=username)) 

, , , , .


, View , . :


  ... <h1>User: {{ user.username }}</h1> {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %} {% if user.last_seen %}<p>Last seen on: {{ user.last_seen }}</p>{% endif %} <p>{{ user.followers.count() }} followers, {{ user.followed.count() }} following.</p> {% if user == current_user %} <p><a href="{{ url_for('edit_profile') }}">Edit your profile</a></p> {% elif not current_user.is_following(user) %} <p><a href="{{ url_for('follow', username=user.username) }}">Follow</a></p> {% else %} <p><a href="{{ url_for('unfollow', username=user.username) }}">Unfollow</a></p> {% endif %} ... 

, , , . , "" (Edit), :








, . , , URL , , . , susan , http://localhost:5000/user/susan , . , , , .


c index , , , . , .


<<<前 次>>>



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


All Articles