Rails笔记——ActiveRecord
admin
|
1#
发表于 2007-06-20 12:58
|
admin
|
1#
admin 发表于 2007-06-20 12:58
Rails笔记——ActiveRecord
以下文章转自 http://hpxing.blogbus.com/,在转载之前并未得到作者同意,如果作者请任何意见或者建议,请PM我。
php?name=rails" onclick="tagshow(event)" class="t_tag">rails笔记 activerecord 修改model的属性(如果是性质变化)以后最好重启动,避免奇怪的错误 自动类型转换: * int, integer => Fixnum * decimal, numeric => Float * clob, blob, text => String * interval, date => Date * float, double => FLoat * char, varchar, string => String * datetime, time => Time * boolean => 字面值 注意 1 decimal到ruby的float中运算以后精度可能_无法保证_(使用放大以后的integer代替) 2 数据表示boolean的特殊之处: Boolean处理 modelobj.name1? 来访问rails的boolean, (否则是ruby内置的低能boolean) 对比 * rails: 0 ,"0","f","false","" ,nil, false 都是false,其他为true * ruby: nil,false是false, 其他为true_ 若要定制boolean可以自己写一个name1?的方法 record的mapping ModelClass.columns.map{|col|col.name} 查看column ModelClass.columns_hash 返回列信息hash 访问数据 使用object.property访问, 也可以使用object[:property]的方式访问(当和ruby保留字冲突时) 后者还可以用来覆盖默认行为,比如 class Account < ActiveRecord::Base def balance=(value) raise BalanceTooLow if value < MINIMUM_LEVEL self[:balance] = value end end 添加modelobj1.name1_before_type_cast可以得到原始的数据对象(未转换的) serialize :last_five 可以使用YAML序列化任何对象到TEXT字段,但是这样只有识别YAML的程序可以读取 set_primary_key 'another_column' 可以让rails按照another_column来当id, 但是在对象级别访问还是使用id属性(此时映射到another_column) 使用establish_connection方法可以连接到不同于配置的数据库,方法中忽略的参数还是从配置读取默认值 对象操作 创建 new是内存中, save以后才到数据苦 Order.new do |o| ... o.save end create一步搞定,可以接受一个hash和hash的数组来创建多个, 返回创建的数组,比如order=Order.create(params)(直接从http上拿参数创建) find 查找 find支持的参数 * :all or :first * :conditinos conditions支持的参赛 o 直接使用sql conditions=>"..." o 使用?参数conditons=>["..?..",a] o 使用conditons=>["..:name", hash] * :order * :limit * :offset * :include join指令, 可能用得不多 ??? LineItem.find(:all, :conditions => "pr.title = 'Programming Ruby'", :joins => "as li inner join products as pr on li.product_id = pr.id") Order.count(支持的参数和:condition类似) 可以获取记录数 默认按照id来find会找不到报异常, 但是自己的复杂find只会返回空记录(nil or []) ObjectA.find_by_sql 可以生成自由填充的ObjectA, 但是如果要修改记得要load id find_(all)_by_A_and_B(a,b) (只支 持and) 可以自动转换为find(:first(:all) ,:conditions=>'A=? and B=?',a,b) reload重新读取 * save 返回true false * save!返回nil 或者报exception ,如果表中有lock_version int default 0 字段,会自动使用乐观锁定 * delete 和delete_all 会直接删除, * destory和destory_all会把对象都读入内存然后调用所有的callback然后再删除(没有前面快) 事务 rails里面实现trasaction, 通过trasaction(obj1,objec2) 调用会自动rollback内存里面的对象 rails内置的 save,delete方法(可能导致多个sql)默认是事务(原子)的,不用额外加事务 rails不支持跨数据库事务(至少目前),下面有个简单模拟方案 User.transaction(user) do Account.transaction(account) do account.calculate_fees user.date_fees_last_calculated = Time.now user.save account.save end end 但是这样只是模拟,如果是执行期出错可以回退, 但是user commit的时候如果出错, 此时内部的account已经commit了,不能回退了 |
admin
|
2#
admin 发表于 2007-06-20 12:59
扩展的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 |