3

这是一个 Rails/ActiveRecord 问题。

我有一个模型,它基本上必须代表事件或表演。每个事件都有许多属性:属性基本上类似于“在这个事件中,人 X 有角色 Y”。

我得出的结论是,允许用户编辑此数据的最佳方法是提供一个需要结构化格式的自由文本字段,我将其称为角色字符串

singer: Elvis Costello, songwriter: Paul McCartney, ...

我使用自动完成功能来完成角色名称(歌手、词曲作者......)和人名。角色和人员都存储在数据库中。

为了实现这一点,我在 Event 模型中创建了一个虚拟属性:

def role_string
    # assemble a role string from the associations in the model
end

def role_string=(s)
    # parse a string in the above role string format,
    # look up the People and Events mentioned, and update
    # what's in the database
end

这一切都很好。当角色字符串格式正确并且角色字符串给出的关联都检查出来时,整个事情运行得很好。

但是如果角色字符串格式不正确怎么办?好的,我想,我可以使用正则表达式和标准验证来检查格式:

validates_format_of :role_string, :with => /(\w+:\s*\w+)(,\s*\w+:\s*\w+)*/

但是如果角色字符串隐含的关联无效怎么办?例如,如果我给上面的角色字符串,并且Elvis Costello没有引用一个有效的人,会发生什么?

我想,好吧,我可以使用validates_each属性:role_string来查找关联,如果给定的名称之一不匹配任何内容,则抛出错误。

我的问题有两个:首先,我不喜欢这种方法,因为要验证关联,我必须解析字符串并查找它们,这重复了我自己要做的事情role_string=,除了实际将关联保存到数据库。

其次,......我如何表明在分配给这个虚拟属性时发生了错误?

4

1 回答 1

3

首先,您错误地将 Person 归因于 Event。相反,您应该将人员的 ID 传递给事件,而不是人员姓名的字符串。例如,如果一个 ID 为 230404 且名字为“Elvis Costello”的人将他的名字改为“Britney Spears”怎么办?好吧,如果发生这种情况,ID 将保持不变,但名称会改变。但是,您将无法再引用该人。

您应该设置关联,以便外键在多种情况下引用人员:

has_one :singer, :class_name => "Person", :foreign_key => "singer_id"
has_one :songwriter, :class_name => "Person", :foreign_key => "songwriter_id"

这样,您可以让多个人员在不同的角色下与一个事件关联,并且您可以引用 Person 可能具有的多个属性。例如:

Event.first.singer.name   # => "Elvis Costello"
Event.first.songwriter.name   # => "Britney Spears"

您可以研究关联的可用验证 ( validates_associated),以及表单中是否存在 ID ( validates_presence_of)。我建议创建您自己的自定义验证,以确保 Person 在 before_save 之前有效。类似(未经测试):

def before_save
  unless Person.exists?(self.songwriter_id)
    self.errors.add_to_base("Invalid songwriter. Please try again!")
    return false
  end
end

另外,我注意到您正在寻找一种方法让用户选择应该用于您的事件中角色的用户。以下是您可以在部分表单中执行的操作:

select(:event, :singer_id, Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => 'None' })

希望这可以帮助!

于 2008-12-01T15:11:10.823 回答