【rails】フォロー機能。追記あり
整理していなかったのでまとめておきます。
参考:第14章 ユーザーをフォローする - Railsチュートリアル
※devise導入済みですすめます。
この機能を導入したら
①view側にフォロー中とフォロワーの数の表示
②フォローボタンの作成
③実際にフォロー中の人、フォロワーを表示
この3つをview側に追加することを目指して作っていきます。
導入前に
twitterのようなフォロー機能は非対称性があります。つまり自分がフォローしていたとしても、
フォローが返されない、とう言う状況もあります。
このことを頭に入れなければいけません。
モデルの導入
rails generate model Relationship follower_id:integer followed_id:integer
そして、 できたファイルに
class CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end
これでfollower_idとfollowed_idの組み合わせが必ずユニークであることを保証します。
マイグレーションファイルに書いてある add_index - Qiita
データベースにindexを張る方法 - Qiita
DBのインデックスと複合インデックス - Qiita
次に$ rails db:migrate
関連付け
userとreleationshipは1対多の関係にあります。
class User < ApplicationRecord has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy end
いつもの関連付けなら、has_many :relationships
とすることろを、自分がフォローした(能動関係)を作るためにhas_many :active_relationships
にして、
次に、has_many :active_relationships
というモデルは無いので
class_name: "Relationship"
と書いて、Railsに探して欲しいモデルのクラス名を明示的に伝える。
更に、外部キーを先程作った foreign_key: "follower_id",
で指定します。
(ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要があります。
そのため、関連付けにdependent: :destroyも追加しています。)
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end
本来ならbelongs_to :user
、外部キーがuser_id
としますが、今回は仮想的にフォローする人される人に分けたいのでfollower_id
、followed_id
として関連付けます。
belongs_to :follower, class_name: "User"
とすることで
userと関連付けるのをfollower_id
と指定します。
また、follower
、followerd
というクラスは存在しないのでclass_name: "User"
として明記します。
このように書くと、今回はuser_id
がfollwer_id
、followed_id
として認識されます。
つまり、relationshipモデルのfollower_idやfollowed_idとuserモデルのidがひも付きます。
この結果、下記のメソッドが使えるようになります。
active_relationship.follower フォロワーを返します active_relationship.followed フォローしているユーザーを返します user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力) user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す
バリテーションの追加
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true end
現時点では、フォローするuserとの1対多の関係ができました。この時点で②フォローボタンの作成 のみならほとんどてきてしまいます。
多対多
一人のユーザーはいくつもフォローするしされる、つまり多対多の関係にあります。
class User < ApplicationRecord has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed #追加したのを和訳すると userはactive_relationshipsを通ってfollowingをたくさん持っている。 さらに細かく言うと、外部キーがfollower_idと紐付いているuserはactive_relationshipsを通ってfollowed_idをたくさん持っている end
Active Record の関連付け - Railsガイド
:sourceパラメーター を使って、「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝えます。
この結果、user.following
でユーザーがフォロー中の人がわかります。 つまりfollowed_id
を抜き出すことができます。
さらに、user.following.count
でフォロー中の人数がわかるようにます。
follow、unfollow、following?メソッドを作成
followingで取得した集合をより簡単に取り扱うために、followやunfollowといった便利メソッドを追加します。
class User < ApplicationRecord # ユーザーをフォローする #followed_idにother_userを追加 def follow(other_user) following << other_user end # ユーザーをフォロー解除する # followed_idを削除 def unfollow(other_user) active_relationships.find_by(followed_id: other_user.id).destroy end # 現在のユーザーがフォローしてたらtrueを返す def following?(other_user) following.include?(other_user) end end
フォロワー側
今まではすべて自分からフォローした側でしたが、自分をもフォローされます。
しかし、これはいままでとすべて反対のことをすればいいだけです。
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed has_many :followers, through: :passive_relationships, source: :follower end
この結果、user.followers
でユーザーをフォロワーがわかります。 つまりfollower_id
を抜き出すことができます。
さらに、user.followers.count
でフォロワー数がわかるようにます。
view側
①view側にフォロー中とフォロワーの数の表示
②フォローボタンの作成
③実際にフォロー中の人、フォローされた人を表示
をいよいよ作っていきます。
①view側にフォロー中とフォロワーの数の表示
touch app/views/devise/shared/_stats.html.slim
この中にフォロー中とフォロワーの数の表示を書きます。
#app/views/devise/shared/_stats.html.slim - @user ||= current_user .stats | フォロー中のユーザー = link_to following_user_path(@user) div#following = @user.following.count | フォロワーリスト = link_to followers_user_path(@user) div#followers =@user.followers.count
そして、 表示させます。
#app/views/users/show.html.slim(わたしの場合) = render 'devise/shared/stats'
これで、①view側にフォロー中とフォロワーの数の表示 ができました。 また、
= link_to following_user_path(@user) = link_to followers_user_path(@user)
次にこのリンクから③実際にフォロー中の人、フォローされた人を表示のviewを作っていきます。
③実際にフォロー中の人、フォローされた人を表示
まずはルーティングから
config/routes.rb Rails.application.routes.draw do resources :users do member do get :following, :followers end end end
このURLで下記のようになります。
HTTPリクエスト URL アクション 名前付きルート GET /users/1/following following following_user_path(1) GET /users/1/followers followers followers_user_path(1)
次に、コントローラー
class UsersController < ApplicationController def following @user = User.find(params[:id]) @users = @user.following render 'show_follow' end def followers @user = User.find(params[:id]) @users = @user.followers render 'show_follow' end end
render 'show_follow
を2つのアクションで使い回しても@users
の中身は違うので問題有りません。
show_follow
のviewを作っていきます。
touch app/views/users/ahow_follow.htnl.slim
#follow_show .container - @users.each do |user| .user_content = link_to user_path(user) = user.username #←.usernameはuserモデルの中のカラム名に合わせてください
②フォローボタンの作成
これで最後です。
まずはコントローラーを作っていきます。
rails generate controller Relationships
次にルーティングです。
フォロー関係を作るか、消すかだけなので
Rails.application.routes.draw do resources :users do member do get :following, :followers end end resources :relationships, only: [:create, :destroy] end
次に、コントローラーを書きます
#app/comtroller/relationships_controller.rb class RelationshipsController < ApplicationController def create @user = User.find(params[:followed_id]) current_user.follow(@user) redirect_to @user end def destroy @user = Relationship.find(params[:id]).followed current_user.unfollow(@user) redirect_to @user end end
さらにボタンを作っていきます。
フォローするボタン
touch app/views/relationships/_follow.html.slim
=button_to current_user.active_relationships.build, params: { followed_id: @user.id } | フォローする
フォローを解除するボタン
touch app/views/relationships/_unfollow.html.slim
=button_to current_user.active_relationships.find_by(followed_id: @user.id), method: :delete | 解除する
そして上記のボタンをまとめます。
touch app/views/users/_follow_form.htnl.slim
- unless @user == current_user #follow_form - if current_user.following?(@user) = render 'relationships/unfollow' - else = render 'relationships/follow'
最後にこれを表示させます。
#app/views/users/show.html.slim(わたしの場合) = render 'devise/shared/stats' = render 'users/follow_form'
おしまい。
追記
【rails】任意のviewのみで特定のjsを読み込む。
とっても簡単です。
参考:
Rails 必要なJavaScriptのみを読み込む | | KeruuWeb
RailsがJavaScriptやスタイルシートを読み込む仕組み - りょうたくの技術ブログ
Asset Pipelineとは
そもそも、任意のveiwのみでjsが読み込んでくれないとは、Asset Pipelineがしっかり機能しているからです。
アセットパイプラインがアセットを連結するとこが第一の機能なのです。
すべてのJavaScriptファイルを1つのマスター.jsファイルに連結し、すべてのCSSファイルを1つのマスター.cssファイルに連結します。
そのため任意のviewのみで読み込むためには手を加える必要があります。
ツリー読み込みの停止
すべて自動で読み込むのを書き換えます。
#app/assets/javascripts/application.js - //= require_tree . + // require_tree .
読み込ませたいjsファイルを記述
#config/initializers/assets.rb Rails.application.config.assets.precompile += %w( test.js )
最後に表示させたいviewに読み込ませます
= javascript_include_tag "test.js"
おしまい
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale.
参考:config.i18n.fallbacksのメッセージについて - Qiita
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0
上記のようなエラーが出てきました。
ちなみにconfig/environments/produciton.rb
の74行目
に'config.i18n.fallbacks = true'.
と私の場合書いてあります。
gemfile.lockを見ると、
i18n (1.7.0) rails (5.2.4)
です。
If you're using I18n (>= 1.1.0) and Rails (< 5.2.2),
にはあたりません。
i18nのバージョンを下げたりもしましたが、注意が出ます。
これは何もしない感じで
【rails】dockerでのDB(postgersql)操作
dockerでの開発環境を構築し、DBにpostgresqlを使いました。今回はDBにアクセスしてテーブル内容の確認をメモしておきます。
まずは立ち上げる
docker-compose up -d
終了は
docker-compose down
参考:
docker-compose コマンド — Docker-docs-ja 1.10.0b ドキュメント
DBに接続
$ docker-compose exec db psql -d database -U username -h host -d: データベース名(未指定だと、ログインユーザー名のデータベースに接続する) -U: ユーザ名(未指定だと、ログインユーザー名になる) -h: ホスト名(未指定だと、localhostになる)
上記の内容はconfig/databese.yml
から参考にする
私の場合は、
default: &default adapter: postgresql encoding: UTF8 host: db username: postgres password: pool: 5 development: <<: *default database: myapp_development
なので
$ docker-compose exec db psql -d myapp_development -U postgres -h db
です。
そうすると
myapp_development=#
と出てくるのでそのまま操作します。
終了はexit
または\q
です。
postgresql操作コマンド
データーベース一覧表示 postgres=# \l
テーブル一覧を表示 postgres=# \z
テーブル定義を確認 postgres=# \d tablename tablenameには任意のテーブル名を入れる。
以上です
【rails】モデルの関連付け
ポートフォリオを改修中に記憶が曖昧になったのでまとめておきます。
前置き
モデルがuser
とpost
の場合、かつuser:post = 1:多
の関係の場合
主キーと外部キーとは
今回の場合、userテーブルに元々あるidが主キーで、
あとに作る、 追加したpost テーブルにuser_idを外部キーとして
主キーを外部キーに保存することで関連付けができます
主キー側のモデル(belongs_toで指定してる側)が親と考えるとスッキリします。
モデルへの書き込み
postは一人のuserから生み出されますpost belongs_to 単数形
class Post < ApplicationRecord belongs_to :user end #これでpost belongs to userになる
userはたくさんのpostを持っているのでhas_many 複数形
class User < ApplicationRecord has_many :posts end #これでuser has many postsになる
これだけで、主キーと外部キーを認識してくれます。
add migrate
$ rails g migration add_user_reference_to_post user:references
※ここでuser_id:references
としなくても勝手にuser_id
としてテーブル側には作成してくれるので注意です。
そして
$ rails db:migarte
その結果
class AddUserToPost < ActiveRecord::Migration[5.2] def change add_reference :posts, :user, foreign_key: true end end
postコントローラー側
実際に使う場合、
class PostController < ApplicationController def create @post = Post.new(post_params.merge(user_id: current_user.id)) if @post.save redirect_to post_path(@post) else render :new end end ........... private def post_params params.require(:post).permit(:name,:description) end
view側
#postのviewの場合 @post.user.name #これでpostを投稿したuserのnameが取得てきます。
【rails】slim導入と使い方。
slimについてまとめてなかったのでまとめておきます。
slimとは
Slim は 不可解にならない程度に view の構文を本質的な部品まで減らすことを目指したテンプレート言語、
RailsのViewを出力するためのテンプレートエンジンとして、標準で利用されるのはERBですが、
より簡素に書けるテンプレートエンジンとしてslim があります
参考:slim/README.jp.md at master · slim-template/slim · GitHub
RailsのHTMLテンプレートエンジン、Slimの基本的な記法 - Qiita
【爆速で習得】Railsでslimを使う方法から基本文法まで - Qiita
変換に困ったとき
HTML2slim
ERB 2 SLIM
導入
#gemfile gem 'slim-rails' #slimのジェネレーターを提供するslim-rails gem 'html2slim' #ERB形式をslim形式に変換してくれるhtml2slim
その後
$ bundle
現時点では、ERB形式のファイルが存在してるので、
$ bundle exec erb2slim app/vies/layouts/ --delete
これで準備はOKです。
使い方
テキスト
|
,このパイプより深くインデントされたのがテキストブロックになります
body p | これはテキストブロックのテストです。
制御コード
-
ERBで<% %>
と書くところを-
のみで表すことができます。
出力
=
ERBで<%= %>
と書くところを=
のみで表すことができます.
HTMLタグ
<
と>
を消す、閉じタグはいらない。
インデントを気をつける。
条件式
endは書かなくて、<% %>
から-
で表す.
-if -else のみ
classやid
class =
を.
id =
を#
で置き換える。
#html <dev class="content"> <p class="title">タイトル</hoge> </dev> <dev id="content"> <p id="title">タイトル</hoge> </dev>
↓
#slim dev.content p.title タイトル dev#content p#title タイトル
おわり。
【GIt】一人でPull Request練習
現在、独学でポートフォリオを作成中です。一人のみでgitを触っているとPull Requestなんて全くしないので、良くないとも思い練習します。
※事前に、githubのアカウント、ローカルの開発環境が整っている前提で進めます。
Pull Request
Pull Requestをすると、git clone元のディレクトリにmergeのお願いをするみたいなイメージで捉えてます。
一人で練習する流れ
作成したディレクトリをローカル環境へgit cloneしてbranchに切り替えて内容を変更したら、git add、git commitして、
branchのままgit push origin ブランチ名
です。
次にgit hub上で、変更内容を説明してPull Requestし、またPull Requestしたのを自分でmergeするだけです。