简体   繁体   English

在 Ruby on Rails 中扩展 model 用于 RESTful api 应用程序

[英]Extend model in Ruby on Rails for RESTful api app

As a part of a project we got an UML diagram to implement.作为项目的一部分,我们得到了一个 UML 图来实现。 Now I'm at the part of OOP, but I'm not sure how to implement that in Ruby. Every Task is a Chore or Homework, as I understand it must be multi table inheritance(MTI).现在我在 OOP 的部分,但我不确定如何在 Ruby 中实现它。每个任务都是杂务或家庭作业,据我了解它必须是多表继承(MTI)。 I'm not sure how to implement the relations between users table -> chores/hw's tables, also tasks->chores/hw's tables.我不确定如何实现用户表之间的关系 -> chores/hw 的表,以及 tasks->chores/hw 的表。 Also, it will be good to know how to implement the CRUD actions (create\update\destroy) Hope somebody can help.另外,最好知道如何实现 CRUD 操作(创建\更新\销毁)希望有人能提供帮助。 Thanks.谢谢。 Here is the diagram: Diagram这是图表:图表

models/person.rb
class Person < ApplicationRecord
  has_many :tasks, as: :owner, class_name: "::Task"
end

models/task.rb
class Task < ApplicationRecord
  belongs_to :owner, polymorphic: true

  enum status: {
    active: 0,
    done: 1
  }

  scope :chores, -> { where(type: "Tasks::Chores")}
  scope :homeworks, -> { where(type: "Tasks::Homework")}
end

models/tasks/chores.rb
class Tasks::Chores < Task
  enum size: {
    small: 0,
    medium: 1,
    large: 2
  }
end

models/tasks/homework.rb
class Tasks::Homework
end

Welcome to Stackoverflow !欢迎来到计算器!

I haven't tried this code but it should be correct.我没有尝试过这段代码,但它应该是正确的。

Task has a polymorphic owner , so it can be a Person , or any other model, this will add 2 columns owner_type and owner_id . Task有一个多态的owner ,所以它可以是一个Person ,或者任何其他 model,这将添加 2 列owner_typeowner_id

A task also has a type (STI) which can be either Tasks::Chores or Tasks::Homework (which I namespaced for clarity)任务也有一个type (STI),它可以是Tasks::ChoresTasks::Homework (为清楚起见,我命名了它)

Using the scopes on the Task model you should be able to call person.tasks.chores or person.tasks.homeworks使用Task model 上的范围,您应该能够调用person.tasks.choresperson.tasks.homeworks

Even though your UML schema violates Dependency Inversion principle when implement it in Rails and violates 2NF即使您的 UML 模式在 Rails 中实现时违反了依赖倒置原则并且违反了2NF

But you can do it like this:但你可以这样做:

You need to create two tables people and tasks您需要创建两个表peopletasks

You can do this by adding these migrations:您可以通过添加这些迁移来做到这一点:

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

After that you need to create these models:之后你需要创建这些模型:

# 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

And finnaly you are able to create you tasks:最后你可以创建你的任务:

person = Person.create(name: 'John Doe', email: 'john.doe@test.test', 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

If you need to be able to know what task is chore and what task is homework you need to add type field to the tasks table so that to be able to determine subtask如果您需要能够知道什么任务是chore ,什么任务是homework ,您需要将type字段添加到tasks表中,以便能够确定子任务

Update:更新:

To avoid violating Dependency Inversion principle you can do next:为避免违反依赖倒置原则,您可以执行以下操作:

Separate Task , Chore and Homework to different tables since Chore and Homework have very different fieldsTaskChoreHomework分开到不同的表中,因为ChoreHomework的字段非常不同

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

And than your models:而不是你的模型:

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

And you can manipulate those models like this:您可以像这样操作这些模型:

person = Person.create(name: 'John Doe', email: 'john.doe@test.test', 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

Note: But this approach is not suitable if you will need to add more different tasks.注意:但如果您需要添加更多不同的任务,则此方法不适用。 Because all the time you will have a new task type you will have to add new table.因为您总是会有新的任务类型,所以您将不得不添加新表。 This only works for your current example.这仅适用于您当前的示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM