高校野球の優勝回数を県別にまとめてみた

県別甲子園優勝回数
D3.jsで日本地図を描き、県別の優勝回数データと連携させビジュアライズしてみた。
こうしてみると優勝旗が白河の関を越えていないのがはっきりわかる。
(東北勢の優勝が条件なので北海道の優勝は入れないらしい)
また開催地である兵庫県に近い地域に優勝が集中しているのもわかる。(滋賀県頑張れ)
九州では宮崎県だけ春夏ともまだ優勝がない。

【参考】
D3.jsで日本地図を作成しデータを反映するサンプル(コロプレス地図) | Tips Note by TAM

また、これを作るにあたってweb上のデータを色々見たが、「朝鮮、台湾、満州」という地域があり驚いた。
戦前は外地からも甲子園に参加していたのは知らなかった。

台湾代表の活躍を描いたこちらの映画、見てみよう。
kano1931.com

Active Recordの関連付け

Active Recordの関連付けを理解するために、Railsガイドの以下のページを自分用にまとめた。 あくまで自分で理解するためのまとめなので関連付けを学びたい人は以下のページを見たほうがよいです。

https://railsguides.jp/association_basics.html

Active Record の関連付け (アソシエーション)

1 関連付けを使用する理由

関連付けをすることで、コード内で行われる操作を簡単にできる。

例) 顧客モデル: Customer 注文用モデル: Order ・1人の顧客は、多くの注文を行うことができる。

各モデル

class Customer < ActiveRecord::Base
end
class Order < ActiveRecord::Base
end

関連付けなしの場合に、既存の顧客のために新しい注文を1つ追加する

@order = Order.create(order_date: Time.now, customer_id: @customer.id)

顧客を削除する。 注文を消してから顧客を削除する必要がある。

@orders = Order.where(customer_id: @customer.id)
@orders.each do |order|
    order.destroy
end
@customer.destroy

Active Recordの関連付けを使用して、2つのモデルにつながりがあることを 明示的に宣言することで、モデル操作が一貫する。

class Customer < ActiveRecord::Base
    has_many :orders, dependent: destroy
end
class Order < ActiveRecord::Base
    belongs_to :customer
end

関連付けされた場合の既存顧客への新しい注文追加方法。

@order = @customer.orders.create(order_date: Time.now)

関連付けされた場合の既存顧客の削除方法。顧客の注文もまとめて削除される。

@customer.destroy

2 関連付けの種類

Railsでサポートされている関連付けは以下の6種類

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

2.1 belongs_to関連付け

belongs_to関連付けを行うと、他のモデルとの間に「1対1」の繋がりが設定される。

  • 顧客(customer)
  • 注文(order) 1つの注文につき正確に1人の顧客だけを割り当てたい場合
class Order < ActiveRecord::Base
    belongs_to :customer
end

customerは「単数形」

2.2 has_one関連付け

has_oneも他のモデルとの間に「1対1」の関連付けを設定するが、belongs_toと違い、その宣言が行われているモデルのインスタンスが、他方のモデルのインスタンスを「まるごと含んでいる」または「所有している」ことを示す。

  • 供給者(supplier)
  • アカウント(account) 供給者1人につきアカウントを1つだけ持つ
class Supplier < ActiveRecord::Base
    has_one :account
end

2.3 has_many 関連付け

has_many関連付けは、他のモデルとの間に「1対多」の繋がりがあることを示す。 has_many関連付けが使用されている場合、反対側のモデルではbelongs_toが使用されることが多い。 has_many関連付けが行われているモデルのインスタンスは、反対側のモデルの「0個以上の」インスタンスを所有する。

  • 顧客(customer)
  • 注文(order)
class Customer < ActionRecord::Base
    has_many :orders
end

ordersのように「複数形」にする必要がある

2.4 has_many :throush関連付け

has_many :throush関連付けは、他方のモデルと「多対多」の繋がりを設定する場合に使用される。 2つのモデルの間に「第3のモデル」(結合モデル)が介在する。モデルに対応するテーブルは「中間テーブル」と呼ばれる。 相手モデルの「0個以上」のインスタンスとマッチする。

  • 患者(patient)
  • 医師(physician)
  • 診察予約(appointment)

患者は複数の医師にかかることができる。 医師は複数の患者を見ることができる。

class  Physician < ActiveRecord::Base
    has_many :appointments
    has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
    belongs_to :physician
    belongs_to :patient
end
class Patient < ActiveRecord::Base
    has_many :appointments
    has_many :physicians, through: :appointments
end

患者と医師の関連性を考えれば自ずと結合モデルの名前も連想できる(はず)

Rails1~2の頃はphisicians_patientsのような命名規則が推奨されていたらしいが、今は極力テーブル名(モデル名)自体に意味を持たせる形が推奨されている。

ネストしたhas_many関連付けを介して「ショートカット」を設定することができる。

  • document
  • section
  • paragraph

1つのdocumentは多数のsectionを持つ。 1つのsectionの下に多くのparagraphがある。

class Document < ActiveRecord::Base
  has_many :sections
  has_many :paragraphs, through: :sections
end
class Section < ActiveRecord::Base
  belongs_to :document
  has_many :paragraphs
end
class Paragraph < ActiveRecord::Base
  belongs_to :section
end

documentは配下のparagraphsに以下のようにアクセスできる

@document.paragraphs

2.5 has_one :through関連付け

has_one :through関連付けは、他のモデルとの間に1対1の繋がりを設定する。 2つのモデルの間に「第3のモデル」(結合モデル)が介在する。これによって、相手モデルの1つのインスタンスとマッチする。

  • 提供者(supplier)
  • アカウント(account)
  • アカウント履歴(account_history)

1人の提供者が1つのアカウントに関連付けられる。 1つのアカウントが1つのアカウント履歴に関連付けられる。

class Supplier < ActiveRecord::Base
  has_one :account
  has_one :account_history, through: :account
end
class Account < ActiveRecord::Base
  belongs_to :supplier
  has_one :account_history
end
class AccountHistory < ActiveRecord::Base
  belongs_to :account
end

2.6 has_and_belongs_to_many関連付け

has_and_belongs_to_many関連付けは、他方のモデルと「多対多」の繋がりを作成する。 through:を指定した場合と異なり、第3のモデル(結合モデル)が介在しない。 ただし結合用のテーブルは必要。

  • 完成品(assembly)
  • 部品(part)

1つの完成品に多数の部品が対応。 1つの部品にも多数の完成品が対応。

class Assembly < ActiveRecord::Base
  has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
  has_and_belongs_to_many :assemblies
end

2.7 belongs_toとhas_oneのどちらを選ぶか?

2つのモデルの間に1対1の関係を作りたい場合、いずれか一方のモデルにbelongs_toを追加し、 もう一方のモデルにhas_oneを追加する必要がある。 では、どちらの関連付けをどちらのモデルに置けばよいのか?

  • 外部キー(foreign key)をどちらに置くか テーブルに外部キーを置いた方のモデルにbelongs_toを追加する。(これだけでは決まらない)
  • has_oneは「所有している」ことを表す。主語となるモデルが目的語となるモデルを所有している、と考える。

  • ◯供給者がアカウントを持っている

  • ☓アカウントが供給者を持っている
class Supplier < ActiveRecord::Base
  has_one :account
end
class Account < ActiveRecord::Base
  belongs_to :supplier
end

マイグレーション中では外部キーは以下のように指定する

t.integer :supplier_id

Rails4以上ならば以下のように記述できる

t.references :supplier

2.8 has_many :throughとhas_and_belongs_to_manyのどちらを選ぶか?

「多対多」のリレーションシップ宣言にどちらを選ぶか?

  • has_and_belongs_to_many

簡単に記述可能。関連付けを直接指定できる。 リレーションシップのモデルで何か特別なことをする必要がないならば結合モデルの不要な こちらを選ぶべき。ただし、専用の結合テーブルはDB上に作る必要がある。

  • has_many :through

結合モデルを使用した間接的な関連付けとなる。 リレーションシップのモデル自体を独立したエンティティとして扱いたい(両モデルの関係そのものについて処理を行いたい)のであればこちらを選ぶべき。 結合モデルでvalidation, callback, 追加の属性が必要であればこちらを使用。

2.9 ポリモーフィック関連付け

ポリモーフィック関連付けを使用すると、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現することができる。

  • 写真(picture)
  • 従業員(employee)
  • 製品(product)

写真モデルは従業員モデルと製品モデルの両方に従属する。

class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
  has_many :pircures, as: :imageable
end
class Product < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

@employee.picturesとすると、写真のコレクションをEmployeeモデルのインスタンスから取得できる。 @product.picturesとすれば写真のコレクションをProductモデルのインスタンスから取得できる。 Pictureモデルのインスタンスがあれば、@picture.imageableで親を取得できる。ポリモーフィックインターフェイスを使用するモデルで、外部キーのカラムと型のカラムを両方とも宣言しておく必要がある。

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string :name
      t.integer :imageable_id
      t.string :imageable_type
      t.timestamps
    end
  end
end

t.referencesを使用すると更にシンプルにできる。

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string :name
      t.references :imageable, polymorphic: true
      t.timestamps
    end
  end
end

controllerも含めた具体的な実装方法

http://ruby-rails.hatenadiary.com/entry/20141207/1417926599

2.10 自己結合

自分自身に関連付けられる必要のあるモデルに使用。

  • 従業員(employee)

同じ従業員の中で、マネージャーと部下の関係を追えるようにしておきたい。

class Employee < ActiveRecord::Base
  has_many :subordinates, class_name: "Employee",
                          foreign_key: "manager_id"

  belongs_to :manager, class_name: "Employee"
end

上のように宣言しておくと、@employee.subordinates@employee.managerが使用できるようになる。

マイグレーション及びスキーマでは、モデル自身にreferencesカラムを追加する。

class CreateEmployees < ActiveRecord::Migration
  def change
    create_table :employees do |t|
      t.references :manager
      t.timestamps
    end
  end
end

3 ヒントと注意事項

Active Recordの関連付けを効率的に使用するためには、以下について知っておく必要がある。

  • キャッシュ制御
  • 名前衝突の回避
  • スキーマの更新
  • 関連付けのスコープ制御
  • 双方向関連付け

3.1 キャッシュ制御

関連付けのメソッドは、全てキャッシュを中心に構築されている。最後に実行したクエリの結果はキャッシュに保持され、次回以降の操作で使用される。

customer.orders         # DBからordersを取得
customer.orders.size    # ordersのキャッシュコピーが使用される
customer.orders.empty?  # ordersのキャッシュコピーが使用される

では、キャッシュのではなくDBから再度読み込むにはどうすればよいか?

customer.orders         # DBからordersを取得
customer.orders.size    # ordersのキャッシュコピーが使用される
customer.orders(true).empty?  # ordersのキャッシュコピーが破棄される。
                              #その後DBから再度読み込まれる

3.2 名前衝突の回避

関連付けを作成すると、モデルにその名前のメソッドが追加される。 ActiveRecord::Baseのインスタンスで既に使用されているような名前を関連付けに使用すると、基底メソッドが上書きされる。 例として、attributesconnectionは関連付けに使ってはいけない。

3.3 スキーマの更新

関連付けを使用するにはその設定に合わせてDBのスキーマを常に更新しておく必要がある。

  • belongs_to関連付けを使用する場合は、外部キーを作成する必要がある。
  • has_and_belongs_to_many関連付けを使用する場合は、適切な結合テーブルを作成する必要がある。

3.4 関連付けのスコープ制御

Active Recordモデルをモジュール内で宣言している場合など、異なるスコープ内で定義されている場合は、関連付けの宣言で完全なクラス名を指定する必要がある。

3.5 双方向関連付け

関連付けは、通常双方向で設定する。2つのモデル両方に関連を定義する必要がある。

class Customer < ActiveRecord::Base
  has_many :orders
end

class Order < ActiveRecord::Base
  belongs_to :customer
end

Active Recordは、これら双方向関連付け同士に繋がりがあることをデフォルトでは認識しない。これによって以下のようにオブジェクトの2つのコピー同士で内容が一致しなくなることがある。

c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => false

なぜこういうことが起こるのかというと、co.customerは同じデータがメモリ上で異なる表現となっており、一方が更新されても他方が自動で更新されないため。 Active Recordの:inverse_ofオプションを使用すればこれらの関係を通知することができる。

inverse of ~ : 「~の逆」の意味

class Customer < ActiveRecord::Base
  has_many :orders, inverse_of: :customer
end

class Order < ActiveRecord::Base
  belongs_to :customer, inverse_of :orders
end

上のように変更することで、Active Recordはcustomerオブジェクトのコピーを1つだけ読み込むようになる。 不整合を防ぎ、アプリケーションの効率も高まる。

c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => true

inverse_ofの制限

  • :through関連付けと併用することはできない
  • :polymorphic関連付けと併用することはできない
  • :as関連付けと併用することはできない
  • belongs_to関連付けの場合、has_manyの逆関連付けは無視される

以下のオプションを設定した関連付けでは、逆関連付けは自動的には設定されない

  • :conditions
  • :through
  • :polymorphic
  • :foreign_key

rails tutorial 1周した

転職してからrailsアプリのフロント作業がメインの仕事になった。
railsの知識はない前提で入社はしたものの、知らないままでは仕事の範囲が狭いままになってしまうので、良いと評判のrails tutorialをやっている。
1/23(月) rails tutorial 1週目終わり。ほぼテストも含めて一通り写経した。

今後の予定は、
・2月末までにrails tutorial 2週目を終わらせる。2周目はテストは書かずにコピペする。
・3月末までにrails tutorial 3週目を終わらせる。3週目はテストも書く。

実務とrails tutorialのやりこみで覚える。