ActiveRecord 使用回调和 STI 的问题
嘿伙计们,以下是 Rails 和 STI 的问题:
我有以下课程:
class Account < AC::Base
has_many :users
end
class User < AC::Base
extend STI
belongs_to :account
class Standard < User
before_save :some_callback
end
class Other < User
end
end
module STI
def new(*args, &block)
type = args.dup.extract_options!.with_indifferent_access.delete(:type)
if type.blank? or (type = type.constantize) == self
super(*args, &block)
else
type.new(*args, &block)
end
end
end
现在的问题是: 如果不重写 User.new
(在模块 STI 中),则 User::Standard
内的回调永远不会被调用,否则 account_id 始终为 nil
如果我这样创建用户:
account.users.create([{ :type => 'User::Standard', :firstname => ... }, { :type => 'User::Other', :firstname => ... }])
如果我对模块使用不同的方法,例如:
module STI
def new(*args, &block)
type = args.dup.extract_options!.with_indifferent_access.delete(:type)
if type.blank? or (type = type.constantize) == self
super(*args, &block)
else
super(*args, &block).becomes(type)
end
end
end
那么实例变量不会共享,因为它正在创建一个新对象。 在不将回调移至父类并检查类的类型的情况下,是否有任何解决方案?
格瑞兹 马里奥
Hey folks, following problem with Rails and STI:
I have following classes:
class Account < AC::Base
has_many :users
end
class User < AC::Base
extend STI
belongs_to :account
class Standard < User
before_save :some_callback
end
class Other < User
end
end
module STI
def new(*args, &block)
type = args.dup.extract_options!.with_indifferent_access.delete(:type)
if type.blank? or (type = type.constantize) == self
super(*args, &block)
else
type.new(*args, &block)
end
end
end
And now the problem:
Without rewriting User.new
(in module STI), the callback inside User::Standard
gets never called, otherwise the account_id is always nil
if I create users this way:
account.users.create([{ :type => 'User::Standard', :firstname => ... }, { :type => 'User::Other', :firstname => ... }])
If I'm using a different approach for the module like:
module STI
def new(*args, &block)
type = args.dup.extract_options!.with_indifferent_access.delete(:type)
if type.blank? or (type = type.constantize) == self
super(*args, &block)
else
super(*args, &block).becomes(type)
end
end
end
Then instance variables are not shared, because it's creating a new object.
Is there any solution for this problem without moving the callbacks to the parent class and checking the type of class?
Greetz
Mario
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
也许有些东西我不知道,但我从未见过以这种方式定义的 Rails STI 类。通常它看起来像...
app/models/user.rb:
app/models/users/standard.rb:
app/models/users/other.rb:
看起来好像你正在合并类范围(类“所在的位置”) “相对于其他类、模块、方法等)具有类继承(由“类标准<用户”表示)。 Rails STI 关系涉及继承,但不关心范围。也许您正在尝试通过嵌套继承的类来完成一些非常具体的事情,而我只是想念它。但如果不是,则可能会导致您出现一些问题。
现在专门讨论回调。标准中的回调不会被调用,因为“account.users”关系使用的是 User 类,而不是 Standard 类(但我认为您已经知道这一点)。有几种方法可以处理这个问题(我将在示例中使用我的类结构):
一:
这将强制所有 account.users 使用 Standard 类。如果您需要其他用户的可能性,那么...
二:
三:
只需在代码中手动调用 Users::Standard.create() 和 Users::Other.create() 即可。
我确信还有很多其他方法可以实现此目的,但可能有最简单的方法。
Maybe there's something I don't know, but I've never seen Rails STI classes defined in that manner. Normally it looks like...
app/models/user.rb:
app/models/users/standard.rb:
app/models/users/other.rb:
It looks as though you are conflating class scope (where a class "lives" in relation to other classes, modules, methods, etc.) with class inheritance (denoted by "class Standard < User"). Rails STI relationships involve inheritance but do not care about scope. Perhaps you are trying to accomplish something very specific by nesting inherited classes and I am just missing it. But if not, it's possible it's causing some of your issues.
Now moving on to the callbacks specifically. The callback in Standard isn't getting called because the "account.users" relationship is using the User class, not the Standard class (but I think you already know that). There are several ways to deal with this (I will be using my class structure in the examples):
One:
This will force all account.users to use the Standard class. If you need the possibility of Other users, then...
Two:
Three:
Just call Users::Standard.create() and Users::Other.create() manually in your code.
I'm sure there are lots of other ways to accomplish this, but there are probably the simplest.
因此,在将实例变量移动到 @attributes 并使用模块
STI
的第二种方法后,我解决了问题:现在我的实例变量(密码)被复制到新的
User::Standard
对象以及回调和验证正在运行。好的!但这只是一种解决方法,而不是真正的解决方案。 ;)So I solved my problems after moving my instance variables to
@attributes
and using my second approach for the moduleSTI
:Now my instance variable (the password) is copied to the new
User::Standard
object and callbacks and validations are working. Nice! But it's a workaround, not really a fix. ;)