在habtm上使用polymorphic关联
程序员文章站
2022-03-15 18:16:14
...
我们知道,在rails中,habtm和polymorphic都是非常好的东东,特别是后者,可以说是rails的ORM中非常强大的功能。《Agile Web Development with Rails》虽然详细讲述了habtm和polymorphic关联,也讲了如何把模型类映射为连接表,但是并没有讲如何把habtm和polymorphic一起使用做一个介绍。这里我就来简单说一下。
还是出自真实项目中的需求,项目是全国大学本科教学评估支持系统,说白了就是大学用来支持本科教学评估的东西。里面有这样一个use case:
评估中需要召开座谈会,参加的人有:专家、教师、学生...(反正就是学校里面的各色人等),并且参加座谈会的各种人都可以是多个。这样就有了这样一个多对多的数据关系:多个人可以参加多个座谈会,参加者的类别是不一样的(需要polymorphic)。
让我们仔细看看这个use case,其实它还是挺微妙有趣的。如果不考虑polymorphic,那么这里显然就是一个标准的habtm关系,那么我们就建立三张表users、symposia和symposia_users,然后两边用habtm关联一下就好了。
但是如果要考虑polymorphic,就没有这么简单了。这里最主要的问题是habtm本身不支持polymorphic选项,支持polymorphic选项的只有belongs_to。那么我们就不得不把habtm拆成has_many和belongs_to两部分了。
先创建关联表attendances及其响应的model:
其中的attendee就会多态地关联到专家、教师、学生等人员身上。
然后再配置symposium.rb、expert.rb、teacher.rb、student.rb:
以上代码都比较好理解,就是其中的source和source_type选项可能不是很常见,看看ActiveRecord的doc就会明白,source用来指定关联到attendance的哪个属性上(当然就是attendee),而source_type则是在使用了polymorphic的情况下指定attendee的具体类型。
经过以上的配置,整个model的关系就建立起来了,不过在使用这些关系的时候仍需注意一点。假设我们要新建一个座谈会,参与者有专家1、2,教师1、2和学生1、2,代码要怎么写呢?很多人可能都会想是:
不过这样写是不对的,会得到如下错误:
ActiveRecord::HasManyThroughCantAssociateNewRecords: Cannot associate new records through 'Symposium#attendances' on '#'. Both records must have an id in order to create the has_many :through record associating them.
意思是说symposium和teacher(expert、student)之间的关联要通过attendance,而现在还没有attendance呢(即both records must have an id)。那么要如何先把要用的attendance创建出来呢?对,在建立关系前先创建并保存symposium,这样就间接地提前创建了需要用的attendance。
还是出自真实项目中的需求,项目是全国大学本科教学评估支持系统,说白了就是大学用来支持本科教学评估的东西。里面有这样一个use case:
评估中需要召开座谈会,参加的人有:专家、教师、学生...(反正就是学校里面的各色人等),并且参加座谈会的各种人都可以是多个。这样就有了这样一个多对多的数据关系:多个人可以参加多个座谈会,参加者的类别是不一样的(需要polymorphic)。
让我们仔细看看这个use case,其实它还是挺微妙有趣的。如果不考虑polymorphic,那么这里显然就是一个标准的habtm关系,那么我们就建立三张表users、symposia和symposia_users,然后两边用habtm关联一下就好了。
但是如果要考虑polymorphic,就没有这么简单了。这里最主要的问题是habtm本身不支持polymorphic选项,支持polymorphic选项的只有belongs_to。那么我们就不得不把habtm拆成has_many和belongs_to两部分了。
先创建关联表attendances及其响应的model:
# create_attendances.rb-------------------------------------- create_table :attendances do |t| t.column :attendee_id, :integer t.column :attendee_type, :string t.column :symposium_id, :integer end # attendance.rb---------------------------------------------- class Attendance < ActiveRecord::Base belongs_to :attendee, :polymorphic => true belongs_to :symposium end
其中的attendee就会多态地关联到专家、教师、学生等人员身上。
然后再配置symposium.rb、expert.rb、teacher.rb、student.rb:
# symposium.rb------------------------------------------------ class Symposium < ActiveRecord::Base has_many :attendances has_many :experts, :through => :attendances, [b]:source => :attendee, :source_type => 'Expert'[/b] has_many :teachers, :through => :attendances, :source => :attendee, :source_type => 'Teacher' has_many :students, :through => :attendances, :source => :attendee, :source_type => 'Student' end # teacher.rb-------------------------------------------------- class Teacher < ActiveRecord::Base has_many :attendances, :as => :attendee has_many :symposia, :through => :attendances end # expert.rb和student.rb与teacher.rb相同------------------------
以上代码都比较好理解,就是其中的source和source_type选项可能不是很常见,看看ActiveRecord的doc就会明白,source用来指定关联到attendance的哪个属性上(当然就是attendee),而source_type则是在使用了polymorphic的情况下指定attendee的具体类型。
经过以上的配置,整个model的关系就建立起来了,不过在使用这些关系的时候仍需注意一点。假设我们要新建一个座谈会,参与者有专家1、2,教师1、2和学生1、2,代码要怎么写呢?很多人可能都会想是:
symposium = Symposium.new(:name => "座谈会1") symposium.experts << Expert.find(1) << Expert.find(2) symposium.teachers << Teacher.find(1) << Teacher.find(2) symposium.students << Student.find(1) << Student.find(2) symposium.save
不过这样写是不对的,会得到如下错误:
ActiveRecord::HasManyThroughCantAssociateNewRecords: Cannot associate new records through 'Symposium#attendances' on '#'. Both records must have an id in order to create the has_many :through record associating them.
意思是说symposium和teacher(expert、student)之间的关联要通过attendance,而现在还没有attendance呢(即both records must have an id)。那么要如何先把要用的attendance创建出来呢?对,在建立关系前先创建并保存symposium,这样就间接地提前创建了需要用的attendance。
begin Symposium.transaction do symposium = Symposium.new(:name => "座谈会1") symposium.save! [b]# 这句很重要[/b] symposium.experts << Expert.find(1) << Expert.find(2) symposium.teachers << Teacher.find(1) << Teacher.find(2) symposium.students << Student.find(1) << Student.find(2) symposium.save! end rescue # 处理事务中的异常... end
上一篇: ZooKeeper基础
下一篇: 分布式之Eureka
推荐阅读
-
在服务器上安装、使用MySQL的注意事项_MySQL
-
什么是ORM,以及在php上的使用?
-
使用CSS3在触屏上为按钮实现激活效果
-
Unity中使用GL在Camera上绘制文字
-
-在SAE上使用duxcms-该如何解决
-
在Linux上使用Wine安装轻聊版的QQ的步骤讲解
-
使用SWFupload 上傳檔案,在firefox 下,下載檔名出現錯誤
-
网页中多个图标在一张图片上,使用css将各图标显示_html/css_WEB-ITnose
-
在使用@angular/cli创建的angular项目上添加postcss等一系列移动端自适应插件
-
在React中使用Native如何实现自定义下拉刷新上拉加载的列表