質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

851閲覧

railsで関連テーブルの合計値順で一覧表示したい

yuta66jp

総合スコア11

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

1クリップ

投稿2020/03/26 04:03

前提・実現したいこと

ユーザー一覧ページに保有ポイントの合計値が多い順に表示したいです。
pointはuserと別のテーブルでrewardsテーブルにpointカラムとしてあります。
関連テーブルのカラムの合計値順に表示することは可能でしょうか。
お願い致します。

発生している問題・エラーメッセージ

いくつかのサイトを参考にし試してみましたがエラーになります。

ActiveRecord::StatementInvalid in Users#index SQLite3::SQLException: misuse of aggregate: sum(): SELECT DISTINCT "users"."id" FROM "users" LEFT OUTER JOIN "rewards" ON "rewards"."user_id" = "users"."id" ORDER BY sum(rewards.point) LIMIT ? OFFSET ?

該当のソースコード

class UsersController < ApplicationController # ログイン済みユーザーにのみアクセスを許可する(deviseのメソッド) before_action :authenticate_user!, except: [:index, :show] before_action :correct_user, only: [:edit, :update] def index #@users = User.all @users = User.eager_load(:rewards).order("sum(rewards.point)").page(params[:page]) end
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # attachmentメソッド(gem:refile)使用 attachment :profile_image # アソシエーション設定 has_many :diaries, dependent: :destroy has_many :body_weights, dependent: :destroy has_many :rewards, dependent: :destroy has_many :diary_comments, dependent: :destroy
class Reward < ApplicationRecord # アソシエーション設定 belongs_to :user # バリデーション設定 # numericalityは属性に数値のみが使われているか検証(デフォルトでnillは弾く) # only_integerで整数のみを許可 validates :point, numericality: { only_integer: true } validates :issue_reason, presence: true # enum機能の定義 enum issue_reason: { 新規登録: 0, ログイン: 1, 日記投稿:2, フォロー: 3, 目標達成: 4 } # point(定数)の定義 Point = [30, 5, 10, 10, 100] # 取得ポイントの合計値を計算 def self.total_point self.all.sum(:point) end # ポイント取得メソッドの定義 def self.give_point(user,reason) create( user_id: user.id, point: Point[reason], issue_reason: reason ) end
<div class="row"> <% users.each do |user| %> <div class="col-xs-3"> <div class="user-index center"> <%= link_to user_path(user) do %> <%= attachment_image_tag user, :profile_image, size: "100x100", class: "img-circle", fallback: "no_image.jpg" %><br> <%= user.name %> <% end %> <p>フォロー:<%= link_to user.follower.count, user_relationships_path(user_id: user.id, index: "follower") %></p> <p>フォロワー:<%= link_to user.followed.count, user_relationships_path(user_id: user.id, index: "followed") %></p> </p> <p><%= user.rank_status %>(<%= user.rewards.total_point %>point)</p> <% if user.public_status == "公開" %> <p>身長<%= user.height %>cm</p> <p>目標体重<%= user.goal_weight %>kg</p> <% else %> <p>身長:非公開</p> <p>目標体重:非公開</p> <% end %> <p>日記投稿数:<%= link_to user.diaries.count, diaries_path(index: "user", id: user.id) %>件</p> <% if user_signed_in? %> <% if current_user != user %> <% if current_user.following?(user) %> <%= link_to "フォローを外す", user_relationships_path(user.id), { method: :delete, class: "btn btn-danger" } %> <% else %> <%= link_to "フォローする", user_relationships_path(user.id), { method: :POST, class: "btn btn-success" } %> <% end %> <% end %> <% end %> </div> </div> <% end %> </div> <div class="center"> <%= paginate users, class: "paginate" %> </div>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

どの程度お力になれるかわかりませんが…。
まず、これを実装する上では若干の設計変更をしたほうが良いように思います。
具体的にはUserテーブルにtotal_pointsというカラムを追加するのが丸いように思えます。
実際に動かしたわけではないのであれですが、方針が参考になればと思い、回答させていただく次第です。

以下、「こんな感じで動いてほしいお祈りコード」になります。

class UsersController < ApplicationController # ログイン済みユーザーにのみアクセスを許可する(deviseのメソッド) before_action :authenticate_user!, except: [:index, :show] before_action :correct_user, only: [:edit, :update] def index # total_pointsで並べ替え @users = User.eager_load(:rewards).order(total_points: "DESC").page(params[:page]) end
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # attachmentメソッド(gem:refile)使用 attachment :profile_image # アソシエーション設定 has_many :diaries, dependent: :destroy has_many :body_weights, dependent: :destroy has_many :rewards, dependent: :destroy has_many :diary_comments, dependent: :destroy # 追加するカラムはinteger、整数のみ許可 validates :total_point, numericality: { only_integer: true } # 一応、カラムを追加するときは以下の記述をmigrationに  t.integer :total_point, null: false, default: 0
class Reward < ApplicationRecord # アソシエーション設定 belongs_to :user # バリデーション設定 # numericalityは属性に数値のみが使われているか検証(デフォルトでnillは弾く) # only_integerで整数のみを許可 validates :point, numericality: { only_integer: true } validates :issue_reason, presence: true # enum機能の定義 enum issue_reason: { 新規登録: 0, ログイン: 1, 日記投稿:2, フォロー: 3, 目標達成: 4 } # point(定数)の定義 Point = [30, 5, 10, 10, 100] # ポイント取得メソッドの定義 def self.give_point(user,reason) # まずpointを設定 point = Point[reason] self.create( user_id: user.id, point: point, issue_reason: reason ) # ユーザーテーブルに設定したカラムにポイントを追加 user.total_point += point end

どうでしょうか、この形なら難しい実装なくポイント順の並び替えを実現できると思うのですが…。
また関係ないのですが、私だったらこう書くかな、みたいなところを少しだけ。

class Reward < ApplicationRecord #中略 # enum機能の定義 # 日本語で機能を記述するとき、このあたりはview(基本はdecorator)で日本語にするロジック書いてます。 enum issue_reason: { sign_up: 0, sign_in: 1, diary:2, follow: 3, achievement: 4 } # メソッドは利用したいコントローラーへ移植 end class SomeController < ActionController def someaction # 適当なところでサブメソッド呼び出し give_point(user, reason) end private # ポイント取得のサブメソッドを定義 def give_point(user, reason) # point(定数)の定義 <- hashにしたほうがマジックナンバーを使わなくてすむのでいい感じかと。また、モデルで定義したenumをそのまま使えるという恩恵も。 Point = {sign_up: 30, sign_in: 5, diary: 10, follow: 10, achievement: 100} # まずpointを設定 point = Point[reason] Record.create( user_id: user.id, point: point, issue_reason: reason ) # ユーザーテーブルに設定したカラムにポイントを追加 user.total_point += point end

…いや、どうでしょう、趣味もあるかもしれませんし、そもそもこの書き方のほうがよろしくないという説もあります。
各種コントローラーに対して横断的な処理をしたい場合には向きませんし、そもそもレコードの追加は当該のモデルに紐づくコントローラーから行われるべきで、、、何より動かない可能性も高いです。妄想お祈りコードなので。

…はい、スルーしてもらって大丈夫です。
ただ、以下の噂が気になったので…。
・Railsではクラスメソッドを多用するのはよろしくないらしい。その場合、設計がおかしい説もあるという噂。scopeでかければいいんですが、modelでは効かないので…。
・マジックナンバー(なんの数字かわからないけど、なんかあるみたいな数字)を使うのはエンジニアの間では好まれないという噂。私は怒られたことあります。

長文になってしまいましたが、こんなところでしょうか。
あくまで「方針の参考」として受け取っていただければ幸いです。
動かなかったらごめんなさい。
石とかぶつけないでくださいね。

投稿2020/04/08 14:16

take77

総合スコア130

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuta66jp

2020/04/09 11:21

回答ありがとうございます! Userテーブルにカラムを追加し実装してみます。 また各コードについてもレビュー頂きありがとうざいます! まだ、勉強始めたばかりで分からない点もございますので勉強させて頂きます。
take77

2020/04/09 23:03

お役に立てたようで良かったです。 お互いにがんばりましょう。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問