rails笔记 activerecord 2

扩展的activerecord

act_as_list

act_as_list : one-to-many对象关系是通过list(默认是set)来完成, 既然有list, 就有了排序,首先对应表中必须有position(自动默认)字段,用来标示排序,如果不默认就得显示通过:order指定

class Parent < ActiveRecord::Base
    has_many :children, :order => :position
end
class Child < ActiveRecord::Base
    belongs_to :parent
    acts_as_list :scope => :parent_id
end

这里的:scope=> :parent_id 说明了list是相对于单个parent_id的, 否则就是所有的product共用一个排序

对应关系建立以后, child对象会增加如下move_lower,move_higher, move_to_top,move_to_bottom,还会有first?和last?方法

注意 对对象作了排序操作以后, 只是修改数据库中的记录, parent对象还不会在list中调整, 必须调用parent.reload

act_as_tree

和act_as_list类似,例子如下

class Category < ActiveRecord::Base
    acts_as_tree :order=> "name"
end

实际上这个代码等同于===>

class Category < ActiveRecord::Base
    belongs_to :parent,
        :class_name => "Category"
    has_many :children,
        :class_name => "Category",
        :foreign_key => "parent_id",
        :order => "name",
        :dependent => true
end

通过children操作, 如有必要,可以通过:counter_cache=> true 并添加children_count来优化子对象数量获取

aggregation hibernate的 component

简单的说就是把几个字段映射为一个字段,方法为新建立一个对象 class Name attr_reader :first,:last end 然后在主对象中使用composed_of

class XX < ActiveRecord::Base
    composed_of :name, :class_name=>Name,:mapping=>
    [
    [:first_name,:first]
    [:last_name,:last]
    ]
end

其中:class_name和mapping可省略 ???

aggregation还可以用来聚合单一字段,下面的类就聚合了一个字段(以,号分隔)

class XxYy
    attr_reader :list
    def initialize(db_str)
      @list=db.str.split(/,/)
    end
    def xx_yy
      @list.join(',')
    end
end

然后主类需要用composed_of 标明映射的字段xx_yy

class Main
    composed_of :xx_yy
end

注意 aggregation对象都是value object, 你不能修改他里面的值,即使修改了,rails也不会回写它们到数据库中,唯一修改的办法是新建另外一个component然后赋给主对象

单表继承

只适用于大部分值都在父类的对象结构, 对于abstact方法的对象结构(子类直接差别非常大),可能不适合

  • 表中必须有所有子对象的属性, 不能有重合,冲突,所有 父类可能有很多允许NULL的字段
  • 表中有个type字段标示类型 ,根对象的type是"",
  • 子对象的类型实际上父对象也有,所有这里的对象不严格了, rail说是权宜之计
  • 由于type对象和ruby内置的type属性冲突,所有要用model[:type]来访问

验证 validation模块

底层有三个方法

  • validate 任何保存操作都调用
  • validate_on_create 创建对象的时候调用 通过new_record?区分
  • validate_on_update 更新对象的时候调用

实现这三个方法就可以自动使用validate, 手动调用的方法是valid?()

validate就是简单的判断, 然后再errors.add(:xxxx,"readon")就可以,例子如下

class User < ActiveRecord::Base
    def validate
        unless name && name =~ /^\w+$/
            errors.add(:name, "is missing or invalid")
        end
    end
    def validate_on_create
        if self.find_by_name(name)
            errors.add(:name, "is already being used")
        end
    end
end

提示 rails增加了一个blank?方法 可以判断string为nil或""

高层的帮助指令(很多)

大部分指令都支持:on和:message参数,

  • :on标示何时进行验证
  • :message标示验证失败的帮助信息(可格式化)

失败以后,controller会自动重新显示form,然后page可以通过调用error_messages_for()来显示错误消息

指令列表如下

  • validates_acceptance_of 用于检查checkbox, 检查对应参数的值是否为"1"
  • validates_asscociated 调用指派model自己的validate, 此方法指派的属性必须也是一个ActiveRecord的Model, 这个方法需要注意不要形成循环验证
  • validates_confirmation_of 用来检查如同password的带confirm的输入,同时检查xxx字段和xxx_confirmation是否一样(约定)
  • validates_each {|model,attr,value| ...} 一次检查多个字段,需要传入一个block, :allow_nil参数如果为true(默认false), nil就不会被传入进来
  • validates_exclusion_of attr....,:in=> enum[] 验证对应的参数不在给的列表内(支持include?的都行)
  • validates_inclusion_of 和上门相反
  • validates_format_of :with=>/..../ 正则匹配
  • validates_length_of 验证长度 有:maximum :minimum :in可用, :message还有三个扩展:too_long :too_short :wrong_length对应细化的错误信息(格式化支持%d标识要求的数值)
  • validates_numericality_of 验证数字, 支持:only_integer(支持负数)
  • validates_presence_of 验证非空
  • validates_uniqueness_of 验证唯一 :scope=>"xx" 指定的字段可以作为范围(在同一个xx中唯一)

Callbacks

一共16个callback, 其中14个如下图调用 (可以看出没有严格嵌套)

callback调用图

另外两个特殊的callback是after_find after_initialize

callback的注册可以通过

  • 直接在类中写方法,如def before_create end
  • 通过类指令: before_cate :some_method
  • 通过提供一个block {|model|...}
  • 提供一个callback对象的实例,单独的Callback对象相当于专门把callback方法独立出来放入一个类中,使得callback可以在类之间复用(默认在/app/model下)

要统一注册callback ,可以修改ActiveRecord::Base根类

注意 字段created_at created_on updated_at updated_on会自动被rails更新

Observers 类似aop

默认在app/model下一个典型的例子, 如果不使用observe类指令, 默认是根据类名推导出来为Audit类

class AuditObserver < ActiveRecord::Observer observe Order, Payment, Refund def after_save(model) model.logger.info("#{model.class.name} #{model.id} created") end end

最后要调用一下 XXXObserver.instance方法才能生效, 在rails中可以在controller中使用observe方法,不用单独instance了

属性扩展 Advanced Attributes

model.attributes可以取得属性hash,可以访问特殊名的字段

特殊的组合sql可能无法得到字段类型(mysql5.0好像没有这个问题了),导致rails以文本方式保存数据,这时我们可以通过调用read_attribute("xxx")和write_attribute("xxx")可以直接在底层操作数据,作一些转换来构造一个facade column来满足需求,例子如下

class ProductData < ActiveRecord::Base
    CUBITS_TO_INCHES = 18
    def length
        read_attribute("length") * CUBITS_TO_INCHES
    end
    def length=(inches)
        write_attribute("length", Float(inches) / CUBITS_TO_INCHES)
    end
end

注意事项

  • 为万无一失, 使用find_by_sql最好都把id给select出来, 不然不能保存
  • rails复写了ruby的id和hash功能,id是数据库ID(原来是object_id),如果id相等则认为对象相等,所有没有保存的对象(没有id)会都相等,用来hash不安全
  • 使用原始的connection: Order.connection.select_all(..) ,详情查阅文档

magic column name

  • created_at, created_on, updated_at, updated_on Automatically updated with the timestamp (_at form) or date (_on form) of a row’s creation or last update (page 267).
  • lock_version Rails will track row version numbers and perform optimistic locking if a table contains lock_version (page 213).
  • type Used by single table inheritance to track the type of a row (page 253).
  • id Default name of a table’s primary key column (page 197).
  • xxx_id Default name of a foreign key reference to table named with the singlua form of xxx (page 216).
  • xxx_count Maintains a counter cache for the child table xxx (page 235).
  • position The position of this row in a list if acts_as_list is used (page 243).
  • parent_id A reference to the id of this row’s parent if acts_as_tree is used (page
posted @ 2006-01-24 10:32  tech.cap  阅读(405)  评论(0编辑  收藏  举报