0

私は Rails の初心者で、それを学ぶために簡単な時間追跡アプリを作成しています。管理者のダッシュボードに、ネストされた情報を持つ多くのテーブルから大量の情報を入力したいと考えています。

データベースにクエリを実行して 1 つの会社のすべてのデータを要求し、すべてのクライアント、プロジェクト、タスク、調整、議事録のダッシュボードを表示するためのベスト プラクティスは何ですか?

データの構造は次のとおりです。

会社には_多くのクライアントがいます

クライアントの所属先の会社 has_many のプロジェクト

プロジェクトの属し_クライアントには_多くのタスクがあります

タスク属し_プロジェクトに has_many 分

分はタスクに属します


このデータ構造は本当に悪いかもしれません。知らない。

データの表示例:

Activision
-- ウェブサイトの再設計
--- 開発
---- 100 分

私はこれから始めていますが、私はきれいですが、完全に逆の可能性があります (ユーザーは会社に属しています):

    @clients = Client.find_all_by_company_id(current_user.company_id)
    @clients.each do |client| 

        project = Project.find_all_by_client_id(client.id)
        puts project.name

        project.each do |project|

            task = Task.find_all_by_project_id(project.id)
            puts task.name

        end
    end

「Rails ActiveRecord のベスト プラクティスを完全に説明している優れた本やリソースはありますか?」という質問もあると思います。

4

3 に答える 3

1

このメソッドを使用してincludes、関連付けを熱心にロードします。

ガイドからの例

Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)

あなたが言ったことに基づいて、それは次のようになります:

require 'active_record'
require 'logger'

# =====  Config  =====
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.logger = Logger.new $stdout
ActiveSupport::LogSubscriber.colorize_logging = false

# =====  Schema  =====
ActiveRecord::Schema.define do
  self.verbose = false

  create_table :clients do |t|
    t.string  :name
    t.integer :company_id
  end

  create_table :companies do |t|
    t.string :name
  end

  create_table :projects do |t|
    t.string  :name
    t.integer :client_id
  end

  create_table :tasks do |t|
    t.string  :name
    t.integer :project_id
  end

  create_table :minutes do |t|
    t.integer :quantity
    t.integer :task_id
  end
end

# =====  Classes  =====
class Company < ActiveRecord::Base
  has_many :clients
end

class Client < ActiveRecord::Base
  belongs_to :company
  has_many   :projects
end

class Project < ActiveRecord::Base
  belongs_to :client
  has_many   :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project
  has_many   :minutes
end

class Minute < ActiveRecord::Base
  belongs_to :task
end

# =====  Data  =====
Company.create! name: 'Activision' do |company|
  company.clients.build name: 'Robert Kotick' do |client|
    client.projects.build name: 'Website Redesign' do |project|
      project.tasks.build name: 'Development' do |task|
        task.minutes.build quantity: 100
      end
    end
  end
end

# =====  Querying and displaying  =====
company = Company.find_by_name 'Activision'
clients = Client.includes(projects: {tasks: :minutes}).where(company_id: company.id)

print "\n----- The query makes four requests, regardless of how much data you have. -----\n\n"
clients.inspect # do this to force loading since AR queries are lazy

print "\n----- some representation of the data (notice no queries while iterating through) -----\n\n"
clients.each do |client|
  puts client.name
  client.projects.each do |project|
    puts "-- #{project.name}"
    project.tasks.each do |task|
      puts "--- #{task.name}"
      task.minutes.each do |minute|
        puts "---- #{minute.quantity}"
      end
    end
  end
end

# =====  Output =====

# >> D, [2012-09-12T00:01:42.755414 #72855] DEBUG -- :    (0.7ms)  select sqlite_version(*)
# >> D, [2012-09-12T00:01:42.755890 #72855] DEBUG -- :    (0.2ms)  CREATE TABLE "clients" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "company_id" integer) 
# >> D, [2012-09-12T00:01:42.756327 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "companies" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)) 
# >> D, [2012-09-12T00:01:42.756728 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "projects" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "client_id" integer) 
# >> D, [2012-09-12T00:01:42.757122 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "tasks" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "project_id" integer) 
# >> D, [2012-09-12T00:01:42.757531 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "minutes" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "quantity" integer, "task_id" integer) 
# >> D, [2012-09-12T00:01:42.906877 #72855] DEBUG -- :    (0.0ms)  begin transaction
# >> D, [2012-09-12T00:01:42.909242 #72855] DEBUG -- :   SQL (0.5ms)  INSERT INTO "companies" ("name") VALUES (?)  [["name", "Activision"]]
# >> D, [2012-09-12T00:01:42.934937 #72855] DEBUG -- :   SQL (24.7ms)  INSERT INTO "clients" ("company_id", "name") VALUES (?, ?)  [["company_id", 1], ["name", "Robert Kotick"]]
# >> D, [2012-09-12T00:01:42.936110 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "projects" ("client_id", "name") VALUES (?, ?)  [["client_id", 1], ["name", "Website Redesign"]]
# >> D, [2012-09-12T00:01:42.937001 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "tasks" ("name", "project_id") VALUES (?, ?)  [["name", "Development"], ["project_id", 1]]
# >> D, [2012-09-12T00:01:42.937767 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "minutes" ("quantity", "task_id") VALUES (?, ?)  [["quantity", 100], ["task_id", 1]]
# >> D, [2012-09-12T00:01:42.938005 #72855] DEBUG -- :    (0.0ms)  commit transaction
# >> D, [2012-09-12T00:01:42.939882 #72855] DEBUG -- :   Company Load (0.1ms)  SELECT "companies".* FROM "companies" WHERE "companies"."name" = 'Activision' LIMIT 1
# >> 
# >> ----- The query makes four requests, regardless of how much data you have. -----
# >> 
# >> D, [2012-09-12T00:01:42.940458 #72855] DEBUG -- :   Client Load (0.1ms)  SELECT "clients".* FROM "clients" WHERE "clients"."company_id" = 1
# >> D, [2012-09-12T00:01:42.943272 #72855] DEBUG -- :   Project Load (0.1ms)  SELECT "projects".* FROM "projects" WHERE "projects"."client_id" IN (1)
# >> D, [2012-09-12T00:01:42.943919 #72855] DEBUG -- :   Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" IN (1)
# >> D, [2012-09-12T00:01:42.944520 #72855] DEBUG -- :   Minute Load (0.1ms)  SELECT "minutes".* FROM "minutes" WHERE "minutes"."task_id" IN (1)
# >> 
# >> ----- some representation of the data (notice no queries while iterating through) -----
# >> 
# >> Robert Kotick
# >> -- Website Redesign
# >> --- Development
# >> ---- 100

これは恐ろしいデメテルの法則違反です。構造や名前に関係なく、これらのいずれかが変更された場合は、このコードを修正する必要があります。多くの抽象化を導入せずにそれをどのように処理するかはよくわかりません。

本に関しては、たくさんありますが、正直なところ、Railsの世界は、ActiveRecordの最良のプラクティスを構成するものをまだ理解していないと思います(実際、ほとんどすべてのActiveRecordのプラクティスはひどいものだと考えるコミュニティの大部分があります-私は主にそのキャンプにいます)。

しかし、負荷の関連付けを熱心に行うために使用するという上記のようなものが必要な場合は#includes、ガイドはそのような情報を見つけるのに最適な場所です。私もこのブログとビデオを本当に楽しんだ。

于 2012-09-12T05:02:54.320 に答える
1

これはあなたが持っているのと同じものを生成します

@clients = current_user.company.clients
@clients.each do |client| 
    projects = client.projects

    # puts project.name # makes no sense here

    projects.each do |project|

        project.tasks.each do |task|
            puts task.name
        end
    end
end
于 2012-09-12T04:55:06.290 に答える
0

のようなものを試してください

 Client.includes(
                      :company =>{:projects=>:tasks})

上記のすべてが (has_one、has_many、belongs_to を介して) 接続されている必要があります。

于 2012-09-12T05:11:13.803 に答える