20210908のRubyに関する記事は10件です。

【Rails】いいね順・投稿数順など ◯◯順の考え方・方法

はじめに ランキング順で表示する際の考え方についてつまづいてしまったので、 考え方を残しておきます。 前提 投稿、いいね、タグ それぞれの機能は作成済みとします。  方法 投稿に紐づくいいね順 まず、posts テーブルと likesテーブルには、以下のようなデータが入っているとします。 まずやることは、2つのテーブルを合体させます。 likesテーブルのpost_idとpostsテーブルのidは、同じになるはずです。 "postに紐づくいいねを、多い順に並べる" ↓ "likesテーブルのpost_idの数を集計し、多い順に並べる" と言うことになります。 likesテーブルにはそもそもいいねをもらったpostのidが存在します。 likesのidは、いいねするたびに増えていきます。 post_idはlikesテーブルの中では重複するものであり、ひとつの投稿に着目すると、 その投稿がいいねされるたびにlikesテーブルの特定のpost_idは増えていきます。 だから、likesテーブルに存在するpost_idを数えれば、どのpostがどのくらいいいねをもらっているかがわかり、比較できるのです。 上記記事を参考に、 @posts = Post.find(Like.group(:post_id).order('count(post_id) desc').limit(3).pluck(:post_id)) と書くことができます。 #likeモデルのpost_idカラムを指定してまとめる Post.find(Like.group(:post_id) #post_idの多い順に並べる .order('count(post_id) desc') #並び替えたものの上位3つのpost_idを抽出する .limit(3).pluck(:post_id)) タグに紐づく投稿順 こちらも、いいねと同じ考え方でいけます。 まず、postとtagを結ぶpost_tagモデルが存在します。 今回行いたいのは、postに紐づくタグの中で、より多くのpostを保有するタグを順番に並べたいです。 簡単に言うと、よく使われるタグを順番に並べたいです。 post_tagテーブルと、tagテーブルは以下の通りです。 これを合体します。 今回は、post_tagテーブルのidと、tagsテーブルのtag_idが一致するはずです。 tagに紐づいているpostの数を比較したいので、 今回はpost_tagsテーブルの、tag_idの数を集計して、多い順に並べれば良いです。 postとtagのセットの情報が、post_tagsテーブルに保管されるので、 post_tagsテーブルのtag_idで頻出のタグは、より多くのpostに紐づいていると言うことです。 実際には、 @tags = Tag.find(PostTag.group(:tag_id).order('count(tag_id) desc').limit(10).pluck(:tag_id)) と書くことができます。 #Postモデルのtag_idカラムを指定してまとめる Tag.find(PostTag.group(:tag_id) #tag_idを数え数の多い順に並び替える .order('count(tag_id) desc') #並び替えたものの上位10個のtag_idを抽出する .limit(10).pluck(:tag_id)) いいねもタグも、最終的に複数のidをpluckで取り出しているので、 @posts, @tagsなどのインスタンス変数に格納し、ビューでeach文を使用してデータを一覧表示すればOKです。 groupメソッドについても確認する必要がありました。 しっかり理解ができれば、色々簡単に応用ができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] 配列から特定の要素を 1 つだけ削除する

やりたいこと Array#delete や Array#reject を使って配列から任意の要素を削除する場合、その要素が重複している場合はすべて削除してしまう。 animals = ['?', '?', '?', '?', '?'] animals.delete('?') # destructive #=> "?" animals #=> ["?", "?", "?"] animals = ['?', '?', '?', '?', '?'] animals.reject { |animal| animal == '?' } # not destructive #=> ["?", "?", "?"] これを 1 要素のみ削除するようにしたい。 # こうしたい。 ["?", "?", "?", "?"] 方法 Array#index でインデックス値を取得し、その値を利用して要素を削除する。 animals = ['?', '?', '?', '?', '?'] animals.delete_at(animals.index('?')) # destructive #=> "?" animals #=> ["?", "?", "?", "?"] animals = ['?', '?', '?', '?', '?'] animals.reject.with_index { |_, i| i == animals.index('?') } # not destructive #=> ["?", "?", "?", "?"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アソシエーションでnilを許可する

はじめに SNSサイトを作成中、投稿にキャンプサイト(未入力or1件まで)を紐付けようと思い、以下の通りにしたところ、 キャンプサイトが未入力時に、バリデーションで弾かれ、投稿ができなかっため、対処法を残しておきます。 前提 Postモデル user_id image_id camp_site_id(FK) body CampSiteモデル site_name やりたいこと キャンプサイトの登録は任意で、Postを投稿したい やったこと camp_site.rb has_many :posts post.rb belongs_to :camp_site, optional: true "optional: true" がなかったために投稿時のcamp_site_idのnilが許可されず エラーが表示されていました。 belongs_toが成り立たなくなるため、通常では、nilは許可されないそうです。 そのため、"optional: true"を記載してあげて、nilを許可する必要があります。 終わりに 深く理解していなかったために、初歩的な段階でつまづいてしまいました。 1から復習すること、些細なことでも調べ直すことが大切だと痛感しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】アソシエーションでnilを許可する

はじめに SNSサイトを作成中、投稿にキャンプサイト(未入力or1件まで)を紐付けようと思い、以下の通りにしたところ、 キャンプサイトが未入力時に、バリデーションで弾かれ、投稿ができなかっため、対処法を残しておきます。 前提 Postモデル user_id image_id camp_site_id(FK) body CampSiteモデル site_name やりたいこと キャンプサイトの登録は任意で、Postを投稿したい やったこと camp_site.rb has_many :posts post.rb belongs_to :camp_site, optional: true "optional: true" がなかったために投稿時のcamp_site_idのnilが許可されず エラーが表示されていました。 belongs_toが成り立たなくなるため、通常では、nilは許可されないそうです。 そのため、"optional: true"を記載してあげて、nilを許可する必要があります。 終わりに 深く理解していなかったために、初歩的な段階でつまづいてしまいました。 1から復習すること、些細なことでも調べ直すことが大切だと痛感しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】投稿者のみに削除ボタン表示(メモ)

環境 Rails: 6.1.3 ruby: 3.0.0 mac: OS 前提 投稿機能、ログイン機能実装済み 方法 ビューに記述 削除ボタン設置前に、投稿者本人か確認するため、if logged_in? && current_user.id == post.user_idを記述。 今ログインしているユーザーidと投稿idが一致した場合にのみ削除ボタンが表示されます。 app/views/posts/index/html.erb <% if logged_in? && current_user.id == post.user_id %> <%= link_to '削除', post_path(post.id),method: :delete, data: { confirm: '本当にいいですか?' } %> <% end %> こちらの場合も同様の手順で投稿者のみに削除ボタンが表示されます。 app/views/comments/_comment.html.erb <% if logged_in? && current_user.id == comment.user_id %> <%= link_to '削除', [comment.post, comment],method: :delete,data: { confirm: "本当に削除してもよろしいですか?" } %> <% end %> 以上になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] Atcoder過去問 C - Replacing Integer

はじめに AtCoder過去問のC問題を解いてみました。 よろしくお願いします。 問題はこちらからご確認ください。 C - Replacing Integer まずは入力を受け取ります。 n, k = gets.split.map(&:to_i) 今回の場合、for文やeach文を使って回すと、nの入力値が大きすぎたときに、かなり負荷がかかってしまうのでできれば使いたくないです。 nからkを何回も引いていく作業は、割り算で作ることができます。 割り算のあまりを算出する%を使えば、0を超えないまで(負の数にならないまで)のnからkを引き続けた結果を得られます。 まずはそれをaに代入します。 n, k = gets.split.map(&:to_i) a = n % k 次にaが最小の数ではないかもしれません。 なぜならn % kをした後にもう一度kを引いたときに、aよりも絶対値が小さくなる可能性があるからです。 さらにkを引いたものをbに代入しておきましょう。 n, k = gets.split.map(&:to_i) a = n % k b = a - k あとはaとbの絶対値のどちらが小さいかを調べて、小さい方を出力するようにします。 aが負の数であることはあり得ないのでaは何も加工しなくても絶対値です。 bに関してはabsメソッドを使用して絶対値に変換します。 n, k = gets.split.map(&:to_i) a = n % k b = a - k if a <= b.abs puts a else puts b.abs end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コントローラー内の処理のカテゴリー

自分でもあやふな部分があるので復習も兼ねて! まずコントローラーとは? ・リクエストに対する処理をする場所 ・処理をした後クライアントにレスポンスとして返す場所 コントローラー内での処理のカテゴリー ・コントローラー内での処理のカテゴリーをアクションと言います!  基本的な7つのアクションが、、、   ・index→一覧表示(トップページ) ・new→新規投稿ページ ・create→データの投稿 ・show→個別詳細ページ ・edit→投稿編集ページ ・update→データの編集 ・destroy→データの削除 例えば、クライアントからトップページが見たああああああい!!とリクエストが来た場合はコントローラー内のindexアクションが動きます! トップページの表示がリクエストで来ていた場合の記述の仕方↓ def index end 以上!!                          
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveStorageにバリデーションを設定する方法

はじめに Ruby on Railsにおいて、ActiveStrageは画像投稿機能を実装する際、 とてもシンプルで使いやすく、重宝しています。 ただ、バリデーションの設定がデフォルトでできないようです。 どうにかしてバリデーションの設定ができないか調べてみると、 オリジナルでファイルを作成している方々がたくさんいらっしゃり、 尊敬と憧れを抱きつつも、ズボラな自分はもっと簡単に設定する方法ないの? と探してたどり着いた方法です。 それはactivestorage-validatorというgemを導入する方法です。 個人的に、普通にモデルに設定するバリデーションとほぼ同じやり方で設定できる点が使いやすいなと感じ、他の初学者の方にもオススメできると思い記事にした次第です。 導入から使い方を下記にまとめて見ました。 (今回はRails 6.0 、MySQLを使用しています。) 導入方法 まず、Gemfileに下記を記述します。 gem 'activestorage-validator' コンソールで % bundle install これで導入は完了です。 使い方 ファイル添付必須 入力フォームでファイルの添付を必須にしたい時の記述方法です。 'activestorage-validator'では、presence: trueで指定できます。 配置例 class User < ApplicationRecord has_one_attached :avatar validates :avatar, presence: true, blob: { content_type: :image } end blob:と記述するのは、ActiveStorageはBlobとAttachmentモデルを使うからです。 content_type:で添付ファイルの型を指定します。今回の例では画像なので、:imageとなります。 (オプションは:imageの他に、:audio、:video、:textを指定可能です。) 拡張子など、もっと細かく指定したい場合 配置例 class User < ApplicationRecord has_many_attached :photos validates :photos, presence: true, blob: { content_type: ['image/png', 'image/jpg', 'image/jpeg'], size_range: 1..5.megabytes } end 拡張子を細かく指定したい場合はcontent_type:で上記のように記述すれば、より細かく指定できます。 保存できるファイルの容量を指定したい場合は、size_range:で指定します。上記だと1〜5MBのファイルのみ保存可能にしています。 終わりに 以上で現時点で私が理解している機能についての説明を終わります。 まだ試していませんが、message:を使えば表示されるエラー文の指定もできるのではないかと考えたり、まだまだ細かいバリデーションを設定できる可能はあります。 active_strage_validationsというgemも似た感じでバリデーションの設定ができるようですが、 なんとなく'activestorage-validator'の方が直感的にわかりやすかったので、個人的にはこちらを使用させていただいております。 もっと詳しく知りたい方は下記参考文献よりご確認ください。 初学者ですので、理解不足な部分があると思いますが、間違いなどあれば ご指摘していただけると幸いです。 参考文献 ActiveStorageのバリデーション 【Rails 5.2】 Active Storageの使い方 activestorage-validator Active Storage Validations
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on Rails】バリデーションが効かなかったときの原因追求をした話

対象者 バリデーションを使っている方 バリデーションが効かない不具合が発生している方 目的 バリデーションを使って投稿が失敗するようにすること 実際の手順と実例 1.前提 上記記事を参考に実装 Articleモデル(記事の投稿)、Tagモデル、ArticleTagモデル(中間テーブル)実装中 title,bodyはArticleのカラム、tag_nameはTagのカラム  下記のようにバリデーションを設定したが投稿ができてしまう、、、 model/tag.rb class Tag < ApplicationRecord has_many :articles, through: :article_tags has_many :article_tags validates :tag_name, presence: true end 2.結論(解決策) 下記のようにコントローラーを変更 before app/tags_controller.rb def create @article = Article.new(article_params) @article.user = current_user tag_list = params[:article][:tag_name].split("/") if @article.save @article.tags_save(tag_list) flash.now[:notice] = "投稿されました" redirect_to article_path(@article) else @user = current_user flash.now[:alert] = "投稿に失敗しました" render :new end end after app/tags_controller.rb def create @article = Article.new(article_params) @article.user = current_user tag_list = params[:article][:tag_name].split("/") #下記の部分を追加 if tag_list == [] tag = Tag.new() tag.tag_name = "" tag.save @user = current_user flash.now[:alert] = "投稿に失敗しました" render :new #上記の部分を追加 elsif @article.save @article.tags_save(tag_list) flash.now[:notice] = "投稿されました" redirect_to article_path(@article) else @user = current_user flash.now[:alert] = "投稿に失敗しました" render :new end end ※上記のコントローラーで使用しているtags_save(tag_list)はArticleモデルで定義したものです。 app/model/articles.rb def tags_save(tag_list) if !tags.nil? article_tags_records = ArticleTag.where(article_id: id) article_tags_records.destroy_all end tag_list.each do |tag| inspected_tag = Tag.where(tag_name: tag).first_or_create #① tags << inspected_tag end end 3.原因 @article.saveで保存が行われてしまっている beforeの書き方だと先にarticleの内容が保存されてしまっており、 tag_saveも””空欄が保存され、そのまま通過してしまっている [7] pry(main)> tag.tag_name = "" => "" 空欄だと投稿は失敗ですよということを表すために、 下記の記述を追加 app/tags_controller.rb if tag_list == [] #タグが空欄だったときの条件付け tag = Tag.new() tag.tag_name = "" #タグ名が空欄であることを定義 tag.save #セーブされ、ここでバリデーションが作動する @user = current_user flash.now[:alert] = "投稿に失敗しました" render :new elsif (中略)        end バリデーションが作動するのは.saveされたとき!ということを学びました、、!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ajax]フォロー機能を非同期にした

はじめに 本記事では、フォロー機能を非同期通信にした方法を記述します。 以前にいいね機能について、非同期通信にした記事も投稿しておりますので、 ご参照ください。 前提 フォロー機能が同期通信できているものとします。 コード 早速ですが、該当箇所のコードを記載します。 コントローラー showページにフォローボタンやフォロワーを確認できるボタンがあります。 後述のモデルに合わせてfollowingsや`followersメソッドを作成します。 また、usersとfollowersなどに分けている方もいらっしゃいましたが、 私は、followingsや`followersに分けた方がわかりやすいと思ったため、こうしています。 users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) @foods = @user.foods.order("created_at DESC") end def followings user = User.find(params[:id]) @users = user.followings end def followers user = User.find(params[:id]) @users = user.followers end 省略 end relationshipsテーブルは、userとuserの関係を示す、中間テーブルになります。 また、その関係を作る(create)、壊す(destroy)の2つが必要になります。 relationships_controller.rb class RelationshipsController < ApplicationController before_action :authenticate_user! def create @user = User.find(params[:user_id]) following = Relationship.create(follower_id: params[:user_id], following_id: current_user.id) end def destroy @user = User.find(params[:user_id]) following = Relationship.find_by(follower_id: params[:user_id], following_id: current_user.id) following.destroy end end モデル 同期通信と特に変わりはありません。 user.rb class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :foods, dependent: :destroy has_many :likes, dependent: :destroy has_many :liked_foods, through: :likes, source: :food has_many :relationships, foreign_key: :following_id has_many :followings, through: :relationships, source: :follower has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id has_many :followers, through: :reverse_of_relationships, source: :following has_one_attached :icon has_many :comments def already_liked?(food) self.likes.exists?(food_id: food) end def is_followed_by?(user) reverse_of_relationships.find_by(following_id: user.id).present? end end ここも同期通信と特に変わりはありません。 relationship.rb class Relationship < ApplicationRecord belongs_to :following ,class_name: "User" belongs_to :follower, class_name: "User" end ビュー show.html.erb 記述が長いので、部分テンプレートです。 ちなみに、クラス名を class="mypage-follow-contents-<%= @user.id %>" にしないと、誰をフォローするのかわからないままですので、 エラーが出続けることになります。 <div class="mypage-intro-topright"> <div class="mypage-follow-contents-<%= @user.id %>"> <%= render partial:"relationships/relationship", lacals: {user: @user} %> </div> </div> followers.html.erbフォロワーの一覧を確認できるページ。ここはあまり関係ないと思います。 <div class="followers-contents"> <div class="row d-flex justify-content-center"> <div class="col-10 mt-5"> <h1>FOLLOWERS</h1> <h2 class="followers-contents-title">Nickname</h2> <% @users.each do |user| %> <%= link_to user_path(user) do %> <%= user.nickname %> <% end %> <% if user != current_user %> <% if user.is_followed_by?(current_user) %> <%=link_to user_relationships_path(user), method: :delete do %> <button type="button" class="btn btn btn-danger">フォロー解除</button> <% end %> <% else %> <%=link_to user_relationships_path(user), method: :post do %> <button type="button" class="btn btn btn-primary">フォロー</button> <% end %> <% end %> <% end %> <% end %> </div> </div> </div> followings.html.erbフォローしている人を確認できるページ。ここはあまり関係ないと思います。 <div class="followings-contents"> <div class="row d-flex justify-content-center"> <div class="col-10 mt-5"> <h1>FOLLOWING</h1> <h2 class="followings-contents-title">Nickname</h2> <% @users.each do |user| %> <%= link_to user_path(user) do %> <%= user.nickname %> <% end %> <% if user != current_user %> <% if user.is_followed_by?(current_user) %> <%=link_to user_relationships_path(user), method: :delete do %> <button type="button" class="btn btn btn-danger">フォロー解除</button> <% end %> <% else %> <%=link_to user_relationships_path(user), method: :post do %> <button type="button" class="btn btn btn-primary">フォロー</button> <% end %> <% end %> <% end %> <% end %> </div> </div> </div> _relationship.html.erb show.html.erbにあった部分テンプレートです。 remote: trueこれが特に大事です。 というか非同期通信のスタートがこれなんではないかというぐらい重要です。 これにより、非同期通信をOKするものになり、リクエストがjs形式になります。 そのため、後ほど出てきますが、 ファイルはjs.erbファイルということになります。 <div class="mypage-followcount-btn"> <%= link_to followings_user_path(@user) do %> <button class="btn btn-mypage-follow">フォロー <i class="fas fa-angle-right fa-position-right"> <%= @user.followings.count %> </i> </button> <% end %> <%= link_to followers_user_path(@user) do %> <button class="btn btn-mypage-follow">フォロワー <i class="fas fa-angle-right fa-position-right"> <%= @user.followers.count %> </i> </button> <% end %> <% if user_signed_in? && @user == current_user %> <div class="mypage-introduction-edit"> <%= link_to edit_user_path(current_user) do %> <button class="btn btn-mypage-introduction-edit">プロフィール編集 <i class="fas fa-user-edit"></i> </button> <% end %> </div> <% else %> <%# <% unless @user == current_user %> <div class="mypage-follow-btn"> <% if @user.is_followed_by?(current_user) %> <%= link_to user_relationships_path(@user), method: :delete, remote: true do %> <button class="btn btn-follow-delete">フォロー中</button> <% end %> <% else %> <%= link_to user_relationships_path(@user), method: :post, remote: true do %> <button class="btn btn-follow-create">フォローする</button> <% end %> <% end %> </div> <% end %> </div> create.js.erbフォロー jについては、以下にも記載したので省略します。 $('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>") 部分テンプレートにより、 前述の以下の部分に進みます。 <% if @user.is_followed_by?(current_user) %> <%= link_to user_relationships_path(@user), method: :delete, remote: true do %> <button class="btn btn-follow-delete">フォロー中</button> <% end %> <% else %> <%= link_to user_relationships_path(@user), method: :post, remote: true do %> <button class="btn btn-follow-create">フォローする</button> <% end %> <% end %> destroy.js.erbフォロー解除 create.js.erbと同じです。 $('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>") 以上です。 いかがでしょうか。 終わりに 非同期通信、終わってみれば「簡単」だと思いますが、 できるまでは難しかったなというのが、印象でした。 アプリケーションにおいて非同期化してしまった方が良いものは全て非同期化した方が良さそうですね。 以下、参考サイトです。 [Rails]Ajaxを用いて非同期でフォロー機能の実装 【Rails】Ajaxを用いた非同期フォロー機能の実装 Railsで実装したフォロー機能の非同期化がうまくいかない。 Ruby on Rails チュートリアル 明日も頑張ります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む