中级Rails:深入理解Models、Views、Controller

原文:IntermediateRails: Understanding Models, Views and Controllers

         非常高兴很多人喜欢StartingRuby on Rails: What I Wish I Knew这篇文章;现在我们来探讨一下曾令我头疼的MVC模式。这不是一篇介绍MVC模式的文章,仅仅是当你初次接触MVC时可能会遇到的问题列表。

         这是我理解的MVC模式图:

  • l  图中Browser是发送请求的,如http://mysite.com/video/show/15
  • l  Web server(mongrel,WEBrick等等)是接收请求的。它通过路由(routes)来找到处理请求的控制器;默认的路由模式是“/controller/action/:id”(定义在config/routes.rb)中。在我们的例子中,对应起来指的就是video控制器,控制器的show方法,id为15。Web服务器使用分发器(Dispatcher)来创建新的控制器,调用动作以及传入方法。
  • l  Controller(控制器)所做的工作就是解析用户的请求、提交的数据、cookies、话以及浏览器相关信息。它们相当于管理者,管理着许多员工。最好的控制器就是像Dilbert-esque:仅仅发布命令而不关注任务是如何完成的。在我们的例子中,video控制器中的show方法知道它必须去查找一个video对象。它通过与model进行交互,获取到id为15的video,最终将查找的结果显示给客户。
  • l  Model(模型)是Ruby中的类。它们与数据库进行交互,存储和验证数据,执行业务逻辑并且一些情况下做一些繁重的事情。它们是捣弄数字背后的胖小子。在我们的例子中,模型会从数据库中遍历出id为15的video。
  • l  Views(视图)是我们所看到的:HTML、CSS、XML、Javascript和JSON。它们像是推销员,按照管理者的指令来发布传单和收集数据。视图仅仅是读取控制器已有数据的傀儡。它们不知道在数据黑屋子里会发生什么。在我们例子中,控制器给了video 15数据来显示视图。显示视图生成了HTML:divs、tables、text、description、footers等等。
  • l  控制器返回响应信息(HTML、XML等等)和原数据(缓存的headers、redirects)给服务器。服务器将原数据组成HTTP响应并发送给用户。

        以故事形式来讲解‘胖模型层,瘦控制器层’比枯燥的‘3层架构’有趣的多。模型层做一些简单的工作,视图层是漂亮的脸蛋,而控制器层是背后的策划者。

        许多的MVC讨论者忽略了web服务器的角色。然而,关注控制器是如何魔法般的创建和接收用户信息的是非常重要的。Web服务器是不可见的网关,传输着来来往往的数据:用户从不会直接和控制器进行交互。

  1. 模型层

模型层在Rails中应该是胖的:它们做一些繁重的事情因此控制器能够保持瘦的、平均的并且忽略掉细节。这里是一些模型层建议:

使用ActiveRecord:

<span style="white-space:pre">	</span>class User < ActiveRecord::Base
<span style="white-space:pre">	</span>end

               “<ActiveRecord::Base”这段代码的意思是,你底层的User模型继承于ActiveRecord::Base,并且获取Rails对数据库的魔法般的查询和保存方法。

               Ruby能够很容易的捕获到未定义的方法。ActiveRecord允许像“find_by_login”这样的方法,而实际上此类方法并不存在。当你调用“find_by_login”的时候,Rails捕获到未定义的方法被调用,并搜索“login”字段。假设你的数据库中有login字段,那么模型将做基于login字段上的查询。实现此功能不需要什么额外的配置。

              定义类方法和实例方法

         def self.foo
                   “Class method”     # User.foo
         end
         def bar
                   “instance method”  # user.bar
         end

         类方法和实例方法经常容易混淆。

         user(小写字母u)是一个对象,你能够像user.save一样调用实例方法。

         User(大写字母U)是一个类方法,你不必需要一个对象来调用它(如User.find)。ActiveRecord为你的模型自动添加了实例方法和类方法。

         建议:要像User.find_latest一样定义类方法,而不是使用明确参数的User.find进行查找(瘦控制器更好)。

使用属性:

         通常的Ruby对象能够像这样定义属性:

                   # attribute in regular Ruby
                   attr_accessor :name                 # like @name
                   def name=(val)                            # custom settermethod
                            @name =val.capitalize   # clean it up beforesaving
                   end
                   def name                                      # customgetter
                            “Dearest ” + @name       # make it nice
                   end

这里是一些解释:

    • l  attr_accessor :name将为模型创建get和set方法(即name=和name)。这就像有了公共实例变量@name。
    • l  定义方法name=(val)来改变@name是如何被保存的(例如验证输入数据的合法性)。
    • l  定义方法name来控制变量的输出形式(例如改变输出格式)。
    • 在Rails中,因为数据库的魔法性属性容易被混淆。这里是一些策略:
    • l  ActiveRecord获取到数据库的所有字段,并将它们放入attributes数组中。这样可以方便的进行get和set,但是你必须通过调用user.save来保存它们。
    • l  若你想覆盖默认的get和set,使用:
# ActiveRecord: override how we access field
         def length=(minutes)
                   self[:length]= minutes * 60
         end
         def length
                   self[:length]/ 60
end

                   ActiveRecord定义了’[]’方法来获取原始的属性(包裹着write_attribute和read_attribute)。这是你如何改变你原始的数据。你能够重定义长度,如下:

           def length   # this is bad
               length / 60
           end

                   由于这是一个无限循环(这一点都搞笑)。因此self[]也是。这也是Rails经常另我头疼的地方,当你不确定的时候可以使用self[:field]。

   从不用忘记你正在使用数据库:

                    Rails是简单、干净的,以至于你会忘记你正在使用数据库。请不要忘记。

                  保存你的模型。若你做了一些修改,记得保存。忘记保存似乎是件非常容易的事情。你能够使用update_attributes(params)来传入哈希键值对,对数据库中数据进行更新。

                  当有改变时,重新加载你的模型。假设一个user对象与video对象之间的关系是has_many。你创建了一个信息的video对象,并指定正确的user,然后你希望通过user.video来获取该用户下所有相关的video信息。这样会起作用吗?

         可能不会。如果你已经从videos表中查询过数据,user.videos可能会保留旧数据。你需要调用user.reload来刷新获取到最新的查找数据。注意内存中的模型数据会被缓存。

使用新模型:

         有两种方式可以创建新对象:

                   joe = User.new(:name => “SadJoe”)    # not save

                   bob = User.create(:name =>“Happy Bob”)   # saved

    • l  User.new创建了新对象,通过哈希设置了属性。但是该对象并未保存到数据库中:你必须调用user.save来保存。若数据无效,调用save将失败。
    • l  User.create创建了一个新对象并将其保存到数据库中。若验证失败,user.errors以哈希形式保存了所有的错误信息以及详细提示信息。

注意哈希是如何被传入的。使用Ruby的大括号魔法,{}不必明确写出,因此:

                   user= User.new( :name => "kalid", :site =>"instacalc.com" )

将变为:

User.new( {:name => "kalid", :site =>"instacalc.com"} )

其中的箭头(=>)表明哈希将被传入(Ruby1.9以后可以写成name: “kalid”)。

       使用关联关系:

         假设用户有状态字段(即users表中有status字段),有多种状态:活跃、不活跃、沉闷等等。关联关系应该是怎样的呢?

                  class User < ActiveRecord::Base
                            belongs_to:status   # this?
                            has_one:status         # or this?
                  end

         你很可能认为是belongs_to:status。是的,这个听起来有点奇怪。不要困惑于”has_one”和”belongs_to”的字面意思,要理解其意义:

  • l  belongs_to:从本表链接向另一个数据表(links_to)。每个用户链接向一个状态。
  • l  has_one:从另一张表链接向本表(linked_from)。每个状态链接到一个用户。事实上,状态模型(statuses)不知道用户模型(users),在状态表中根本没有用户的信息。在Class Status(即状态模型)中,我们写入has_many :users(has_one和has_many功能相似,但前者返回所链接的一个对象,后者返回所链接的多个对象)。

备注:

  • l  belongs_to与links_to押韵
  • l  has_one与linked_from押韵
  • 上述是以韵律进行排列的,此方法对我有帮助,也希望可以帮到你。

这些关联关系实际上定义了方法来对其他类进行查找。例如,‘user belongs_to status’意味着user.status将通过status_id来查询status表。同样的,‘status has_many :users’意味着status.users将通过当前的status_id来查询user表。ActiveRecord在我们初始声明这些关系的时候,捕获并处理了这些魔法。

    使用自定义的关联关系:

         假设我们有两个状态,一个为主状态,另一个为副状态,并使用如下:

belongs_to :primary_status, :model => ‘Status’, :foreign_key=> ‘primary_status_id’                                                  
belongs_to:secondary_status, :model=>’Status’, :foreign_key=>’secondary_status_id’

         你定义一个新字段,并明确声明了模型的引用以及指向user表的外键。例如,user.primary_status返回一个Status对象且id值与primary_status_id相同。非常棒!、

  1. 控制器层:

         这部分比较简短,因为控制器不应该做太多,除了管理模型和视图外。主要处理如下一些事情:

    • l  处理一下像会话、登录验证,过滤,重定向以及错误提示等。
    • l  有一些默认的方法(被ActionController添加)。若访问http://localhost:3000/user/show将会调用show方法,若show方法未定义将会自动渲染show.html。
    • l  将实例变量(如@user)传入视图。局部变量(不以@符号开头的)将不被传送。
    • l  比较难调试。摔死render:text => “Error found” and return来打印调试你的页面。这也是为什么将大多数代码放入到model层,方便通过控制台来调试。
    • l  使用会话来存储请求间的数据:session[:variable] = “data”

   我将再次提醒(我曾因此抓狂):使用@foo而不是foo来传入数据到视图。

  1. 使用视图:

                   视图是简单明了的,基本概念:

    • l  控制器动作使用与动作名相同的视图(如show动作默认将加载show.html页面)
    • l  控制器实例变量(@foo)在做所有视图和局部视图中都可以调用(译者注:应该注意作用域)。
    • 在视图中使用ERB来运行代码:
    • l  <% … %>:运行代码,但是不输出。常用于if/then/else/end和array.each循环。你能够使用<% if false %> Hi there <% end %>来注释HTML代码。若你在%>后有内容,你将看到两行之间会有空行。(译者注:Rails3.2以上未发现,但不使用Rails框架进行ERB开发时会有)
    • l  <%- … %>(此方式以废弃):运行代码,不打印尾部新行。生成XML或JSON数据,和当你为了易读性将.html中代码分块,但又不希望输出时有空行时,使用此种方式即可。
    • l  <%= … %>:运行代码并打印返回值。例如:<%= @foo %>(@开头的变量会被传入到视图中,不是吗?)。不要将if语句放入到<%=中,会导致错误。
    • l  <%= h … %>:打印代码并对html代码进行字符化处理:>将显示为>。h()实际上是一个Ruby函数,但是不需要括号就可以调用,这是Rubyists经常的做法。

    起初,你可能会有所困惑,运行一些代码后,你将理解更清晰。

    深呼吸一下:

         对于MVC模式很难将其简短的讲明白。当你对它越来越熟悉的时候,任何Rails程序将变得容易被琢磨:更能理解每一部分的意义。MVC将保持你的代码更优秀和模块化,能更好的被调试和维护。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值