通过 Rails 3 中的行为模拟 has_and_belongs_to_many 嵌套
所以 Rails 不支持 :through 通过 habtm 关系进行关联。有一些插件可以在 Rails 2.x 中添加此功能,但我使用的是 Rails 3 / Edge,并且只需要一个特定模型的关联。所以我想我应该用阿雷尔的美丽来证明自己。
首先,模型:
class CardSet < ActiveRecord::Base
has_and_belongs_to_many :cards, :uniq => true
end
class Card < ActiveRecord::Base
has_and_belongs_to_many :card_sets, :uniq => true
has_many :learnings, :dependent => :destroy
end
class Learning < ActiveRecord::Base
belongs_to :card
end
我想获得属于特定卡组的所有学习内容,:through =>牌。
这就是我到目前为止在 CardSet 模型中所拥有的:
def learnings
c = Table(Card.table_name)
l = Table(Learning.table_name)
j = Table(self.class.send(:join_table_name, Card.table_name, CardSet.table_name))
learning_sql = l.where(l[:card_id].eq(c[:id])).where(c[:id].eq(j[:card_id])).join(j).on(j[:card_set_id].eq(self.id)).to_sql
Learning.find_by_sql(learning_sql)
end
它给了我(该死的,Arel 太漂亮了!):
SELECT `learnings`.`id`, `learnings`.`card_id`, `learnings`.`user_id`, `learnings`.`ef`, `learnings`.`times_seen`, `learnings`.`next_to_be_seen`, `learnings`.`interval`, `learnings`.`reps`, `learnings`.`created_at`, `learnings`.`updated_at`, `card_sets_cards`.`card_set_id`, `card_sets_cards`.`card_id` FROM `learnings` INNER JOIN `card_sets_cards` ON `card_sets_cards`.`card_set_id` = 1 WHERE `learnings`.`card_id` = `cards`.`id` AND `cards`.`id` = `card_sets_cards`.`card_id`
这非常接近我的目标 - 只需要在卡片中添加
表中的FROM 语句部分。
我的问题是:我已经浏览了 Arel 源代码,但我怎么也想不出如何添加另一个表。
作为旁注,除了渲染到 sql 并使用 find_by_sql 运行查询之外,是否有更好的方法通过 ActiveRecord 运行来自 Arel 的结果(通常返回 Arel::Relation,这是我不想要的)正在做?
So Rails doesn't have support for :through associations through a habtm relationship. There are plugins out there that will add this in for Rails 2.x, but I'm using Rails 3 / Edge, and only need the association for one particular model. So I thought I'd stump it out myself with the beauty that is Arel.
First, the models:
class CardSet < ActiveRecord::Base
has_and_belongs_to_many :cards, :uniq => true
end
class Card < ActiveRecord::Base
has_and_belongs_to_many :card_sets, :uniq => true
has_many :learnings, :dependent => :destroy
end
class Learning < ActiveRecord::Base
belongs_to :card
end
I want to get all the learnings that belong to a particular card set, :through => cards.
This is what I have so far in my CardSet model:
def learnings
c = Table(Card.table_name)
l = Table(Learning.table_name)
j = Table(self.class.send(:join_table_name, Card.table_name, CardSet.table_name))
learning_sql = l.where(l[:card_id].eq(c[:id])).where(c[:id].eq(j[:card_id])).join(j).on(j[:card_set_id].eq(self.id)).to_sql
Learning.find_by_sql(learning_sql)
end
which gives me (damn, Arel is beautiful!):
SELECT `learnings`.`id`, `learnings`.`card_id`, `learnings`.`user_id`, `learnings`.`ef`, `learnings`.`times_seen`, `learnings`.`next_to_be_seen`, `learnings`.`interval`, `learnings`.`reps`, `learnings`.`created_at`, `learnings`.`updated_at`, `card_sets_cards`.`card_set_id`, `card_sets_cards`.`card_id` FROM `learnings` INNER JOIN `card_sets_cards` ON `card_sets_cards`.`card_set_id` = 1 WHERE `learnings`.`card_id` = `cards`.`id` AND `cards`.`id` = `card_sets_cards`.`card_id`
which is oh so close to what I'm aiming for - just needs to add in the cards
table in the FROM part of the statement.
And there's my question: I've looked through the Arel source, and for the life of me I can't figure out how to add in another table.
As a sidenote, is there a better way to run the result from Arel (which usually returns an Arel::Relation, which I don't want) through ActiveRecord, other than rendering to sql and running the query with find_by_sql as I'm doing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我对 Arel 不熟悉,但它不会处理:
I'm not familiar with Arel, but won't it handle:
在我了解了 Rails 如何封装 Arel 之后,这比我上面写的更容易,因此您不需要直接构建查询。
如果您尝试在 CardSet 模型中存根嵌套 habtm,又名:
那么您可以使用它来模拟行为:
然后像这样的查询将起作用:
This is easier than what I wrote above, after I learned how Rails encapsulates Arel so you don't need to build the query directly.
If you're trying to stub nested habtm in the CardSet model, a.k.a.:
then you can use this to simulate the behaviour:
then a query like this will work: