论坛首页 编程语言技术论坛

美就是生产力(Rails美学宣言阐释)

浏览 39525 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-04-26  
hehe,甭管怎样,喜欢看。快继续吧
0 请登录后投票
   发表时间:2006-04-26  
自Rails1.1 :through出现后has_and_belongs_to_many基本处于半作废状态, 只有少数情况才会用到(比如自引用多对多).
0 请登录后投票
   发表时间:2006-04-26  
robbin 写道
potian 写道
robbin 写道
potian 写道
我的建议是你看RubyForRails


我也这样觉得,要是先看programming ruby,学习速度太慢了,谢谢指点。

不客气
我指的是一本另外的书


已经用骡子拽下来了,粗粗一看,似乎是专为没有ruby背景的rails程序员写的,挺合适,就是可读性没有agile 那本书那么好。

RubyForRails 是哪本书?
0 请登录后投票
   发表时间:2006-04-26  
既然cookoo提到了,我们顺便看看自引用,所谓自引用,就是一个表中的对象相互引用,看看典型的组织机构,首先是简单的多对多




为了实现这个结构,我们定义如下模型
class Organization < ActiveRecord::Base
  has_and_belongs_to_many :children,
    :class_name => 'Organization',
    :join_table => 'org_relationships',
    :foreign_key => 'parent_id',
    :association_foreign_key => 'child_id'

  has_and_belongs_to_many :parents,
    :class_name => 'Organization',
    :join_table => 'org_relationships',
    :foreign_key => 'child_id',
    :association_foreign_key => 'parent_id'
end

这下读不了了 (: , class_name是指对方属于什么类,我们知道不管Parent还是child都是Organization,连接表当然也是在同一个表中org_relationships,这个表有两个字段,parent_id, child_id.

从parent的角度来看,他是通过parent_id来选取的,譬如where parent_id = 自己的id,和他相关的children是通过child_id来得到的

SELECT * FROM organizations INNER JOIN orgrelationships ON organizations.id = orgrelationships.child_id WHERE (orgrelationships.parent_id = 自己的id ) 



ok,我们来建立上边这种组织机构

china = Organization.create(:name => "china")
jiangsu =  Organization.create(:name => "jiangsu")  
zhejiang = Organization.create(:name => "zhejiang")
account =  Organization.create(:name => "accout")

china.children << [jiangsu, zhejiang, account]


shaoxin = Organization.create(:name => "shaoxin")
hangzhou = Organization.create(:name => "hangzhou")
zhejiang_account = Organization.create(:name => "zhejiang account")

zhejiang.children <<[shaoxin, hangzhou, zhejiang_account]
account.children << [zhejiang_account]

hangzhou_account = Organization.create(:name => "hangzhou_account")
hangzhou_account.parents << [hangzhou , zhejiang_account]


我们来看看是不是正确:
def show_relationship(org)
  puts "parents:"
  unless org.parents.empty?
    puts org.parents.map {|parent| parent.name}.join ","
  end
   
  puts "children:"
    unless org.children.empty?
     puts org.children.map {|child| child.name}.join ","
    end
end

当然代码可以写得更简单,但是给大家看得明白点
show_relationship(china)
parents:
children:
jiangsu,zhejiang,accout

show_relationship(zhejiang)
parents:
china
children:
shaoxin,hangzhou,zhejiang account

show_relationship(zhejiang_account)
parents:
zhejiang,account
children:
hangzhou account


复杂吧,其实还好,就是两个关系声明,你知道我们前面的所有事情照样可以做:
Organization.find_by_name("china").children.find_by_name("zhejiang")

等等等等

当然,这还是太简单了,让我们来看看accountability的实现
0 请登录后投票
   发表时间:2006-04-27  
这个组织机构实在够混乱了,我们整理整理


好复杂呦,真的吗?真的很复杂吗?(想起了以前Javaeye一个著名的帖子)

在看下面的代码之前,先想一下该如何实现,当然,我也想了好久(有2-3分钟之久),不过这个图花的时间可长了,不知道能不能在Java雇一个专业画图手,可惜不能在这里放白班

OK,废话少说


class Accountability < ActiveRecord::Base
  belongs_to :parent,  :foreign_key => "parent_id", :class_name => "Organization"
  belongs_to :child,   :foreign_key => "child_id",   :class_name => "Organization"
end

class Organization < ActiveRecord::Base
  has_many :accountabilities_as_parent, :foreign_key => 'parent_id', :class_name => 'Accountability'
  has_many :accountabilities_as_child,   :foreign_key => 'child_id',   :class_name => 'Accountability'
  has_many :parents,  :through => :accountabilities_as_child
  has_many :children,    :through => :accountabilities_as_parent 
end



不要被吓到,只要每个人跟我念一遍,Rational公司就会倒闭

CREATE TABLE `accountabilities` (
  `id` int(11) NOT NULL auto_increment,
  `parent_id` int(11) default NULL,
  `child_id` int(11) default NULL,
  `acctype` varchar(20) default NULL,
  PRIMARY KEY  (`id`)
)

首先我得承认自己偷懒了,acctype直接变成一个string
Accountability这个类我懒得念了。看看Organization,

1 class Organization < ActiveRecord::Base
2  has_many :accountabilities_as_parent, :foreign_key => 'parent_id', :class_name => 'Accountability'
3  has_many :accountabilities_as_child,   :foreign_key => 'child_id',   :class_name => 'Accountability'
4  has_many :parents,  :through => :accountabilities_as_child
  has_many :children,    :through => :accountabilities_as_parent 
5 end


有一点小小的弯头,由于我们是自引用的,所以4,5两句话has_many :through没地方好through,但是我们可以通过has_many模拟出来,说到底,through就是为了能够建立连接关系,当我们需要连接的时候,需要一个外键引用而已,如果我要得到某个人的子,那么就需要parent_id = 自己的id,所以4行,through accountabilities_as_parent,而accountabilities_as_parent则使用外键parent_id

SELECT organizations.* FROM organizations INNER JOIN accountabilities ON organizations.id = accountabilities.child_id WHERE (accountabilities.parent_id = 自己的id
) 


好了好了,咱们来实证

china = Organization.create(:name => "china")
jiangsu =  Organization.create(:name => "jiangsu")  
zhejiang = Organization.create(:name => "zhejiang")
account =  Organization.create(:name => "account")

zhejiang_account = Organization.create(:name => "zhejiang account")


不过这一次,可不能乱来了

china和zhejiang,jiangsu,都是regional的关系
Accountability.create(:parent=>china, :child=>jiangsu, :acctype =>"regional")
Accountability.create(:parent=>china, :child=>zhejiang, :acctype =>"regional")

但是china和account是organizational关系
Accountability.create(:parent=>china, :child=>account, :acctype =>"organizational")

而zhejiang和zhejiang_account是organizational关系

Accountability.create(:parent=> zhejiang, :child=> zhejiang_account, :acctype =>"organizational")


而account和zhejiang_account是functional的关系
Accountability.create(:parent=> account, :child=> zhejiang_account, :acctype =>"functional")


太多了,就到这里,哪位看官有兴趣自己来
china.children.find_by_name("account")
zhejiang_account.parents.find_by_name("account")


等等等等

打住,你是否想寻找某一个父中某种类型的子,或者某个子中某个类型的父呢,OK,实在是太简单了,记得我们前面在关系上增加新方法吗,其实,你也可以直接加到模型的类方法,这里我们要寻找的结果是orgnization,所以加到Organization类里面
class Organization < ActiveRecord::Base
  has_many :accountabilities_as_parent, :foreign_key => 'parent_id', :class_name => 'Accountability'
  has_many :accountabilities_as_child,   :foreign_key => 'child_id',   :class_name => 'Accountability'
  has_many :parents,  :through => :accountabilities_as_child
  has_many :children,    :through => :accountabilities_as_parent 
########
  def self.by_type(type)
      find(:all, :conditions => ["accountabilities.acctype = ?", type])
  end
########
end

看到我加的了吧
china.children.by_type("regional")
zhejiang_account.parents.by_type("organizational")


爽吧,美吧

不过记得在写其他代码的时候不要不由自主地has_many :through,你的编译器可不认
0 请登录后投票
   发表时间:2006-04-27  
potian, 在看你最后的使用演示之前我也看得云里雾里的(主要是一开始看到accountability想到的是responsibility然后就困惑模型里这个到底指什么了...) :through一开始我也觉得很方便, 但是后来发现它会屏蔽:class_name和:foreign_key就比较郁闷了. 用has_many模拟的办法比较巧妙啊. 学习~

不过回来说这个数据模型, 地区这棵树和帐户混在一起有点怪怪的, 呵呵.
0 请登录后投票
   发表时间:2006-04-27  
不好意思,我指的是财务部门

Accountability是老马的成名作
0 请登录后投票
   发表时间:2006-04-27  
potian 写道
不好意思,我指的是财务部门

Accountability是老马的成名作


失败, 看来得开始看analysis pattern了, 以前总是以为经验不足看不懂.
0 请登录后投票
   发表时间:2006-05-01  
我花了2个月出头,从学习ruby到做出一个完全ajax的网站。
做网站以前连对html,javascript,dom,ajax等基本的web概念都非常不熟悉,然而开发进度又比较紧张,所以也无法在将这些基础补充完毕之后再进行开发,只能边做边学。现在回想起来,这样效率也比较高,就是中间过程比较辛苦一些。在去ruby-cn.org这里将中文翻译的ruby参考文档大体浏览完毕,然后就可以看Agile这本书了,看完Agile之后最好就立刻开始动手,做一个简单的程序,比如blog,相册之类。程序完毕之后,可以再返回来阅读programming ruby和ruby for rails,尤其是后者,这样可以加深对ruby和rails本身的认识,同时对于rails的提高建议阅读Rails Recipes进行rails进阶。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics