Techioz Blog

RESTful API アプリ用に Ruby on Rails でモデルを拡張する

概要

プロジェクトの一環として、実装する UML 図を入手しました。今私は OOP の部分にいますが、Ruby でそれを実装する方法がわかりません。 マルチテーブル継承(MTI)でなければならないと理解しているので、すべてのタスクは雑用または宿題です。 ユーザーテーブル -> chores/hw のテーブル、タスク -> chores/hw のテーブル間の関係を実装する方法がわかりません。 また、CRUD アクション (create) の実装方法を知っておくとよいでしょう。 誰かが助けてくれることを願っています。 ありがとう。 図は次のとおりです。 ダイアグラム

解決策

UML スキーマが Rails で実装する際に依存性逆転の原則に違反し、2NF に違反している場合でも、

しかし、次のようにすることもできます。

人とタスクの 2 つのテーブルを作成する必要があります

これを行うには、次の移行を追加します。

class CreatePeople < ActiveRecord::Migration[7.0]
  def change
    create_table :people do |t|
      t.string :name
      t.string :email
      t.string :fav_prog_lang
      t.timestamps
    end
  end
end
class CreateTasks < ActiveRecord::Migration[7.0]
  def change
    create_table :tasks do |t|
      t.references :owner, null: false, foreign_key: { to_table: :people }
      t.text :description
      t.integer :size, default: 0
      t.string :course
      t.datetime :due_date
      t.string :details
      t.integer :status, default: 0
      t.timestamps
    end
  end
end

その後、これらのモデルを作成する必要があります。

# app/models/person.rb

class Person < ApplicationRecord
  has_many :tasks, foreign_key: :owner_id
end
# app/models/task.rb

class Task < ApplicationRecord
  belongs_to :owner, class_name: 'Person', foreign_key: :owner_id

  enum status: { active: 0, done: 1 }
end
# app/models/chore.rb

class Chore < Task
  enum size: { small: 0, medium: 1, large: 2 }
end
# app/models/homework.rb

class Homework < Task
  enum size: { small: 0, medium: 1, large: 2 }, _prefix: true
end

そして最後に、タスクを作成できます。

person = Person.create(name: 'John Doe', email: '[email protected]', fav_prog_lang: 'Ruby')

Homework.create(owner: person, course: 'Some course', due_date: 10.days.since, details: 'Some details')

Chore.create(owner: person, size: :medium, description: 'Some description')

person.tasks

どのタスクが雑用で、どのタスクが宿題であるかを知る必要がある場合は、タスク テーブルにタイプ フィールドを追加して、サブタスクを判断できるようにする必要があります。

アップデート:

依存関係の逆転の原則に違反しないようにするには、次のことを実行できます。

家事と宿題はフィールドが大きく異なるため、タスク、家事、宿題を別のテーブルに分離します。

class CreateTasks < ActiveRecord::Migration[7.0]
  def change
    create_table :tasks do |t|
      t.references :owner, null: false, foreign_key: { to_table: :people }
      t.integer :status, default: 0

      t.timestamps
    end
  end
end
class CreateChores < ActiveRecord::Migration[7.0]
  def change
    create_table :chores do |t|
      t.references :task, null: false, foreign_key: true
      t.text :description
      t.integer :size, default: 0
      t.timestamps
    end
  end
end
class CreateHomeworks < ActiveRecord::Migration[7.0]
  def change
    create_table :homeworks do |t|
      t.references :task, null: false, foreign_key: true
      t.string :course
      t.datetime :due_date
      t.string :details
      t.timestamps
    end
  end
end

そしてあなたのモデルよりも:

class Person < ApplicationRecord
  has_many :tasks, foreign_key: :owner_id
  has_many :chores, through: :tasks
  has_many :homeworks, through: :tasks
end
class Task < ApplicationRecord
  belongs_to :owner, class_name: 'Person', foreign_key: :owner_id
  has_many :chores
  has_many :homeworks

  enum status: { active: 0, done: 1 }
  
  accepts_nested_attributes_for :chores, :homeworks
end
class Chore < ApplicationRecord
  belongs_to :task
  enum size: { small: 0, medium: 1, large: 2 }
end
class Homework < ApplicationRecord
  belongs_to :task
end

そして、これらのモデルを次のように操作できます。

person = Person.create(name: 'John Doe', email: '[email protected]', fav_prog_lang: 'Ruby')

person.tasks.create(
  status: :done, chores_attrubutes: [{ size: :medium, description: 'Some description' }]
)

person.tasks.create(homeworks_attrubutes: [{ course: 'Some course', due_date: 10.days.since, details: 'Some details' }])


person.chores

person.homeworks

# and you can have also all tasks

person.tasks

注: ただし、このアプローチは、さらにさまざまなタスクを追加する必要がある場合には適していません。新しいタスク タイプが常に存在するため、新しいテーブルを追加する必要があります。これは現在の例でのみ機能します。