在Rails管理员

时间:2016-01-10 09:33:07

标签: ruby ruby-on-rails-4 rails-admin

我有两个模型,它们通过has_and_belongs_to_many关系相互关联。

class Band < ActiveRecord::Base
    has_and_belongs_to_many :stages, association_foreign_key: :stage_number
end

class Stage < ActiveRecord::Base
    has_and_belongs_to_many :bands
end

假设两个表都有一个id字段,那个阶段有一个stage_name字段。

他们通过名为bands_stages的表格相互关联,其架构看起来与此类似:

create_table :bands_stages, id: false do |t|
    t.integer :band_id
    t.integer :stage_number
end

我的目的是使用Rails管理员允许我们修改Stage上的某些字段,但每次运行时,我都会遇到SQL错误:

  

stages.id不存在

似乎Rails管理员默认选择错误的列来加入。我如何通知Rails管理员我希望它加入我的连接表中实际存在的列?

请注意,我实际上无法使用stages表中的ID。目的是在任何给定时间仅存在十个阶段,由其阶段编号表示,但每个阶段可以访问每个阶段。由于ID会自动增加,因此它似乎更安全,更明确地表示其更倾向于利用更具体的:stage_number字段。

3 个答案:

答案 0 :(得分:4)

我确信这不是rails admin的问题,而是habtm的关联。 要使habtm使用sql主键中的右列必须为stage模型和外键指定关联。 这是让它正常工作的唯一方法。

$date = Carbon::now()->addHours('1');

$query->where('notified', false)
      ->whereDate('start', '=', $date->format('Y-m-d'))
      ->whereRaw('hour(start) <= ?', [$date->format('H')])
      ->whereRaw('minute(start) <= ?', [$date->format('i')]);

但我认为最好的方法是使用联合模型和has_many / belongs_to,因为对于has_many / belongs_to,可以通过:primary_key选项将任何列设置为主键。

class Stage < ActiveRecord::Base
    self.primary_key = "stage_number"

    has_and_belongs_to_many :bands, foreign_key: :stage_number
end

更新:请注意,在这种情况下,仍然无需为舞台表指定任何主键。例如,我的迁移是:

class BandStageLink < ActiveRecord::Base
    self.table_name = "bands_stages"

    belongs_to :band
    belongs_to :stage, foreign_key: :stage_number, primary_key: :stage_number
end
class Band < ActiveRecord::Base
    has_many :band_stage_links
    has_many :stages, through: :band_stage_links, foreign_key: :stage_number
end
class Stage < ActiveRecord::Base
    has_many :band_stage_links, primary_key: :stage_number, foreign_key: :stage_number
    has_many :bands, through: :band_stage_links
end

我测试了rails 4.2.5的两个案例,一切正常。

答案 1 :(得分:3)

编辑 - 我确实误解了主键位,我认为希望告诉Rails使用不同的属性作为PK,这应该比重新设置自动增量更少问题-by-default PK ID。在这种情况下,Stage模型应包含self.primary_key = "stage_number",此答案底部的其余详细信息与HABTM相关。当然,多次使用仍然是我首选的解决方案。

我认为模型和方法存在比Rails Admin更大的问题。

如果我理解你要做的事情,那么你还需要关闭stages表中主键的自动增量,以保持任意数字(代表阶段号)作为主键标识。它可能会很快结束,所以我建议反对它。

如果数据真的是静态的(有10个阶段),您甚至可以将其保持为Band模型中的常量并完全废弃Stage(除非还有更多),例如

class Band < ActiveRecord::Base
  POSSIBLE_STAGES = [1, 2, ...]
  validates :stage, inclusion: { in: POSSIBLE_STAGES, message: "%{value} is not a stage we know of!" }
end

对于基于表格的方法,我建议多次使用,它将在未来为您节省很多痛苦(即使您不需要连接表上的其他属性,例如嵌套表单比HABTM更容易使用。像这样:

class Band < ActiveRecord::Base
  has_many :events
  has_many :stages, through :events
  # band details go into this model
end
class Event < ActiveRecord::Base
  belongs_to :band
  belongs_to :stage
  # you could later add attributes here, such as date/time of event, used_capacity, attendee rating, and
  # even validations such as no more than X bands on any given stage at the same time etc.
end
class Stage < ActiveRecord::Base
  has_many :events
  has_many :bands, through :events
  # stage number/details go into this model
end

迁移可能看起来像这样:

create_table :bands do |t|
  t.string :bandname
  # whatever else
end
create_table :events do |t|
  t.belongs_to :band
  t.belongs_to :stage
  # you could add attributes here as well, e.g. t.integer :used_capacity
end
create_table :stages do |t|
  t.integer :number
  t.integer :total_capacity
  # whatever else
end

正如您所看到的那样,根本没有触及主键ID,我总是避免将业务数据存储在Rails和数据库的任何类型的管道中(这是我认为的ID,它们就在那里)确保关系数据库中数据的关系/完整性,以及与ActiveRecord的良好一致的映射 - 所有业务数据应该与实际属性不同,而不是用于连接模型的管道。)

如果您仍然想要HABTM并重新使用主ID,那么我认为Stage应该包含一个foreign_key语句,以便将自己“宣传”到bands_stages联接表中自定义键名(仅限bands_stages),同时将association_foreign_key保留在Band端,以显示您要在联接表中查询的内容以到达另一方。 stages表仍会使用id作为主键,您只想关闭t.integer :id, :options => 'PRIMARY KEY'之类的自动增量(可能取决于数据库的风格 - 以及再次,我会建议不要这样做。)

你的模型看起来像这样:

class Band < ActiveRecord::Base
  has_and_belongs_to_many :stages, association_foreign_key: "stage_number"
end
class Stage < ActiveRecord::Base
  has_and_belongs_to_many :bands, foreign_key: "stage_number"
end

bandsbands_stages之间的关联将是bands.id = bands_stages.band_id,其中会找到许多bands_stages.stage_number,并且每个都会通过{stage连接到bands_stages.stage_number = stages.id {1}}(其中stages.id已被重新用于代表未来可能面临的商业数据)。

答案 2 :(得分:2)

association_foreign_key值更改为字符串而不是符号。

class Band < ActiveRecord::Base
 has_and_belongs_to_many :stages, association_foreign_key: 'stage_number'
end

class Stage < ActiveRecord::Base
  has_and_belongs_to_many :bands, foreign_key: 'stage_number'
end