編集済み
何よりもまず、コードが機能します。ImmutableRelation
Rails 4.2.1 で実行されている非推奨メッセージが発生したり、表示されたりすることはありません。
ページ アクションのコードでは、関係を複製する必要はありません。これは、各月のステップで新しい関係を作成するためです ( with: User...
)。はい、12 のクエリを実行しますが、それは問題ではありません。
あまり重要ではありませんが、質問に 2 つのタイプミスがあります。モデルは で変更def number_first_logged_in(month)
する必要がdef self.number_first_logged_in(month)
あり、モデル名は である必要がありUser
、 ではない必要がありModel
ます。
Railsコンソールでテストしました(Products
の代わりに、フィールドの代わりにUser
使用)が、同じで正常に動作します。また、新しい Rails 4.2.1 アプリを起動し、質問のコードを (タイプミスを修正して) 使用すると、動作することを確信しています。:created_at
:first_logged_in_at
alejandro@work-one [ruby-2.1.1@rails42]: ~/rails/r42example
[09:14:04] $ rails c
Loading development environment (Rails 4.2.1)
~/rails/r42example (development) > @m = {};(0..11).each {|m| @m[m] = Product.group(:name).number_first_logged_in(Date.new(Date.today.year,m+1, 1)) }
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-01-01' AND '2015-01-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-02-01' AND '2015-02-28') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-03-01' AND '2015-03-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-04-01' AND '2015-04-30') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-05-01' AND '2015-05-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-06-01' AND '2015-06-30') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-07-01' AND '2015-07-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-08-01' AND '2015-08-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-09-01' AND '2015-09-30') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-10-01' AND '2015-10-31') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-11-01' AND '2015-11-30') GROUP BY "products"."name"
(0.1ms) SELECT COUNT(*) AS count_all, name AS name FROM "products" WHERE ("products"."created_at" BETWEEN '2015-12-01' AND '2015-12-31') GROUP BY "products"."name"
=> 0..11
しかし、問題があります。この問題は public rails API とは関係ありません。これには、これらのエラーまたは非推奨を引き起こす可能性のあるメソッドがないためです。Rails チームは、#nodoc
パブリック メソッドはパブリック API の一部ではないと述べています。ほとんどのクエリ メソッド (すべてではないにしても) には、ペア bang メソッド ( code ) があり、(クローンを返す代わりに) リレーションを変更し、InmutableRelation
エラー (または以前のバージョンでは非推奨メッセージ) を発生させます。これらのパブリックメソッドは#nodoc
、パブリック Rails API の一部ではありません。
何をすべきか?簡単ではない:
- コードでこれらの bang メソッドを検索してください。おそらく、AR で行われたモンキー パッチです。
- 使用している宝石を確認してください。おそらく、コードが機能する新しいアプリを開始し、ターゲットアプリにあるすべての宝石を追加し、失敗した場合は、再び機能するまで試行錯誤して宝石を削除します.
bang メソッドについて言及しましたが、リレーションを変更するメソッドにも当てはまります (これはパブリック API では実行できません)。モンキー パッチを探すか、リレーションを拡張する必要があります。
問題を解決するにはそれで十分なはずです。
私はあなたのコメントを読みました、そして私は要点を得ました、私はこれらのオプションだと思います:
Rails データベースの独立性は、are を通じて機能します。また、arel には、db 関数 (日付フィールドの月) を直接操作するメソッドがありません。arel を拡張して書くこともできますが、PostgreSql 用に 1 つ、MySql 用に 1 つ、Sqlite 用に 1 つ作成する必要があります。(この時点で高すぎる)
dev/test/prod で同じ db マネージャーを使用する場合は、私が提案したように部分的なテキスト クエリを使用できます。(これはあなたが好きではありません)
12 個のクエリを保持します (これで問題ないと思います)。
group_by に専用フィールドを追加します (year_month の可能性があります)。(非常に厳密で、変更が難しい)
私があなただったら、私は次のようなことをします:
class User
scope :for_current_year, -> { where(created_at: Date.today.beginning_of_year..Date.today.end_of_year }
end
コントローラーページのアクションで使用できます:(それを使用することをお勧めします)
User.for_current_year
.group("date_trunc('month', users.created_at)", "usergroup").count
次のパターンのハッシュを返します: (詳細はこちら)
{
[<first date of the month of created_at>, <usergroup>] => count,
...
}
@months
しかし、以前と同じものを取得したい場合は、結果を ruby でマッピングする必要があります。
def page
@months = User.for_current_year
.group("date_trunc('month', users.created_at)", "usergroup").count
.map { |k,v| {k[0].month => {k[1] => v}} }
end
注 1: このコードは PostgreSQL で機能します。関数を使用するためdate_trunc(...)
です。MySql で使用する必要がある場合は、month(users.created_at)
代わりに使用します。k[0]
MySql でマッピングする場合は、の代わりに使用する必要がありますk[0].month
。
注 2:group
返されたハッシュのキーに 2 つの値が必要なため、呼び出しではフィールドのパラメーターが分離されています。