Rubyonrails Guide to Active Record Associations
Rubyonrails Guide to Active Record Associations
Active Record
Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。Active Record 和Row Gateway (行记录入口)十分相似,但前者是领域模型,后者是一种数据源模式。关系型数据库往往通过外键来表述实体关系,Active Record 在数据源层面上也将这种关系映射为对象的关联和聚集。 Active Record 适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合Data Mapper (数据映射器)使用。 Active Record 驱动框架一般兼有ORM 框架的功能,但ActivActive Recorde Record 不是简单的ORM,正如和Row Gateway 的区别。著名的例子是全栈(Full Stack)Web 开发框架Ruby on Rails ,其默认使用一个纯Ruby 写成的Active Record 框架来驱动MVC 中的模型层。 对象关系映射(ORM)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则:简单:以最基本的形式建模数据。传达性:数据库结构被任何人都能理解的语言文档化。精确性:基于数据模型创建正确标准化了的结构。 在Martin Fowler 的《企业应用架构模式》一书中曾详细叙述了本模式。 以下是著名的Active Record 驱动框架: SQLObject(Python) Ruby on Rails ActiveRecord (Ruby) Yii Framework ActiveRecord (PHP) Castle ActiveRecord (.NET)Migrations
Migrations are a convenient way for you to alter移动your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。 Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行rake db:migrate。Active Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。It will also update your db/schema.rb file to match the structure of your database.它还会更新你的db/schema.rb文件使其于你的数据库结构相匹配。 Rails使用的是Active Record 框架来处理数据迁移,这里笔者把ActiveRecord框架放在一个地方学习了,如需了解Migration部分需要直接阅读Migration部分。Active Record Validations and Callbacks 活动记录验证和回调
This guide teaches you how to hook勾子into the life cycle of your Active Record objects.这个教程指导你怎样挂接到你的Active Record objects的生存周期。You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle.你将会学习到在将数据对象存入数据库之前怎样验证它们的状态,以及在对象生存周期的一些点上怎样执行定制操作。 Rails使用的是Active Record 框架来处理验证和回调,这里笔者把ActiveRecord框架放在一个地方学习了,如需了解Migration部分需要直接阅读ValidationsandCallbacks部分。A Guide to Active Record Associations
This guide covers the association features of Active Record. By referring to this guide, you will be able to:本教程涵盖了Active Record的关系部分的特性。(通过)这个教程提及的,你将能够:- Declare associations between Active Record models 在Active Record的models中声明(它们的)关系
- Understand the various types of Active Record associations 明白各种类型的Active Record关系
- Use the methods added to your models by creating associations 通过添加方法到models(的形式)来创建关系
1 Why Associations?为什么(会有)Associations
Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this:为什么在models之间需要关系?因为它们使得在你的代码中大多数操作更加简单和容易。例如,思考一个简单的Rails应用程序其中包含一个customers的models和一个orders的模型(生产者和消费者模型)。每个消费者可以拥有多个生产者。没有associations(的话),模型声明看起来像这样: classCustomer<ActiveRecord::Base end classOrder<ActiveRecord::Base end Now,suppose we wanted to add a new order for an existing customer.We’d need to do something like this:现在假设我们想为一个存在的customer添加一个新的order。我们需要做如下事情: @order=Order.create(:order_date=>Time.now, :customer_id=>@customer.id) Or consider deleting a customer, and ensuring that all of its orders get deleted as well:或者考虑删除一个customer,那么确保它的所有的orders也被删除。 @orders = Order.where(:customer_id => @customer.id) @orders.each do |order| order.destroy end @customer.destroy With Active Record associations, we can streamline these — and other — operations by declaratively声明方式telling Rails that there is a connection between the two models. Here’s the revised code for setting up customers and orders:通过Active Record associations,我们可以精简这样的或者其它(类似的)操作通过声明的方式告诉Rails在两个models之间有一个连接。这里修订代码来设定customers和orders: class Customer < ActiveRecord::Base has_many :orders, :dependent => :destroy end class Order < ActiveRecord::Base belongs_to :customer end With this change, creating a new order for a particular customer is easier:通过这样的修改,创建一个新的order给一个特定的customer更加容易:@order
=
@customer.orders.create(:order_date
=>
Time.now)
Deleting a customer and all of its orders is much easier:删除一个customer和它的所有的orders也容易了许多:
@customer.destroy
To learn more about the different types of associations, read the next section of this guide. That’s followed by some tips and tricks for working with associations, and then by a complete reference参考to the methods and options for associations in Rails.想要学习更多不同的类型的associations,阅读guide接下来的部分。这些(内容)伴随这一些在(使用)associations工作发现的tips和tricks,然后是一些Rails的associations的一些方法和options的一些完整的参考。
2 The Types of Associations
In Rails, an association is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model belongs_to another, you instruct Rails to maintain Primary Key–Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations:在Rails,一个association是一个在两个ActiveRecord模型之间的连接。Associations(通过)使用宏方式的调用实施,以至于你可以(以)声明的方式添加features到你的models。例如申明一个modelbelongs_to另一个(模型)。Rails支持六种类型的associations:- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
2.1 The belongs_to Association
A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model “belongs to” one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you’d declare the order model this way:一个belongs_to关系设立一个one-to-one连接到另一个model,通过这样声明model的每一个实例“belongs to” 另一个模型的实例。例如,如果你的应用程序包含customers和orders,并且每个order能够关联到仅仅一个customer,你可以这样的方式申明order模型: class Order < ActiveRecord::Base belongs_to :customer end2.2 The has_one Association
A has_one association also sets up a one-to-one connection with another model, but with somewhat different semantics语义(and consequences结果). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you’d declare the supplier model like this: 一个has_one关系也设定一个one-to-one连接到另一个model,但是有某些不同的语义(和结果)。这个关系指出了(声明的)模型的每一个实例包含或拥有一个其他模型的实例。例如你的应用程序中的每一个supplier仅仅只有一个account,你可以这样声明supplier模型: class Supplier < ActiveRecord::Base has_one :account end2.3 The has_many Association
A has_many association indicates a one-to-many connection with another model. You’ll often find this association on the “other side” of a belongs_to association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this: 一个has_many关系指定了一个one-to-many连接到另一个model。你会经常发现这个关系在belongs_to关系的另一个端。这个关系指明了(声明)模型的每一个实例有零个或多个其它模型的实例。例如,在一个应用程序中包含customers和orders,customer模型如下声明: class Customer < ActiveRecord::Base has_many :orders end The name of the other model is pluralized多元化when declaring a has_many association.2.4 The has_many :through Association
A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this: 一个has_many :through关系通常用于设定一个many-to-many连接到另一个模型。这个关系指定申明的模型可以通过第三个模型(中间模型)匹配零个或多个另一个模型的实例。例如,想想一个医疗实践(例子)其中病人约定时间去看医生。它们的有关关系声明可以如下: class Physician < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointments#通过约会有多个病人 end class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patient end class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, :through => :appointments end The collection of join models can be managed via通过the API. For example, if you assign加入模型的集合可以通过API管理。例如,如果你指定 physician.patients = patients new join models are created for newly associated关联的objects, and if some are gone their rows are deleted. Automatic deletion of join models is direct, no destroy callbacks are triggered. The has_many :through association is also useful for setting up “shortcuts” through nested has_many associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way: has_many :through关系也有利于设定‘快捷方式’通过嵌套has_many关系。 class Document < ActiveRecord::Base has_many :sections has_many :paragraphs, :through => :sections end class Section < ActiveRecord::Base belongs_to :document has_many :paragraphs end class Paragraph < ActiveRecord::Base belongs_to :section end With :through => :sections specified, Rails will now understand:因为:through => :sections被指定,Rails现在将会明白如下(语句):@document.paragraphs
2.5 The has_one :through Association
A has_one :through association sets up a one-to-one connection with another model. This association indicates that the declaring model can be matched with one instance of another model by proceeding through a third model. For example, if each supplier has one account, and each account is associated with one account history, then the customer model could look like this: 一个has_one :through关系设定一个one-to-one连接到另一个模型。这个关系指明声明的模型可以匹配另个模型的一个实例通过第三个模型(中间模型)。例如,如果每个supplier(供应商)有一个account,并且每个account关联一个account history,那么customer模型将会像这样: class Supplier < ActiveRecord::Base has_one :account has_one :account_history, :through => :account end class Account < ActiveRecord::Base belongs_to :supplier has_one :account_history end class AccountHistory < ActiveRecord::Base belongs_to :account end2.6 The has_and_belongs_to_many Association
A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening干预model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way: 一个has_and_belongs_to_many关系创建一个直接的many-to-many连接到另一个模型,没有干扰模型。例如,如果你的应用程序包含assemblies和parts,其中每个assembly拥有多个parts并且每个part发生在多个集会中,你可以以这样的方式声明模型: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end2.7 Choosing Between belongs_to and has_one
If you want to set up a 1–1 relationship between two models, you’ll need to add belongs_to to one, and has_one to the other. How do you know which is which? 如果你想在两个模型间设定一个1-1的关系,你将需要添加belongs_to到一个(模型中),以及has_one到另一个(模型中)。你是怎么知道谁是谁的呢? The distinction区别is in where you place the foreign key (it goes on the table for the class declaring the belongs_to association), but you should give some thought to the actual meaning of the data as well. The has_one relationship says that one of something is yours – that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this: class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end The corresponding migration might look like this: class CreateSuppliers < ActiveRecord::Migration def change create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.integer :supplier_id t.string :account_number t.timestamps end end end Using t.integer :supplier_id makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using t.references :supplier instead.使用t.integer :supplier_id使得外建命名明显和精确。在当前版本的Rails,你可以抽象掉这个实施细节通过使用t.references :supplier替代。2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly: Rails提供了两种不同的方式来声明一个模型间的many-to-many关系。比较简单的方式使用has_and_belongs_to_many,它允许你直接的(创建)关系: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model: 第二种方式是声明一个many-to-many关系使用has_many :through,它可以使得关系间接的,同加入一个模型: class Assembly < ActiveRecord::Base has_many :manifests has_many :parts, :through => :manifests end class Manifest < ActiveRecord::Base belongs_to :assembly belongs_to :part end class Part < ActiveRecord::Base has_many :manifests has_many :assemblies, :through => :manifests end The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity实体.If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).最简单的使用规则是,如果你需要使用关系模型作为依赖实体来工作你应该设定一个has_many :through关系。 You should use has_many:through if you need validations, callbacks, or extra attributes on the join model.2.9 Polymorphic Associations多元关系
A slightly more advanced twist on associations is the polymorphic association.一个略微先进的关系枢纽是多元关系。With polymorphic associations, a model can belong to more than one other model, on a single association. 通过多态关系,(在单个关系中)一个模型可以属于超过一个其他的模型。For example, you might have a picture model that belongs to either an employee model or a product model. Here’s how this could be declared:例如,你可能有一个picture模型属于一个employee或者一个product模型。这是它们如何被定义的: class Picture < ActiveRecord::Base belongs_to :imageable, :polymorphic => true end class Employee < ActiveRecord::Base has_many :pictures, :as => :imageable end class Product < ActiveRecord::Base has_many :pictures, :as => :imageable end You can think of a polymorphic belongs_to declaration as setting up an interface that any other model can use. From an instance of the Employee model, you can retrieve a collection of pictures: @employee.pictures. 你可以认为一个多元belongs_to声明相当于设定一个其它任何模型可以使用的接口。来源于Employee模型的实例,你可以检索一个pictures的集合:@employee.pictures。 Similarly, you can retrieve @product.pictures.相近的,你可以检索@product.pictures。 If you have an instance of the Picture model, you can get to its parent via @picture.imageable. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:如果你有一个Picture模型的实例,你可以得到它的父模型通过@picture.imageable。要使这些工作,你需要声明一个外键字段和一个什么多元(关系)的接口的字段: class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end end end This migration can be simplified by using the t.references form:这个migration可以通过t.references形式简洁的(声明): class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.references :imageable, :polymorphic => true t.timestamps end end end2.10 Self Joins
In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations:在数据库设计中,有时你会发现一个模型具有一个(关联)到它自己的关系例如,你可能希望保存所有的employees在单个数据库模型中,但是能够跟踪关系比如在manager和subordinates之间。这个情况可以设定self-joining模型: class Employee < ActiveRecord::Base has_many :subordinates, :class_name => “Employee” belongs_to :manager, :class_name => “Employee”, :foreign_key => “manager_id” end With this setup, you can retrieve @employee.subordinates and @employee.manager.通过这个设定,你可以检索@employee.subordinates and @employee.manager。3 Tips提示, Tricks窍门, and Warnings警告
Here are a few things you should know to make efficient use of Active Record associations in your Rails applications:这里有一些事情你应该知道(它)使得在你的Rails应用程序使用Active Record关系变得高效:- Controllingcaching控制缓存
- Avoidingnamecollisions避免名称碰撞
- Updating the schema 更新结构
- Controlling association scope 控制关系范围
3.1 Controlling Caching
All of the association methods are built around caching, which keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:所有的关系方法都创建在缓存周围,这使的最近的查询,可用于进一步的操作的结果。 customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders.empty? # uses the cached copy of orders But what if you want to reload the cache, because data might have been changed by some other part of the application? Just pass true to the association call:但是如果你想重载cache,因为数据可能被应用程序的其他部分改变?仅仅通过添加投入额到关系调用: customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders(true).empty? # discards the cached copy of orders # and goes back to the database3.2 Avoiding Name Collisions避免名称碰撞
You are not free to use just any name for your associations. Because creating an association adds a method with that name to the model, it is a bad idea to give an association a name that is already used for an instance method of ActiveRecord::Base. The association method would override the base method and break things. For instance, attributes or connection are bad names for associations. 你不能不限制的给你的关系使用任何名字。因为创建一个关系添加一个方法和你命名的名字到模型,给一个关系取一个在ActiveRecord::Base实例方法中已经使用了的方法不是个好主意。这个关系方法将会覆盖原有方法和打断事情。比如实例,attributes or connection是关系的坏名称。3.3 Updating the Schema
Associations are extremely useful, but they are not magic. You are responsible for maintaining your database schema to match your associations. In practice, this means two things, depending on what sort of associations you are creating. For belongs_to associations you need to create foreign keys, and for has_and_belongs_to_many associations you need to create the appropriate join table. 关系相当有用,但是它们不是魔法。你有责任注意你的数据库结构和你的关系的匹配。在实际中,这意味这两件事,依赖于你创建的何种关系。对于belongs_to关系你需要创建外键,对应has_and_belongs_to_many关系你需要创建适当的(关系)加入表中。3.3.1 Creating Foreign Keys for belongs_to Associations
When you declare a belongs_to association, you need to create foreign keys as appropriate. For example, consider this model:当你声明一个belongs_to关系,你需要创建一个外键(作为合适的方式来更新数据结构)。例如,思考如下模型: class Order < ActiveRecord::Base belongs_to :customer end This declaration needs to be backed up by the proper foreign key declaration on the orders table:这个声明需要通过在生产者表单声明适当的外键备份。 class CreateOrders < ActiveRecord::Migration def change create_table :orders do |t| t.datetime :order_date t.string :order_number t.integer :customer_id end end end If you create an association some time after you build the underlying底层model, you need to remember to create an add_column migration to provide the necessary foreign key.有时你创建的关系在你创建的底层模型之后,你需要记住创建一个add_column migration提供必须的外键。3.3.2 Creating Join Tables for has_and_belongs_to_many Associations
If you create a has_and_belongs_to_many association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the :join_table option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of “customers_orders” because “c” outranks “o” in lexical ordering. 如果你创建了一个has_and_belongs_to_many关系,你需要准确的创建joining表单。除非join表单使用:join_table选项准确的指定,(否则)Active Record就会使用类名提供的的词汇创建表单名称。因此一个在customer和order模型之间join(的表单)将会默认插入表名“customers_orders”因为‘c’在‘o’前面在提供的词汇中。 The precedence优先between model names is calculated using the < operator for String. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables “paper_boxes” and “papers” to generate a join table name of “papers_paper_boxes” because of the length of the name “paper_boxes”, but it in fact generates a join table name of “paper_boxes_papers” (because the underscore ‘_’ is lexicographically字典less than ‘s’ in common encodings). Whatever the name, you must manually手动generate the join table with an appropriate migration. For example, consider these associations: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end These need to be backed up by a migration to create the assemblies_parts table. This table should be created without a primary key: class CreateAssemblyPartJoinTable < ActiveRecord::Migration def change create_table :assemblies_parts, :id => false do |t| t.integer :assembly_id t.integer :part_id end end end We pass :id => false to create_table because that table does not represent表示a model. That’s required for the association to work properly. If you observe观察any strange behavior in a has_and_belongs_to_many association like mangled错误models IDs, or exceptions about conflicting IDs chances are you forgot that bit.3.4 Controlling Association Scope控制关系范围
By default, associations look for objects only within the current module’s scope. This can be important when you declare Active Record models within a module. For example:默认情况,(通过)关系查找对象仅仅在当前模型范围中。这很重要点你在一个module中声明Active Record models例如: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end end end This will work fine, because both the Supplier and the Account class are defined within the same scope. But the following will not work, because Supplier and Account are defined in different scopes: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end end module Billing class Account < ActiveRecord::Base belongs_to :supplier end end end To associate a model with a model in a different namespace, you must specify the complete class name in your association declaration: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, :class_name => “MyApplication::Billing::Account” end end module Billing class Account < ActiveRecord::Base belongs_to :supplier, :class_name => “MyApplication::Business::Supplier” end end end4 Detailed Association Reference具体关系参考
The following sections give the details of each type of association, including the methods that they add and the options that you can use when declaring an association.接下来的部分给出了每种关系的具体的介绍,包含当你声明一个关系时可以使用的(这些关系)添加的方法和选项。4.1.1 Methods Added by belongs_to
When you declare a belongs_to association, the declaring class automatically gains受益four methods related to the association:- association(force_reload = false)
- association=(associate)
- build_association(attributes = {})
- create_association(attributes = {})
customer
customer=
build_customer
create_customer
When initializing a new has_one or belongs_to association you must use the build_ prefix构造前缀to build the association, rather than the association. build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix新建前缀.
4.1.1.1 association(force_reload = false)
The association method returns the associated object, if any. If no associated object is found, it returns nil. @customer = @order.customer If the associated object has already been retrieved检索from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass true as the force_reload argument.4.1.1.2 association=(associate)
The association= method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object’s foreign key to the same value. @order.customer = @customer4.1.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object’s foreign key will be set, but the associated object will not yet be saved. @customer = @order.build_customer(:customer_number => 123, :customer_name => “John Doe”)4.1.1.4 create_association(attributes = {})
The create_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object’s foreign key will be set. In addition, the associated object will be saved (assuming that it passes any validations). @customer = @order.create_customer(:customer_number => 123, :customer_name => “John Doe”)4.1.2 Options for belongs_to
In many situations, you can use the default behavior of belongs_to without any customization. But despite尽管Rails’ emphasis强调of convention公约over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a belongs_to association. For example, an association with several options might look like this: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true, :conditions => “active = 1” end The belongs_to association supports these options:- :autosave
- :class_name
- :conditions 条件
- :counter_cache
- :dependent
- :foreign_key
- :include
- :polymorphic 多元
- :readonly
- :select
- :touch
- :validate
4.1.2.1 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.4.1.2.2 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is Patron, you’d set things up this way:4.1.2.3 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Order < ActiveRecord::Base belongs_to :customer, :conditions => “active = 1” end4.1.2.4 :counter_cache
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models: class Order < ActiveRecord::Base belongs_to :customer end class Customer < ActiveRecord::Base has_many :orders end With these declarations, asking for the value of @customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true end class Customer < ActiveRecord::Base has_many :orders end With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method. Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => :count_of_orders end class Customer < ActiveRecord::Base has_many :orders end Counter cache columns are added to the containing model’s list of read-only attributes through attr_readonly.4.1.2.5 :dependent
If you set the :dependent option to :destroy, then deleting this object will call the destroy method on the associated object to delete that object. If you set the :dependent option to :delete, then deleting this object will delete the associated object without calling its destroy method. You should not specify this option on a belongs_to association that is connected with a has_many association on the other class. Doing so can lead to orphaned records in your database.对应多个的话用了dependent的话删除一个和他的外键的话其他关联就失效了4.1.2.6 :foreign_key
By convention约定, Rails guesses that the column used to hold the foreign key on this model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly: class Order < ActiveRecord::Base belongs_to :customer, :class_name => “Patron”, :foreign_key => “patron_id” end In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.4.1.2.7 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class LineItem < ActiveRecord::Base belongs_to :order end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class Customer < ActiveRecord::Base has_many :orders end If you frequently retrieve customers directly from line items (@line_item.order.customer), then you can make your code somewhat more efficient by including customers in the association from line items to orders:如果你频繁的从line_item(@line_item.order.customer)直接检索,那么你可以在line_item关系中包含customers,使得你的代码变得更加有效率: class LineItem < ActiveRecord::Base belongs_to :order, :include => :customer end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class Customer < ActiveRecord::Base has_many :orders end There’s no need to use :include for immediate立即(直接)associations – that is, if you have Order belongs_to :customer, then the customer is eager-loaded automatically when it’s needed.4.1.2.8 :polymorphic
Passing true to the :polymorphic option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlierinthisguide.4.1.2.9 :readonly
If you set the :readonly option to true, then the associated object will be read-only when retrieved via the association.4.1.2.10 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. If you set the :select option on a belongs_to association, you should also set the foreign_key option to guarantee保证the correct results.4.1.2.11 :touch
If you set the :touch option to :true, then the updated_at or updated_on timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: class Order < ActiveRecord::Base belongs_to :customer, :touch => true end class Customer < ActiveRecord::Base has_many :orders end In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: class Order < ActiveRecord::Base belongs_to :customer, :touch => :orders_updated_at end4.1.2.12 :validate
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.4.1.3 How To Know Whether There’s an Associated Object?怎样知道这里是否有Associated 对象?
To know whether there’s and associated object just check association.nil?: if @order.customer.nil? @msg = “No customer found for this order” end4.1.4 When are Objects Saved?
Assigning an object to a belongs_to association does not automatically save the object. It does not save the associated object either.4.2 has_one Association Reference has_one关系参考
The has_one association creates a one-to-one match with another model. In database terms, this association says that the other class contains the foreign key. If this class contains the foreign key, then you should use belongs_to instead.4.2.1 Methods Added by has_one
When you declare a has_one association, the declaring class automatically gains four methods related to the association:- association(force_reload = false)
- association=(associate)
- build_association(attributes = {})
- create_association(attributes = {})
account
account=
build_account
create_account
When initializing a new has_one or belongs_to association you must use the build_ prefix to build the association, rather than the association.build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix.
4.2.1.1 association(force_reload = false)
The association method returns the associated object, if any. If no associated object is found, it returns nil. @account = @supplier.account If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass true as the force_reload argument.4.2.1.2 association=(associate)
The association= method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associate object’s foreign key to the same value. @supplier.account = @account4.2.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set, but the associated object will notyetbesaved. @account = @supplier.build_account(:terms => “Net 30”)4.2.1.4 create_association(attributes = {})
The create_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition此外, the associated object willbesaved(assumingthatitpassesanyvalidations). @account = @supplier.create_account(:terms => “Net 30”)4.2.2 Options for has_one
In many situations, you can use the default behavior of has_one without any customization. But despite Rails’ emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_one association. For example, an association with several options might look like this: class Supplier < ActiveRecord::Base has_one :account, :class_name => “Billing”, :dependent => :nullify end The has_one association supports these options:- :as
- :autosave
- :class_name
- :conditions
- :dependent
- :foreign_key
- :include
- :order
- :primary_key
- :readonly
- :select
- :source
- :source_type
- :through
- :validate
4.2.2.1 :as
Setting the :as option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlierinthisguide.4.2.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.4.2.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is Billing, you’d set things up this way: class Supplier < ActiveRecord::Base has_one :account, :class_name => “Billing” end4.2.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Supplier < ActiveRecord::Base has_one :account, :conditions => “confirmed = 1” end4.2.2.5 :dependent
If you set the :dependent option to :destroy, then deleting this object will call the destroy method on the associated object to delete that object. If you set the :dependent option to :delete, then deleting this object will delete the associated object without calling its destroy method. If you set the :dependent option to :nullify, then deleting this object will set the foreign key in the association object to NULL.4.2.2.6 :foreign_key
By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added添加id后缀. The :foreign_key option lets you set the name of the foreign key directly: class Supplier < ActiveRecord::Base has_one :account, :foreign_key => “supp_id” end In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.4.2.2.7 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end class Representative < ActiveRecord::Base has_many :accounts end If you frequently retrieve representatives directly from suppliers (@supplier.account.representative), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts: class Supplier < ActiveRecord::Base has_one :account, :include => :representative end class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end class Representative < ActiveRecord::Base has_many :accounts end4.2.2.8 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). Because a has_one association will only retrieve a single associated object, this option should not be needed.4.2.2.9 :primary_key
By convention, Rails guesses that the column used to hold the primary key of this model is id. You can override this and explicitly specify the primary key with the :primary_key option.4.2.2.10 :readonly
If you set the :readonly option to true, then the associated object will be read-only when retrieved via the association.4.2.2.11 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns..2.2.12 :source
The :source option specifies the source association name for a has_one :through association.4.2.2.13 :source_type
The :source_type option specifies the source association type for a has_one :through association that proceeds through a polymorphic association.4.2.2.14 :through
The :through option specifies a join model through which to perform the query. has_one :through associations were discussed in detail earlierinthisguide.4.2.2.15 :validate
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.4.2.3 How To Know Whether There’s an Associated Object?
To know whether there’s and associated object just check association.nil?: if @supplier.account.nil? @msg = “No account found for this supplier” end4.2.4 When are Objects Saved?
When you assign an object to a has_one association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too. If either of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_one association) is unsaved (that is, new_record? returns true) then the child objects are not saved. They will automatically when the parent object is saved. If you want to assign an object to a has_one association without saving the object, use the association.build method.4.3 has_many Association Reference
The has_many association creates a one-to-many relationship with another model. In database terms, this association says that the other class will have a foreign key that refers to instances of this class.4.3.1 Methods Added by has_many
When you declare a has_many association, the declaring class automatically gains 13 methods related to the association: collection(force_reload = false)- collection<<(object, …)
- collection.delete(object, …)
- collection=objects
- collection_singular_ids
- collection_singular_ids=ids
- collection.clear
- collection.empty?
- collection.size
- collection.find(…)
- collection.where(…)
- collection.exists?(…)
- collection.build(attributes = {}, …)
- collection.create(attributes = {})
4.3.1.1 collection(force_reload = false)
The collection method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. @orders = @customer.orders4.3.1.2 collection<<(object, …)
The collection<< method adds one or more objects to the collection by setting their foreign keys to the primary key of the calling model. @customer.orders << @order14.3.1.3 collection.delete(object, …)
The collection.delete method removes one or more objects from the collection by setting their foreign keys to NULL. @customer.orders.delete(@order1) Additionally, objects will be destroyed if they’re associated with :dependent => :destroy, and deleted if they’re associated with :dependent => :delete_all.4.3.1.4 collection=objects
The collection= method makes the collection contain only the supplied objects, by adding and deleting as appropriate.4.3.1.5 collection_singular_ids
The collection_singular_ids method returns an array of the ids of the objects in the collection. @order_ids = @customer.order_ids4.3.1.6 collection_singular_ids=ids
The collection_singular_ids= method makes the collection contain only the objects identified确定by the supplied primary key values, by adding and deleting as appropriate适当.4.3.1.7 collection.clear
The collection.clear method removes every object from the collection. This destroys the associated objects if they are associated with :dependent => :destroy, deletes them directly from the database if :dependent => :delete_all, and otherwise sets their foreign keys to NULL.4.3.1.8 collection.empty?
The collection.empty? method returns true if the collection does not contain any associated objects. <% if @customer.orders.empty? %> No Orders Found <% end %>4.3.1.9 collection.size
The collection.size method returns the number of objects in the collection. @order_count = @customer.orders.size4.3.1.10 collection.find(…)
The collection.find method finds objects within the collection. It uses the same syntax and options as ActiveRecord::Base.find. @open_orders = @customer.orders.where(:open => 1)4.3.1.11 collection.where(…)
The collection.where method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. @open_orders = @customer.orders.where(:open => true) # No query yet @open_order = @open_orders.first # Now the database will be queried4.3.1.12 collection.exists?(…)
The collection.exists? method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as ActiveRecord::Base.exists?.4.3.1.13 collection.build(attributes = {}, …)
The collection.build method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will not yet be saved. @order = @customer.orders.build(:order_date => Time.now, :order_number => “A12345”)4.3.1.14 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and the associated object will be saved (assuming that it passes any validations). @order = @customer.orders.create(:order_date => Time.now, :order_number => “A12345”)4.3.2 Options for has_many
In many situations, you can use the default behavior for has_many without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_many association. For example, an association with several options might look like this: class Customer < ActiveRecord::Base has_many :orders, :dependent => :delete_all, :validate => :false end The has_many association supports these options:- :as
- :autosave
- :class_name
- :conditions
- :counter_sql
- :dependent
- :extend
- :finder_sql
- :foreign_key
- :group
- :include
- :limit
- :offset
- :order
- :primary_key
- :readonly
- :select
- :source
- :source_type
- :through
- :uniq
- :validate
4.3.2.1 :as
Setting the :as option indicates that this is a polymorphic association, as discussed earlierinthisguide.4.3.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.4.3.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is Transaction, you’d set things up this way: class Customer < ActiveRecord::Base has_many :orders, :class_name => “Transaction” end4.3.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => “Order”, :conditions => “confirmed = 1” end You can also set conditions via a hash: class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => “Order”, :conditions => { :confirmed => true } end If you use a hash-style :conditions option, then record creation via this association will be automatically scoped using the hash. In this case, using @customer.confirmed_orders.create or @customer.confirmed_orders.build will create orders where the confirmed column has the value true. if you need to evaluate conditions dynamically at runtime, use a proc:4.3.2.5 :counter_sql
Normally Rails automatically generates the proper SQL to count the association members. With the :counter_sql option, you can specify a complete SQL statement to count them yourself. If you specify :finder_sql but not :counter_sql, then the counter SQL will be generated by substituting SELECT COUNT(*) FROM for the SELECT … FROM clause of your :finder_sql statement.4.3.2.6 :dependent
If you set the :dependent option to :destroy(删除映射链和映射对象), then deleting this object will call the destroy method on the associated objects to delete those objects. If you set the :dependent option to :delete_all(只删除映射链), then deleting this object will delete the associated objects without calling their destroy method. If you set the :dependent option to :nullify, then deleting this object will set the foreign key in the associated objects to NULL. This option is ignored when you use the :through option on the association.4.3.2.7 :extend
The :extend option specifies a named module to extend the association proxy. Association extensions are discussed in detail laterinthisguide.4.3.2.8 :finder_sql
Normally Rails automatically generates the proper SQL to fetch the association members. With the :finder_sql option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.4.3.2.9 :foreign_key
By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added添加前缀. The :foreign_key option lets you set the name of the foreign key directly: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.4.3.2.10 :group
The :group option supplies an attribute name to group the result set by, using a GROUP BY clause in the finder SQL. class Customer < ActiveRecord::Base has_many :line_items, :through => :orders, :group => “orders.id” end4.3.2.11 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class Customer < ActiveRecord::Base has_many :orders end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :order end If you frequently retrieve line items directly from customers (@customer.orders.line_items), then you can make your code somewhat more efficient by including line items in the association from customers to orders: class Customer < ActiveRecord::Base has_many :orders, :include => :line_items end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :order end4.3.2.12 :limit
The :limit option lets you restrict the total number of objects that will be fetched through an association. class Customer < ActiveRecord::Base has_many :recent_orders, :class_name => “Order”, :order => “order_date DESC”, :limit => 100 end4.3.2.13 :offset
The :offset option lets you specify the starting offset for fetching objects via an association. For example, if you set :offset => 11, it will skip the first 11 records.4.3.2.14 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). class Customer < ActiveRecord::Base has_many :orders, :order => “date_confirmed DESC” end4.3.2.15 :primary_key
By convention, Rails guesses that the column used to hold the primary key of the association is id. You can override this and explicitly specify the primary key with the :primary_key option.4.3.2.16 :readonly
If you set the :readonly option to true, then the associated objects will be read-only when retrieved via the association.4.3.2.17 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. If you specify your own :select, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error.4.3.2.18 :source
The :source option specifies the source association name for a has_many :through association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name.4.3.2.19 :source_type
The :source_type option specifies the source association type for a has_many :through association that proceeds through a polymorphic association.4.3.2.20 :through
The :through option specifies a join model through which to perform the query. has_many :through associations provide a way to implement many-to-many relationships, as discussed earlierinthisguide.4.3.2.21 :uniq
Set the :uniq option to true to keep the collection free of duplicates. This is mostly useful together with the :through option. class Person < ActiveRecord::Base has_many :readings has_many :posts, :through => :readings end person = Person.create(:name => ‘john’) post = Post.create(:name => ‘a1’) person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 5, name: “a1”>, #<Post id: 5, name: “a1”>] Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>] In the above case there are two readings and person.posts brings out both of them even though these records are pointing to the same post. Now let’s set :uniq to true: class Person has_many :readings has_many :posts, :through => :readings, :uniq => true end person = Person.create(:name => ‘honda’) post = Post.create(:name => ‘a1’) person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 7, name: “a1”>] Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>] In the above case there are still two readings. However person.posts shows only one post because the collection loads only unique records.4.3.2.22 :validate
If you set the :validate option to false, then associated objects will not be validated whenever you save this object. By default, this is true: associated objects will be validated when this object is saved.4.3.3 When are Objects Saved?
When you assign an object to a has_many association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved. If any of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_many association) is unsaved (that is, new_record? returns true) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved. If you want to assign an object to a has_many association without saving the object, use the collection.build method.4.4 has_and_belongs_to_many Association Reference
The has_and_belongs_to_many association creates a many-to-many relationship with another model. In database terms, this associates two classes via an intermediate join table that includes foreign keys referring to each of the classes.4.4.1 Methods Added by has_and_belongs_to_many
When you declare a has_and_belongs_to_many association, the declaring class automatically gains 13 methods related to the association:- collection(force_reload = false)
- collection<<(object, …)
- collection.delete(object, …)
- collection=objects
- collection_singular_ids
- collection_singular_ids=ids
- collection.clear
- collection.empty?
- collection.size
- collection.find(…)
- collection.where(…)
- collection.exists?(…)
- collection.build(attributes = {})
- collection.create(attributes = {})
4.4.1.1 Additional Column Methods
If the join table for a has_and_belongs_to_many association has additional columns beyond the two foreign keys, these columns will be added as attributes to records retrieved via that association. Records returned with additional attributes will always be read-only, because Rails cannot save changes to those attributes. The use of extra attributes on the join table in a has_and_belongs_to_many association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a has_many :through association instead of has_and_belongs_to_many.4.4.1.2 collection(force_reload = false)
The collection method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. @assemblies = @part.assemblies4.4.1.3 collection<<(object, …)
The collection<< method adds one or more objects to the collection by creating records in the join table. @part.assemblies << @assembly1 This method is aliased as collection.concat and collection.push.4.4.1.4 collection.delete(object, …)
The collection.delete method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects. @part.assemblies.delete(@assembly1)4.4.1.5 collection=objects
The collection= method makes the collection contain only the supplied objects, by adding and deleting as appropriate.4.4.1.6 collection_singular_ids
The collection_singular_ids method returns an array of the ids of the objects in the collection. @assembly_ids = @part.assembly_ids4.4.1.7 collection_singular_ids=ids
The collection_singular_ids= method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.4.4.1.8 collection.clear
The collection.clear method removes every object from the collection by deleting the rows from the joining table. This does not destroy the associated objects.4.4.1.9 collection.empty?
The collection.empty? method returns true if the collection does not contain any associated objects. <% if @part.assemblies.empty? %> This part is not used in any assemblies <% end %>4.4.1.10 collection.size
The collection.size method returns the number of objects in the collection. @assembly_count = @part.assemblies.size4.4.1.11 collection.find(…)
The collection.find method finds objects within the collection. It uses the same syntax and options as ActiveRecord::Base.find. It also adds the additional condition that the object must be in the collection. @new_assemblies = @part.assemblies.all( :conditions => [“created_at > ?”, 2.days.ago]) Starting Rails 3, supplying options to ActiveRecord::Base.find method is discouraged. Use collection.where instead when you need to pass conditions.4.4.1.12 collection.where(…)
The collection.where method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection. @new_assemblies = @part.assemblies.where(“created_at > ?”, 2.days.ago)4.4.1.13 collection.exists?(…)
The collection.exists? method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as ActiveRecord::Base.exists?.4.4.1.14 collection.build(attributes = {})
The collection.build method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through the join table will be created, but the associated object will not yet be saved. @assembly = @part.assemblies.build( {:assembly_name => “Transmission housing”})4.4.1.15 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and the associated object will be saved (assuming that it passes any validations). @assembly = @part.assemblies.create( {:assembly_name => “Transmission housing”})4.4.2 Options for has_and_belongs_to_many
In many situations, you can use the default behavior for has_and_belongs_to_many without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_and_belongs_to_many association. For example, an association with several options might look like this: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true end The has_and_belongs_to_many association supports these options:- :association_foreign_key
- :autosave
- :class_name
- :conditions
- :counter_sql
- :delete_sql
- :extend
- :finder_sql
- :foreign_key
- :group
- :include
- :insert_sql
- :join_table
- :limit
- :offset
- :order
- :readonly
- :select
- :uniq
- :validate
4.4.2.1 :association_foreign_key
By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix _id added. The :association_foreign_key option lets you set the name of the foreign key directly: The :foreign_key and :association_foreign_key options are useful when setting up a many-to-many self-join. For example: class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => “User”, :foreign_key => “this_user_id”, :association_foreign_key => “other_user_id” end4.4.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.4.4.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is Gadget, you’d set things up this way: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :class_name => “Gadget” end4.4.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => “factory = ‘Seattle’” end You can also set conditions via a hash: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => { :factory => ‘Seattle’ } end If you use a hash-style :conditions option, then record creation via this association will be automatically scoped using the hash. In this case, using @parts.assemblies.create or @parts.assemblies.build will create orders where the factory column has the value “Seattle”.4.4.2.5 :counter_sql
Normally Rails automatically generates the proper SQL to count the association members. With the :counter_sql option, you can specify a complete SQL statement to count them yourself. If you specify :finder_sql but not :counter_sql, then the counter SQL will be generated by substituting SELECT COUNT(*) FROM for the SELECT … FROM clause of your :finder_sql statement.4.4.2.6 :delete_sql
Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the :delete_sql option, you can specify a complete SQL statement to delete them yourself.4.4.2.7 :extend
The :extend option specifies a named module to extend the association proxy. Association extensions are discussed in detail laterinthisguide.4.4.2.8 :finder_sql
Normally Rails automatically generates the proper SQL to fetch the association members. With the :finder_sql option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.4.4.2.9 :foreign_key
By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly: class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => “User”, :foreign_key => “this_user_id”, :association_foreign_key => “other_user_id” end4.4.2.10 :group
The :group option supplies an attribute name to group the result set by, using a GROUP BY clause in the finder SQL. class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :group => “factory” end4.4.2.11 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used.4.4.2.12 :insert_sql
Normally Rails automatically generates the proper SQL to create links between the associated classes. With the :insert_sql option, you can specify a complete SQL statement to insert them yourself.4.4.2.13 :join_table
If the default name of the join table, based on lexical ordering, is not what you want, you can use the :join_table option to override the default.4.4.2.14 :limit
The :limit option lets you restrict the total number of objects that will be fetched through an association. class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => “created_at DESC”, :limit => 50 end4.4.2.15 :offset
The :offset option lets you specify the starting offset for fetching objects via an association. For example, if you set :offset => 11, it will skip the first 11 records.4.4.2.16 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => “assembly_name ASC” end4.4.2.17 :readonly
If you set the :readonly option to true, then the associated objects will be read-only when retrieved via the association.4.4.2.18 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.4.4.2.19 :uniq
Specify the :uniq => true option to remove duplicates from the collection.4.4.2.20 :validate
If you set the :validate option to false, then associated objects will not be validated whenever you save this object. By default, this is true: associated objects will be validated when this object is saved.4.4.3 When are Objects Saved?
When you assign an object to a has_and_belongs_to_many association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved. If any of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_and_belongs_to_many association) is unsaved (that is, new_record? returns true) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved. If you want to assign an object to a has_and_belongs_to_many association without saving the object, use the collection.build method.4.5 Association Callbacks
Normal callbacks hook into the life cycle of Active Record objects, allowing you to work with those objects at various points. For example, you can use a :before_save callback to cause something to happen just before an object is saved. Association callbacks are similar to normal callbacks, but they are triggered by events in the life cycle of a collection. There are four available association callbacks:- before_add
- after_add
- before_remove
- after_remove
4.6 Association Extensions
You’re not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example: class Customer < ActiveRecord::Base has_many :orders do def find_by_order_prefix(order_number) find_by_region_id(order_number[0..2]) end end end If you have an extension that should be shared by many associations, you can use a named extension module. For example: module FindRecentExtension def find_recent where(“created_at > ?”, 5.days.ago) end end class Customer < ActiveRecord::Base has_many :orders, :extend => FindRecentExtension end class Supplier < ActiveRecord::Base has_many :deliveries, :extend => FindRecentExtension end To include more than one extension module in a single association, specify an array of modules: class Customer < ActiveRecord::Base has_many :orders, :extend => [FindRecentExtension, FindActiveExtension] end Extensions can refer to the internals of the association proxy using these three accessors访问器:- proxy_owner returns the object that the association is a part of.
- proxy_reflection returns the reflection object that describes the association.
- proxy_target returns the associated object for belongs_to or has_one, or the collection of associated objects for has_many or has_and_belongs_to_many.
Active Record Validations and Callbacks 活动记录验证和回调
Active Record Validations and Callbacks 活动记录验证和回调
Active Record Validations and Callbacks 活动记录验证和回调
This guide teaches you how to hook勾子into the life cycle of your Active Record objects.这个教程指导你怎样挂接到你的Active Record objects的生存周期。You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle.你将会学习到在将数据对象存入数据库之前怎样验证它们的状态,以及在对象生存周期的一些点上怎样执行定制操作。
After reading this guide and trying out the presented concepts, we hope that you’ll be able to:在阅读了这个教程以及尝试介绍的概念,我们希望你能够:
- Understand the life cycle of Active Record objects 理解 Active Record对象的生存周期
- Use the built-in Active Record validation helpers 使用内建的 Active Record验证helpers
- Create your own custom validation methods 创建属于你的定制验证方法
- Work with the error messages generated by the validation process 在验证过程中使用错误消息创建器工作
- Create callback methods that respond to events in the object life cycle 新建一个回调方法响应对象生存周期的事件
- Create special classes that encapsulate封装common behavior for your callbacks 创建特殊的类来封装你回调的通常习惯(方法)
- Create Observers观察员that respond to life cycle events outside of the original class 创建 Observers来响应原类以外的生存周期事件
1 The Object Life Cycle
During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this object life cycle so that you can control your application and its data.一个Rails应用程序的正常操作期间,对象可能被新建,更新,和销毁。Active Record提供挂载到这个对象生存周期,以便你可以控制你的应用程序和数据。
Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger触发logic before or after an alteration改造变动of an object’s state.验证允许你确保只有验证数据被存储到你的数据库。回调和观察员(监视器)允许你在变动一个对象的状态之前或之后触发逻辑。
2 Validations Overview验证概述
Before you dive into the detail of validations in Rails, you should understand a bit about how validations fit into the big picture.在你深入Rails 验证的详细说明之前,你应该明白一点就是关于怎样让验证适合于(Rails)这幅大画卷。
2.1 Why Use Validations?
Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address.验证用于确保只有通过验证的数据被保存入你的数据库。例如,在你的应用程序中确认每个用户提供了一个有效的Email地址和邮寄地址是非常重要的。
There are several ways to validate data before it is saved into your database, including native database constraints, client-side validations, controller-level validations, and model-level validations.在数据被存储到你的数据库中之前这里有几种方法来验证它,包括本地数据库约束,客户端验证,控制层级别的验证和模型层级别的验证。
Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. However, if your database is used by other applications, it may be a good idea to use some constraints at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise.
- Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using JavaScript, they may be bypassed if JavaScript is turned off in the user’s browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site. 客户端验证是很有用的,但是一般情况下单独使用是靠不住的。如果他们使用JavaScript来实施(验证),它们可能被绕过如果用户的浏览器中JavaScript被关闭的话。然而,如果联合其他的技术,客户端验证是一种方便的方法来提供用户即使反馈(验证信息)如果他们使用你的的站点。
- Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it’s a good idea to keepyourcontrollersskinny, as it will make your application a pleasure to work with in the long run. 控制层的验证的使用是诱人的,但是通常变得笨重和难以测试和维护。只要有可能,它是一个保持你的控制层苗条好主意,它也使得你的应用程序愉快的长时间工作。
- Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well. 模板层的验证是最好的方式来确保只有有效的数据被保存进了你的数据库。它们与数据库无关,不能被终端用户绕过,并且方便测试和维护。Rails通过提供(基于)常规需要的内建的helpers,使得它们容易使用,并且同样允许你新建属于你的验证方法。
2.2 When Does Validation Happen?验证在什么时候发生?
There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, for example using the new method, that object does not belong to the database yet. Once you call save upon后that object it will be saved into the appropriate database table. Active Record uses the new_record? instance method to determine whether an object is already in the database or not. Consider the following simple Active Record class:这里有两种Active Record对象:一些对应于你数据库中的一行以及一些不是。当你创建一个新鲜的对象,例如使用新的方法,这个对象还不属于数据库。一旦你调用save(函数)之后这个对象将会被适当的保存入数据表单。Active Record使用new_record?实例方法来决定一个对象是否已经被保存进了数据库。思考下面简单的Active Record类:
class
Person
<
ActiveRecord::Base
end
We can see how it works by looking at some rails console output:我们通过观察一些rails的控制台输可以明白它是怎么工作的:
$rails
console
>>
p
=
Person.new(:name
=>
“John
Doe”)
=>
#<Person
id:
nil,
name:
“John
Doe”,
created_at:
nil,
:updated_at:
nil>
>>
p.new_record?
=>
true
>>
p.save
=>
true
>>
p.new_record?
=>
false
Creating and saving a new record will send an SQL INSERT operation to the database. Updating an existing record will send an SQL UPDATE operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the INSERT or UPDATE operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated.创建和保存一个新的记录将会发送一个SQL INSERT操作到数据库。更新一个存在的记录将会发送一个SQL UPDATE操作。验证通常在这些命令被发送到数据库之前运行。如果任何验证失败,这个对象将会标记为非法并且Active Record将不会执行INSERT或UPDATE操作。这有助于避免存储一个非法的数据到数据库。你可以在对象被创建,保存或更新的时候选择指定的验证执行。
There are many ways to change the state of an object in the database. Some methods will trigger触发validations, but some will not. This means that it’s possible to save an object in the database in an invalid state if you aren’t careful.这里有很多方法来改变对象在数据库中的状态。一些方法将会触发验证,但是一些却不会。这里的意思是如果你不仔细的话就可能以一种非法的状态保存一个对象到数据库。
The following methods trigger validations, and will save the object to the database only if the object is valid:下面的方法触发验证,并且如果对象是合法的话将会保存对象到数据库:
- create
- create!
- save
- save!
- update
- update_attributes
- update_attributes!
The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don’t: save and update_attributes return false, create and update just return the objects.有感叹号的形式(例如save!)在记录是非法的时候会唤起一个异常。没有感叹号形式的就不会:save和update_attributes返回false,create和update只是返回这个对象。
2.3 Skipping Validations忽略验证
The following methods skip validations, and will save the object to the database regardless无论of its validity. They should be used with caution.下面的方法会略过验证,并且将会保存对象到数据库而无论它的有效性。他们应该慎重使用。
- decrement! 递减
- decrement_counter
- increment!
- increment_counter
- toggle!
- touch 切换
- update_all
- update_attribute
- update_column
- update_counters
Note that save also has the ability to skip validations if passed :validate => false as argument. This technique should be used with caution.注意save也可以略过演奏如果通过使用:validate => false作为参数。这个技术也应该慎重使用。
- save(:validate => false)
2.4 valid? and invalid?
- To verify whether or not an object is valid, Rails uses the valid? method. You can also use this method on your own. valid? triggers your validations and returns true if no errors were added to the object, and false otherwise.验证一个对象是否有效,Rails使用valid?方法。你也可以使用属于你的方法。valid?触发你的验证并且返回True如果没有错误被添加到对象,否则就返回false。
class
Person
<
ActiveRecord::Base
validates
:name,
:presence
=>
true
end
Person.create(:name
=>
“John
Doe”).valid?
#
=>
true
Person.create(:name
=>
nil).valid?
#
=>
false
When Active Record is performing validations, any errors found can be accessed through the errors instance method. By definition an object is valid if this collection is empty after running validations.当Active Record在执行验证的时候,任何发现的错误都可以通过errors实例方法来访问。通过定义一个对象是有效的如果这个集合在运行验证过后是空的。
Note that an object instantiated with new will not report errors even if it’s technically invalid, because validations are not run when using new.注意当实例化一个新的对象的时候将不会报告错误即使假设它的验证技术(得出数据)是非有效的,因为在使用new的时候验证是没有运行的。
class
Person
<
ActiveRecord::Base
validates
:name,
:presence
=>
true
end
>>
p
=
Person.new
=>
#<Person
id:
nil,
name:
nil>
>>
p.errors
=>
{}
>>
p.valid?
=>
false
>>
p.errors
=>
{:name=>[“can’t
be
blank”]}
>>
p
=
Person.create
=>
#<Person
id:
nil,
name:
nil>
>>
p.errors
=>
{:name=>[“can’t
be
blank”]}
>>
p.save
=>
false
>>
p.save!
=>
ActiveRecord::RecordInvalid:
Validation
failed:
Name
can’t
be
blank
>>
Person.create!
=>
ActiveRecord::RecordInvalid:
Validation
failed:
Name
can’t
be
blank
invalid? is simply the inverse of valid?. invalid? triggers your validations and returns true if any errors were added to the object, and false otherwise. invalid?是valid?简单的逆。invalid?触发你的验证如果有任何错误添加到对象中则返回true,否则返回false。
2.5 errors[]
To verify whether or not a particular attribute of an object is valid, you can use errors[:attribute]. It returns an array of all the errors for :attribute. If there are no errors on the specified attribute, an empty array is returned.验证一个对象具体的属性是否有效,你可以使用errors[:attribute]。它返回有关:attribute所有错误的数组。如果指定的属性没有错误,将会返回一个空数组。
This method is only useful after validations have been run, because it only inspects the errors collection and does not trigger validations itself. It’s different from the ActiveRecord::Base#invalid? method explained above because it doesn’t verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object.这个方法只有在验证被执行过后才有用,因为它仅仅检查错误集合却不会自己触发验证。它和ActiveRecord::Base#invalid?方法上面解释的不同因为它不会整个验证对象的有效性。它仅仅检查这个对象的个别属性是否有错误被找到。
class
Person
<
ActiveRecord::Base
validates
:name,
:presence
=>
true
end
>>
Person.new.errors[:name].any?
#
=>
false
>>
Person.create.errors[:name].any?
#
=>
true
We’ll cover validation errors in greater depth in the WorkingwithValidationErrors section. For now, let’s turn to the built-in validation helpers that Rails provides by default.我们将会非常深入的涵盖验证错误在WorkingwithValidationErrors。现在,让我们转入Rails默认提供的内建的验证helpers。
3 Validation Helpers
Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers provide common validation rules. Every time a validation fails, an error message is added to the object’s errors collection, and this message is associated with the field being validated.Active Record提供许多预定义的你可以插入你的类中直接使用的验证helpers。这些helpers提供常规验证规则。每次验证失败,一个错误消息会被添加到对象的errors集合中,并且这些消息和被验证的field相关。
Each helper accepts an arbitrary number of attribute names, so with a single line of code you can add the same kind of validation to several attributes.每个helper接受一个任意数目的属性名字,因此使用一行代码你可以给一些属性添加相同类型验证。
All of them accept the :on and :message options, which define when the validation should be run and what message should be added to the errors collection if it fails, respectively个别的. The :on option takes one of the values :save (the default), :create or :update. There is a default error message for each one of the validation helpers. These messages are used when the :message option isn’t specified. Let’s take a look at each one of the available helpers.所有的(helpers)接受:on和:message选项,用来对个别的(helpers)定义什么时候运行验证以及如果验证失败什么消息被添加到errors集合。:on选项获取:save (默认的), :create或:update的值。这里每个验证helpers是默认错误消息。这些消息在:message选项没有被指定时使用。下面我们来看看每个可用的helpers。
3.1 acceptance接受承认
Validates that a checkbox on the user interface was checked when a form was submitted. This is typically used when the user needs to agree to your application’s terms of service, confirm reading some text, or any similar concept. This validation is very specific to web applications and this ‘acceptance’ does not need to be recorded anywhere in your database (if you don’t have a field for it, the helper will just create a virtual attribute).这个是很特殊对于web应用程序的验证并且这个‘acceptance’ 不需要在你的数据库中的任何地方记录(如果没有一个它的field,helper将会仅仅创建一个虚拟属性)。
class
Person
<
ActiveRecord::Base
validates
:terms_of_service,
:acceptance
=>
true
end
The default error message for this helper is “must be accepted”.
It can receive an :accept option, which determines the value that will be considered acceptance. It defaults to “1” and can be easily changed.它可以接收一个:accept选项,用来决定哪个值认为是接受。默认是‘1’并且可以很容易更改成其他的。
class
Person
<
ActiveRecord::Base
validates
:terms_of_service,
:acceptance
=>
{
:accept
=>
‘yes’
}
end
3.2 validates_associated
You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, valid? will be called upon each one of the associated objects.
class
Library
<
ActiveRecord::Base
has_many
:books
validates_associated
:books
end
This validation will work with all of the association types.
Don’tusevalidates_associatedonbothendsofyourassociations.Theywouldcalleachotherinaninfinite无限loop.
The default error message for validates_associated is “is invalid”. Note that each associated object will contain its own errors collection; errors do not bubble up冒泡to the calling model.
3.3 confirmation确认
You should use this helper when you have two text fields that should receive exactly the same content. For example, you may want to confirm an email address or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with “_confirmation” appended.
你应该使用这个helper当你有两个内容完全相同的文本框需要回收。(验证输入)
class
Person
<
ActiveRecord::Base
validates
:email,
:confirmation
=>
true
end
In your view template you could use something like
<%=
text_field
:person,
:email
%>
<%=
text_field
:person,
:email_confirmation
%>
This check is performed only if email_confirmation is not nil. To require confirmation, make sure to add a presence check for the confirmation attribute (we’ll take a look at presence later on this guide):
class
Person
<
ActiveRecord::Base
validates
:email,
:confirmation
=>
true
validates
:email_confirmation,
:presence
=>
true
end
The default error message for this helper is “doesn’t match confirmation”.
3.4 exclusion排除
This helper validates that the attributes’ values are not included in a given set. In fact, this set can be any enumerable列举object.
class
Account
<
ActiveRecord::Base
validates
:subdomain,
:exclusion
=>
{
:in
=>
%w(www
us
ca
jp),
:message
=>
“Subdomain
%{value}
is
reserved.”
}
end
The exclusion helper has an option :in that receives the set of values that will not be accepted for the validated attributes. The :in option has an alias called :within that you can use for the same purpose目的, if you’d like to. This example uses the :message option to show how you can include the attribute’s value.
The default error message is “is reserved”.
3.5 format格式(匹配)
This helper validates the attributes’ values by testing whether they match a given regular expression正则表达式, which is specified using the :with option.
class
Product
<
ActiveRecord::Base
validates
:legacy_code,
:format
=>
{
:with
=>
/\A[a-zA-Z]+\z/,
:message
=>
“Only
letters
allowed”
}
end
The default error message is “is invalid”.
3.6 inclusion列入
This helper validates that the attributes’ values are included in a given set. In fact, this set can be any enumerable object.
class
Coffee
<
ActiveRecord::Base
validates
:size,
:inclusion
=>
{
:in
=>
%w(small
medium
large),
:message
=>
“%{value}
is
not
a
valid
size”
}
end
The inclusion helper has an option :in that receives the set of values that will be accepted. The :in option has an alias called :within that you can use for the same purpose, if you’d like to. The previous example uses the :message option to show how you can include the attribute’s value.
The default error message for this helper is “is not included in the list”.
3.7 length
This helper validates the length of the attributes’ values. It provides a variety of options, so you can specify length constraints in different ways:
class Person < ActiveRecord::Base
validates :name, :length => { :minimum => 2 }
validates :bio, :length => { :maximum => 500 }
validates :password, :length => { :in => 6..20 }
validates :registration_number, :length => { :is => 6 }
end
The possible length constraint约束options are:
- :minimum – The attribute cannot have less than the specified length.
- :maximum – The attribute cannot have more than the specified length.
- :in (or :within) – The attribute length must be included in a given interval. The value for this option must be a range.
- :is – The attribute length must be equal to the given value.
The default error messages depend on the type of length validation being performed. Youcanpersonalizethesemessagesusingthe:wrong_length,:too_long,and:too_shortoptionsand%{count}asaplaceholderforthenumbercorrespondingtothelengthconstraintbeingused.You can still use the :message option to specify an error message.
class
Person
<
ActiveRecord::Base
validates
:bio,
:length
=>
{
:maximum
=>
1000,
:too_long
=>
“%{count}
characters
is
the
maximum
allowed”
}
end
This helper counts characters by default, but you can split the value in a different way using the :tokenizer option:
class
Essay
<
ActiveRecord::Base
validates
:content,
:length
=>
{
:minimum
=>
300,
:maximum
=>
400,
:tokenizer
=>
lambda
{
|str|
str.scan(/\w+/)
},
:too_short
=>
“must
have
at
least
%{count}
words”,
:too_long
=>
“must
have
at
most
%{count}
words”
}
end
Note that the default error messages are plural多元的(e.g., “is too short (minimum is %{count} characters)”). For this reason, when :minimum is 1 you should provide a personalized个性化message or use validates_presence_of instead. When :in or :within have a lower limit of 1, you should either provide a personalized message or call presence prior前to length.(当长度比1还小就为空了应该使用validates_presence_of来验证)
The size helper is an alias for length.
3.8 numericality只能输入数字
This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by an integral or floating point number. To specify that only integral numbers are allowed set :only_integer to true.
If you set :only_integer to true, then it will use the
/\A[+–]?\d+\Z/
regular expression to validate the attribute’s value. Otherwise, it will try to convert the value to a number using Float.
Notethattheregularexpressionaboveallowsatrailingnewlinecharacter.请注意,上面的正则表达式允许一个尾随的换行符
class
Player
<
ActiveRecord::Base
validates
:points,
:numericality
=>
true
validates
:games_played,
:numericality
=>
{
:only_integer
=>
true
}
end
Besides :only_integer, this helper also accepts the following options to add constraints to acceptable values:
- :greater_than – Specifies the value must be greater than the supplied value. The default error message for this option is “must be greater than %{count}”. 大于
- :greater_than_or_equal_to – Specifies the value must be greater than or equal to the supplied value. The default error message for this option is “must be greater than or equal to %{count}”.
- :equal_to – Specifies the value must be equal to the supplied value. The default error message for this option is “must be equal to %{count}”.
- :less_than – Specifies the value must be less than the supplied value. The default error message for this option is “must be less than %{count}”.
- :less_than_or_equal_to – Specifies the value must be less than or equal the supplied value. The default error message for this option is “must be less than or equal to %{count}”.
- :odd – Specifies the value must be an odd number if set to true. The default error message for this option is “must be odd”. 奇数
- :even – Specifies the value must be an even number if set to true. The default error message for this option is “must be even”. 偶数
The default error message is “is not a number”.
3.9 presence存在
This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either nil or a blank string, that is, a string that is either empty or consists of whitespace.
class
Person
<
ActiveRecord::Base
validates
:name,
:login,
:email,
:presence
=>
true
end
If you want to be sure that an association is present, you’ll need to test whether the foreign key used to map the association is present, and not the associated object itself.
class
LineItem
<
ActiveRecord::Base
belongs_to
:order
validates
:order_id,
:presence
=>
true
end
Since false.blank? is true, if you want to validate the presence of a boolean field you should use validates :field_name, :inclusion => { :in => [true, false] }.
The default error message is “can’t be empty”.
3.10 uniqueness独特性
This helper validates that the attribute’s value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create a unique index in your database.
class
Account
<
ActiveRecord::Base
validates
:email,
:uniqueness
=>
true
end
The validation happens by performing an SQL query into the model’s table, searching for an existing record with the same value in that attribute.
There is a :scope范围option that you can use to specify other attributes that are used to limit the uniqueness check:
class
Holiday
<
ActiveRecord::Base
validates
:name,
:uniqueness
=>
{
:scope
=>
:year,
:message
=>
“should
happen
once
per
year”
}
end
There is also a :case_sensitive option that you can use to define whether the uniqueness constraint约束will be case sensitive敏感or not. This option defaults to true.
class
Person
<
ActiveRecord::Base
validates
:name,
:uniqueness
=>
{
:case_sensitive
=>
false
}
end
Note that some databases are configured to perform case-insensitive searches anyway.需要注意的是一些数据库配置为执行区分大小写的搜索。
The default error message is “has already been taken”.
3.11 validates_with通过指定类验证
This helper passes the record to a separate class for validation.
class Person < ActiveRecord::Base
validates_with GoodnessValidator
end
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if record.first_name == “Evil”
record.errors[:base] << “This person is evil”
end
end
end
The validates_with helper takes a class, or a list of classes to use for validation. There is no default error message for validates_with. You must manually add errors to the record’s errors collection in the validator class.
To implement the validate method, you must have a record parameter defined, which is the record to be validated.
Like all other validations, validates_with takes the :if, :unless and :on options. If you pass any other options, it will send those options to the validator class as options:
class Person < ActiveRecord::Base
validates_with GoodnessValidator, :fields => [:first_name, :last_name]
end
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any?{|field| record.send(field) == “Evil” }
record.errors[:base] << “This person is evil”
end
end
end
3.12 validates_each
This helper validates attributes against a block. It doesn’t have a predefined validation function. You should create one using a block, and every attribute passed to validates_each will be tested against it. In the following example, we don’t want names and surnames to begin with lower case.
class Person < ActiveRecord::Base
validates_each :name, :surname do |model, attr, value|
model.errors.add(attr, ‘must start with upper case’) if value =~ /\A[a-z]/
end
end
The block receives the model, the attribute’s name and the attribute’s value. You can do anything you like to check for valid data within the block. If your validation fails, you can add an error message to the model, therefore故making it invalid.
4 Common Validation Options常规验证选项
These are common validation options:
4.1 :allow_nil
The :allow_nil option skips the validation when the value being validated is nil.
class Coffee < ActiveRecord::Base
validates :size, :inclusion => { :in => %w(small medium large),
:message => “%{value} is not a valid size” }, :allow_nil => true
end
:allow_nilisignoredbythepresencevalidator.
4.2 :allow_blank
The :allow_blank option is similar to the :allow_nil option. This option will let validation pass if the attribute’s value is blank?, like nil or an empty string for example.
class Topic < ActiveRecord::Base
validates :title, :length => { :is => 5 }, :allow_blank => true
end
Topic.create(“title” => “”).valid? # => true
Topic.create(“title” => nil).valid? # => true
:allow_blankisignoredbythepresencevalidator.
4.3 :message
As you’ve already seen, the :message option lets you specify the message that will be added to the errors collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.
4.4 :on
The :on option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be run on save (both when you’re creating a new record and when you’re updating it). If you want to change it, you can use :on => :create to run the validation only when a new record is created or :on => :update to run the validation only when a record is updated.:on 选项让你指定什么时候产生验证。
class Person < ActiveRecord::Base
it will be possible to update email with a duplicated value
validates :email, :uniqueness => true, :on => :create
it will be possible to create the record with a non-numerical age
validates :age, :numericality => true, :on => :update
the default (validates on both create and update)
validates :name, :presence => true, :on => :save
end
5 Conditional Validation有附加条件的验证
Sometimes it will make sense感觉to validate an object just when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string or a Proc. You may use the :if option when you want to specify when the validation should happen. If you want to specify when the validation should not happen, then you may use the :unless option.
有时候验证一个对象的时候如果给你一个判定性的(语句)你会感觉到满足。你可以使用:if和:unless选项达到这样的效果,它可以(判断)一个符号(标记),字符串或者是Proc。
Proc 是Ruby 对block的面向对象的封装。
5.1 Using a Symbol with :if and :unless
You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
class Order < ActiveRecord::Base
validates :card_number, :presence => true, :if => :paid_with_card?
def paid_with_card?
payment_type == “card”
end
end
5.2 Using a String with :if and :unless
YoucanalsouseastringthatwillbeevaluatedusingevalandneedstocontainvalidRubycode.You should use this option only when the string represents a really short condition.
class Person < ActiveRecord::Base
validates :surname, :presence => true, :if => “name.nil?”
end
5.3 Using a Proc with :if and :unless
Finally, it’s possible to associate :if and :unless with a Proc object which will be called. Using a Proc object gives you the ability to write an inline condition instead of a separate单独method. This option is best suited for one-liners.
class Account < ActiveRecord::Base
validates :password, :confirmation => true,
:unless => Proc.new { |a| a.password.blank? }
end
5.4 Grouping conditional validations
Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using with_options.
class User < ActiveRecord::Base
with_options :if => :is_admin? do |admin|
admin.validates :password, :length => { :minimum => 10 }
admin.validates :email, :presence => true
end
end
All validations inside of with_options block will have automatically passed the condition :if => :is_admin?
6 Performing Custom Validations执行定制验证
When the built-in validation helpers are not enough for your needs, you can write your own validators or validation methods as you prefer.
6.1 Custom Validators定制验证器
Custom validators are classes that extend ActiveModel::Validator. These classes must implement a validate method which takes a record as an argument and performs the validation on it. The custom validator is called using the validates_with method.
class MyValidator < ActiveModel::Validator
def validate(record)
if record.name.starts_with? ‘X’
record.errors[:name] << ‘Need a name starting with X please!’
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end
The easiest way to add custom validators for validating individual attributes is with the convenient ActiveModel::EachValidator. In this case, the custom validator class must implement实施落实a validate_each method which takes three arguments: record, attribute and value which correspond对应to the instance, the attribute to be validated and the value of the attribute in the passed instance.
最简单的方法添加(需要)验证个别的属性到定制的validatoras是便捷的使用ActiveModel::EachValidator。在这个案例中,定制的validator类必须落实validate_each方法获得三个参数:与实例对应的record, attribute and value,(它们是)接下来的实例的属性和值
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || “is not an email”)
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
As shown in the example, you can also combine结合standard validations with your own custom validators.
6.2 Custom Methods定制(验证)方法
You can also create methods that verify the state of your models and add messages to the errors collection when they are invalid. You must then register these methods by using one or more of the validate, validate_on_create or validate_on_update class methods, passing in the symbols符号for the validation methods’ names.
你也可以创建方法验证你models的状态以及在被验证的时候添加消息到errors集合。你必须注册这些方法通过使用一个或多个这些方法:validate, validate_on_create or validate_on_update类方法加上验证方法的名字。
You can pass more than one symbol for each class method and the respective各自的validations will be run in the same order as they were registered.你可以添加一个或多个符号给(对应的)每一个类方法并且各自的验证将会按照与注册相同的顺序运行。
class Invoice < ActiveRecord::Base
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if !expiration_date.blank? and expiration_date < Date.today
errors.add(:expiration_date, “can’t be in the past”)
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, “can’t be greater than total value”)
end
end
end
Youcanevencreateyourownvalidationhelpersandreusetheminseveraldifferentmodels. For example, an application that manages surveys调查may find it useful to express that a certain某些field corresponds对应to a set of choices:
ActiveRecord::Base.class_eval do
def self.validates_as_choice(attr_name, n, options={})
validates attr_name, :inclusion => { {:in => 1..n}.merge(options) }
end
end
Simply reopen ActiveRecord::Base and define a class method like that. You’d typically put this code somewhere in config/initializers. You can use this helper like this:
class Movie < ActiveRecord::Base
validates_as_choice :rating, 5
end
7 Working with Validation Errors工作与Validation Errors
In addition to除了the valid? and invalid? methods covered earlier, Rails provides a number of methods for working with the errors collection and inquiring疑问about the validity of objects.
Thefollowingisalistofthemostcommonlyusedmethods.PleaserefertotheActiveRecord::Errorsdocumentationforalistofalltheavailablemethods.
7.1 errors
Returns an OrderedHash with all errors. Each key is the attribute name and the value is an array of strings with all errors.
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors
=> {:name => [“can’t be blank”, “is too short (minimum is 3 characters)”]}
person = Person.new(:name => “John Doe”)
person.valid? # => true
person.errors # => []
7.2 errors[]
errors[] is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array.
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new(:name => “John Doe”)
person.valid? # => true
person.errors[:name] # => []
person = Person.new(:name => “JD”)
person.valid? # => false
person.errors[:name] # => [“is too short (minimum is 3 characters)”]
person = Person.new
person.valid? # => false
person.errors[:name]
=> [“can’t be blank”, “is too short (minimum is 3 characters)”]
7.3 errors.add
The add method lets you manually手动add messages that are related to particular attributes. You can use the errors.full_messages or errors.to_a methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended前置(and capitalized大写). add receives接收the name of the attribute you want to add the message to, and the message itself. #这里只是添加的消息并没有验证
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors.add(:name, “cannot contain the characters !@#%*()_–+=”)
end
end
person = Person.create(:name => “!@#”)
person.errors[:name]
=> [“cannot contain the characters !@#%*()_–+=”]
person.errors.full_messages
=> [“Name cannot contain the characters !@#%*()_–+=”]
Another way to do this is using []= setter
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors[:name] = “cannot contain the characters !@#%*()_–+=”
end
end
person = Person.create(:name => “!@#”)
person.errors[:name]
=> [“cannot contain the characters !@#%*()_–+=”]
person.errors.to_a
=> [“Name cannot contain the characters !@#%*()_–+=”]
7.4 errors[:base]
You can add error messages that are related to the object’s state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since errors[:base] is an array, you can simply add a string to the array and uses it as the error message.
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors[:base] << “This person is invalid because …”
end
end
7.5 errors.clear
The clear method is used when you intentionally有意want to clear all the messages in the errors collection. Of course, calling errors.clear upon在…之上an invalid object won’t actually make it valid: the errors collection will now be empty, but the next time you call valid? or any method that tries to save this object to the database, the validations will run again. Ifanyofthevalidationsfail,theerrorscollectionwillbefilledagain.
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors[:name]
=> [“can’t be blank”, “is too short (minimum is 3 characters)”]
person.errors.clear
person.errors.empty? # => true
p.save # => false
p.errors[:name]
=> [“can’t be blank”, “is too short (minimum is 3 characters)”]
7.6 errors.size
The size method returns the total number of error messages for the object.
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors.size # => 3
person = Person.new(:name => “Andrea”, :email => “andrea@example.com”)
person.valid? # => true
person.errors.size # => 0
8 Displaying Validation Errors in the View在视图中显示验证错误(信息)
Rails maintains an official plugin that provides helpers to display the error messages of your models in your view templates. You can install it as a plugin or as a Gem.
8.1 Installing as a plugin
$ rails plugin install git://github.com/joelmoss/dynamic_form.git
8.2 Installing as a Gem
Add this line in your Gemfile:
gem “dynamic_form”
Now you will have access to these two methods in your view templates
8.3 error_messages and error_messages_for
When creating a form with the form_for helper, you can use the error_messages method on the form builder to render all failed validation messages for the current model instance.
class Product < ActiveRecord::Base
validates :description, :value, :presence => true
validates :value, :numericality => true, :allow_nil => true
end
<%= form_for(@product) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :description %><br />
<%= f.text_field :description %>
</p>
<p>
<%= f.label :value %><br />
<%= f.text_field :value %>
</p>
<p>
<%= f.submit “Create” %>
</p>
<% end %>
To get the idea, if you submit the form with empty fields you typically get this back, though styles are indeed missing by default:
You can also use the error_messages_for helper to display the error messages of a model assigned to a view template. It’s very similar to the previous example and will achieve exactly the same result.
<%=
error_messages_for
:product
%>
The displayed text for each error message will always be formed by the capitalized name of the attribute that holds the error, followed by the error message itself.大写错误的属性名,后跟错误消息。
<%= f.error_messages :header_message => “Invalid product!”,
:message => “You’ll need to fix the following fields:”,
:header_tag => :h3 %>
Which results in the following content:
If you pass nil to any of these options, it will get rid of the respective section of the div.
8.4 Customizing the Error Messages CSS定制错误消息的CSS样式
The selectors to customize the style of error messages are:定制错误消息的选择器是:
- field_with_errors – Style for the form fields and labels with errors. Errors的form fields和labels的样式
- #errorExplanation – Style for the div element with the error messages. error消息的div元素样式
- #errorExplanation h2 – Style for the header of the div element. div元素包含的标题样式
- #errorExplanation p – Style for the paragraph that holds the message that appears right below the header of the div element.div元素中出现在标题下方段落的样式
- #errorExplanation ul li – Style for the list items with individual error messages. 错误消息中个别的项目列表样式
Scaffolding脚手架for example generates app/assets/stylesheets/scaffold.css.scss, which later compiles to app/assets/stylesheets/scaffold.css and defines the red-based style you saw above.
The name of the class and the id can be changed with the :class and :id options, accepted by both helpers.
Scaffolding — 基架
基于数据库架构生成网页模板的过程。在ASP .NET 中,动态数据使用基架来简化基于Web 的UI 的生成过程。用户可以通过这种UI 来查看和更新数据库。
8.5 Customizing the Error Messages HTML
By default, form fields with errors are displayed enclosed by a div element with the field_with_errors CSS class. However, it’s possible to override that.
The way form fields with errors are treated is defined by ActionView::Base.field_error_proc. This is a Proc that receives two parameters:
- A string with the HTML tag
- An instance of ActionView::Helpers::InstanceTag.
Here is a simple example where we change the Rails behavior to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a span element with a validation-error CSS class. There will be no div element enclosing the input element, so we get rid of that red border around the text field. You can use the validation-error CSS class to style it anyway you want.
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
if instance.error_message.kind_of?(Array)
%(#{html_tag}<span class=“validation-error”>
{instance.error_message.join(‘,’)}</span>).html_safe
else
%(#{html_tag}<span class=“validation-error”>
{instance.error_message}</span>).html_safe
end
end
This will result in something like the following:
9 Callbacks Overview回调概述
Callbacks are methods that get called at certain moments of an object’s life cycle. With callbacks it’s possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.
回调是一种在对象生存周期中的某些情况中(使得对象)被调用的方法。通过callbacks可以运行编写的代码无论Active Record对象是在数据库被创建,保存,删除,验证或者是导入。
9.1 Callback Registration
In order to use the available callbacks, you need to register them. You can do that by implementing实施them as ordinary一般methods, and then using a macro-style class method to register them as callbacks.
class User < ActiveRecord::Base
validates :login, :email, :presence => true
before_validation :ensure_login_has_a_value
protected
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end
The macro-style class methods can also receive接收a block. Consider using this style if the code inside your block is so short that it fits in just one line.
class User < ActiveRecord::Base
validates :login, :email, :presence => true
before_create do |user|
user.name = user.login.capitalize if user.name.blank?
end
end
It’s considered考虑good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate违反the principle of object encapsulation封装.(基于)周密的考虑声明callback方法为protected或者private。如果是public,他们可以被model外调用这样违反了封装对象的原则。
10 Available Callbacks
Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations:
10.1 Creating an Object
- before_validation
- after_validation
- before_save
- before_create
- around_create
- after_create
- after_save
10.2 Updating an Object
- before_validation
- after_validation
- before_save
- before_update
- around_update
- after_update
- after_save
10.3 Destroying an Object
- before_destroy
- after_destroy
- around_destroy
after_save runs both on create and update, but always after the more specific callbacks after_create and after_update, no matter the order in which the macro calls were executed.after_save总是在after_create and after_update的后面不管他们的微调用如何。
10.4 after_initialize and after_find
The after_initialize callback will be called whenever an Active Record object is instantiated, either by directly using new or when a record is loaded from the database. It can be useful to avoid the need to directly override覆盖your Active Record initialize method.
The after_find callback will be called whenever Active Record loads a record from the database. after_find is called before after_initialize if both are defined.
The after_initialize and after_find callbacks have no before_* counterparts同行, but they can be registered just like the other Active Record callbacks.
class User < ActiveRecord::Base
after_initialize do |user|
puts “You have initialized an object!”
end
after_find do |user|
puts “You have found an object!”
end
end
>> User.new
You have initialized an object!
=> #<User id: nil>
>> User.first
You have found an object!
You have initialized an object!
=> #<User id: 1>
11 Running Callbacks
The following methods trigger callbacks:下面的方法触发回调:
- create
- create!
- decrement! 递减
- destroy
- destroy_all
- increment!
- save
- save!
- save(false)
- toggle!
- update
- update_attribute
- update_attributes
- update_attributes!
- valid?
Additionally, the after_find callback is triggered by the following finder methods:此外,after_find会被下面的查找方法触发:
- all
- first
- find
- find_all_by_attribute
- find_by_attribute
- find_by_attribute!
- last
The after_initialize callback is triggered every time a new object of the class is initialized.当类的一个新的对象被初始化的时候都会触发after_initialize回调。
12 Skipping Callbacks
Just as with validations, it’s also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential潜在implications影响may lead to invalid data.正如验证,它有可能忽略回调。这些方法需要注意使用,然而,由于重要的业务规则和应用程序逻辑可能保持回调。没有明白潜在的影响就绕过它们可能会导致非法数据。
- decrement
- decrement_counter
- delete
- delete_all
- find_by_sql
- increment
- increment_counter
- toggle
- touch
- update_column
- update_all
- update_counters
13 Halting Execution停止执行
As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model’s validations, the registered callbacks, and the database operation to be executed.
The whole callback chain链is wrapped in a transaction交易. If any before callback method returns exactly false or raises an exception the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish完成that by raising an exception.
Raising an arbitrary exception may break code that expects save and friends not to fail like that.抛出任意异常可能会打断代码预计的save以及朋友们不想要的失败。The ActiveRecord::Rollback exception is thought precisely正是to tell Active Record a rollback is going on. That one is internally captured but not reraised.
14 Relational Callbacks Callbacks相关
Callbacks work through model relationships, and can even be defined by them. Let’s take an example where a user has many posts. In our example, a user’s posts should be destroyed if the user is destroyed. So, we’ll add an after_destroy callback to the User model by way of its relationship to the Post model.
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
end
class Post < ActiveRecord::Base
after_destroy :log_destroy_action
def log_destroy_action
puts ‘Post destroyed’
end
end
>> user = User.first
=> #<User id: 1>
>> user.posts.create!
=> #<Post id: 1, user_id: 1>
>> user.destroy
Post destroyed
=> #<User id: 1>
15 Conditional Callbacks有条件的回调
Like in validations, we can also make our callbacks conditional, calling them only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string or a Proc. You may use the :if option when you want to specify when the callback should get called. If you want to specify when the callback should not be called, then you may use the :unless option.
15.1 Using :if and :unless with a Symbol
You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before the callback. When using the :if option, the callback won’t be executed if the method returns false; when using the :unless option, the callback won’t be executed if the method returns true. This is the most common option. Using this form of registration it’s also possible to register several different methods that should be called to check if the callback should be executed.
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => :paid_with_card?
end
15.2 Using :if and :unless with a String
You can also use a string that will be evaluated using eval and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => “paid_with_card?”
end
15.3 Using :if and :unless with a Proc
Finally, it’s possible to associate :if and :unless with a Proc object. This option is best suited when writing short validation methods, usually one-liners.
class Order < ActiveRecord::Base
before_save :normalize_card_number,
:if => Proc.new { |order| order.paid_with_card? }
end
15.4 Multiple Conditions for Callbacks
When writing conditional callbacks, it’s possible to mix both :if and :unless in the same callback declaration.
class Comment < ActiveRecord::Base
after_create :send_email_to_author, :if => :author_wants_emails?,
:unless => Proc.new { |comment| comment.post.ignore_comments? }
end
16 Callback Classes
Sometimes the callback methods that you’ll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.
Here’s an example where we create a class with an after_destroy callback for a PictureFile model.
class PictureFileCallbacks
def after_destroy(picture_file)
if File.exists?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
When declared inside a class the callback method will receive the model object as a parameter. We can now use it this way:
class PictureFile < ActiveRecord::Base
after_destroy PictureFileCallbacks.new
end
Note that we needed to instantiate a new PictureFileCallbacks object, since we declared our callback as an instance method. Sometimes it will make more sense to have it as a class method.
class PictureFileCallbacks
def self.after_destroy(picture_file)
if File.exists?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
If the callback method is declared this way, it won’t be necessary to instantiate a PictureFileCallbacks object.
class PictureFile < ActiveRecord::Base
after_destroy PictureFileCallbacks
end
You can declare as many callbacks as you want inside your callback classes.
17 Observers观测者
Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn’t directly related to its purpose, observers allow you to add the same functionality outside of a model. For example, it could be argued that a User model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn’t directly related to your model, you may want to consider creating an observer instead.
Observers与callbacks很相似,但是有着很大不同。鉴于callbacks能够影响model通过代码那与他的目的并没有直接关联,observers允许你在model外添加相同的功能。
17.1 Creating Observers
For example, imagine a User model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model’s purpose, we could create an observer to contain this functionality.
$
rails
generate
observer
User
class UserObserver < ActiveRecord::Observer
def after_create(model)
code to send confirmation email…
end
end
As with callback classes, the observer’s methods receive the observed model as a parameter.
17.2 Registering Observers
Observers are conventionally placed inside of your app/models directory and registered in your application’s config/application.rb file. For example, the UserObserver above would be saved as app/models/user_observer.rb and registered in config/application.rb this way:
#
Activate
observers
that
should
always
be
running
config.active_record.observers
=
:user_observer
As usual, settings in config/environments take precedence优先权over those in config/application.rb. So, if you prefer that an observer doesn’t run in all environments, you can simply register it in a specific environment instead.
17.3 Sharing Observers
By default, Rails will simply strip “Observer” from an observer’s name to find the model it should observe. However, observers can also be used to add behavior to more than one model, and so it’s possible to manually specify the models that our observer should observe.
class MailerObserver < ActiveRecord::Observer
observe :registration, :user
def after_create(model)
code to send confirmation email…
end
end
In this example, the after_create method would be called whenever a Registration or User was created. Note that this new MailerObserver would also need to be registered in config/application.rb in order to take effect.
config.active_record.observers
=
:mailer_observer#
与上面的类名相关
18 Transaction Callbacks
There are two additional callbacks that are triggered by the completion of a database transaction: after_commit and after_rollback. These callbacks are very similar to the after_save callback except that they don’t execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction.
Consider, for example, the previous example where the PictureFile model needs to delete a file after a record is destroyed. If anything raises an exception after the after_destroy callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that picture_file_2 in the code below is not valid and the save! method raises an error.
PictureFile.transaction do
picture_file_1.destroy
picture_file_2.save!
end
By using the after_commit callback we can account for this case.
class PictureFile < ActiveRecord::Base
attr_accessor :delete_file
after_destroy do |picture_file|
picture_file.delete_file = picture_file.filepath
end
after_commit do |picture_file|
if picture_file.delete_file && File.exist?(picture_file.delete_file)
File.delete(picture_file.delete_file)
picture_file.delete_file = nil
end
end
end
The after_commit and after_rollback callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don’t interfere with the other callbacks. As such, if your callback code could raise an exception, you’ll need to rescue it and handle it appropriately within the callback.
Markdown Syntax Guide
Markdown Syntax Guide
Markdown Syntax Guide The Allura code uses markdown syntax everywhere to allow you to create rich text markup, and extends markdown in several ways to allow for quick linking to other artifacts in your project.
Markdown was created to be easy to read, easy to write, and still readable in plain text format.
Links Reference Links Artifact Links Text Blockquotes Preformatted Text Lists Tables Headers Horizontal Rules Images Escapes More Headers Code Highlighting Includes Neighborhood Notifications Download Button Project Screenshots Thanks Links Most URLs will automatically be turned into links. To be explicit, just write it like this:
Output:
To use text for the link, write it:
like thisOutput:
like this
You can add a title (which shows up under the cursor):
like thisOutput:
like this
Reference Links You can also put the link URL below the current paragraph like this.
Output:
You can also put the link URL below the current paragraph like this.
Here the text “link URL” gets linked to “http://url”, and the lines showing “1: http://url” won’t show anything.
Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.
Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.
Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.
Artifact Links Any existing forge resource can be linked with surrounding square brackets ie [MyPage] or [#123].
Links to resources in other tools can be explicitly referenced by adding a tool identifier prefix to the link. So for instance [developerwiki:MyPage]
can refer to a wiki page in a developerwiki
instance. You can also link to tickets with [tickets:#123]
assuming there’s a 123 ticket in a Tracker instance mounted at tickets
. The same is true for forums, or any of the other tools you have installed. You can even link to tickets in a subproject with [subproject.tickets:#123]
.
[MyPage] [developerwiki:MyPage] [#123] [tickets:#123] No example output is available for this one because it only works on real artifacts. Try it in your project!
Text Use * or _ to emphasize things:
this is in italic and so is this
this is in bold and so is this
this is bold and italic and so is this Output:
this is in italic and so is this
this is in bold and so is this
this is bold and italic and so is this
You can strike through text using HTML like this:
this is strike through text
Output:
this is strike through text
Just write paragraphs like in a text file and they will display how you would expect. A blank line separates paragraphs.
So this is a new paragraph. But any text on adjacent lines will all end up in the same paragraph. Output:
Just write paragraphs like in a text file and they will display how you would expect. A blank line separates paragraphs.
So this is a new paragraph. But any text on adjacent lines will all end up in the same paragraph.
Blockquotes Use the > character in front of a line, just like in email
Use it if you’re quoting a person, a song or whatever.
You can use italic or lists inside them also. And just like with other paragraphs, all of these lines are still part of the blockquote, even without the > character in front.
To end the blockquote, just put a blank line before the following paragraph. Output:
Use it if you’re quoting a person, a song or whatever.
You can use italic or lists inside them also. And just like with other paragraphs, all of these lines are still part of the blockquote, even without the > character in front.
To end the blockquote, just put a blank line before the following paragraph.
Preformatted Text If you want some text to show up exactly as you write it, without Markdown doing anything to it, just indent every line by at least 4 spaces (or 1 tab).
This line won't *have any markdown* formatting applied.
I can even write <b>HTML</b> and it will show up as text.
This is great for showing program source code, or HTML or even
Markdown. <b>this won't show up as HTML</b> but
exactly <i>as you see it in this text file</i>.
As a shortcut you can use backquotes to do the same thing while
inside a normal pargraph. This won't be *italic* or **bold**
at all.
Output:
This line won’t have any markdown formatting applied. I can even write HTML and it will show up as text. This is great for showing program source code, or HTML or even Markdown. this won’t show up as HTML but exactly as you see it in this text file. As a shortcut you can use backquotes to do the same thing while inside a normal pargraph. This won’t be italic or bold at all.
Lists * an asterisk starts an unordered list * and this is another item in the list + or you can also use the + character – or the – character
To start an ordered list, write this:
- this starts a list with numbers
- this will show as number “2”
- this will show as number “3.”
- any number, +, –, or * will keep the list going.
- just indent by 4 spaces (or tab) to make a sub-list
- keep indenting for more sub lists
- here i’m back to the second level Output:
- just indent by 4 spaces (or tab) to make a sub-list
an asterisk starts an unordered list and this is another item in the list or you can also use the + character or the – character To start an ordered list, write this:
this starts a list with numbers this will show as number “2” this will show as number “3.” any number, +, –, or * will keep the list going. just indent by 4 spaces (or tab) to make a sub-list keep indenting for more sub lists here i’m back to the second level Tables You can create tables using pipes and dashes like this:
First Header | Second Header |
---|---|
Content Cell | Content Cell |
Content Cell | Content Cell |
Output:
First Header Second Header Content Cell Content Cell Content Cell Content Cell
You can use markdown syntax within table cells for formatting:
First Header | Second Header |
---|---|
Content Cell | Content Cell |
Content Cell | Content Cell |
Output:
First Header Second Header Content Cell Content Cell Content Cell Content Cell
You can also create tables using HTML code.
Headers Just put 1 or more dashes or equals signs (—– or ===) below the title.
This is a huge header
this is a smaller header
Output:
This is a huge header this is a smaller header Horizontal Rule Just put three or more *’s or –’s on a line:
Output:
Or, you can use single spaces between then, like this:
Output:
or
Output:
Make sure you have a blank line above the dashes, though, or else:
you will get a header
Output:
you will get a header Images To include an image, just put a “!” in front of a text link:
Output:
The “alternate text” will show up if the browser can’t load the image.
You can also use a title if you want, like this:
Output:
To reference an attached image, just use the img macro. You can add more attributes:
[[img src=attached-image.jpg alt=foobar]] Output:
Escapes What if you want to just show asterisks, not italics?
- this shows up in italics: a happy day
- this shows the asterisks: *a happy day* Output:
this shows up in italics: a happy day this shows the asterisks: a happy day The backslashes will disappear and leave the asterisks.
You can do the same with any of the characters that have a special meaning for Markdown.
HTML tags may need to be escaped. will be interpreted as a bold tag. Entity codes will be used.
this will be bold you should escape <unknown> tags < special entities work < if you want to escape it Output:
this will be bold
you should escape
Individual ampersands (&) and less-than signs (<) are fine, they will be shown as expected.
More Headers More ways of doing headers:
this is a huge header
this is a smaller header
this is even smaller
more small
even smaller
smallest still: <h6>
header
Output:
this is a huge header this is a smaller header this is even smaller more small even smaller smallest still:
header You can use up to 6 # characters at the beginning of the line.
Code Highlighting The Code highlighting used in the newforge is based on (http://www.freewisdom.org/projects/python-markdown/CodeHilite). It follows the same syntax as regular Markdown code blocks, except that there are two ways to tell the highlighter what language to use for the code block.
If the first line of the codeblock contains a shebang, the language is derived from that and line numbers are used.
#!/usr/bin/python
# Code goes here ...
Output:
1 2 #!/usr/bin/python
Code goes here …
If the first line contains a shebang, but the shebang line does not contain a path (a single / or even a space) or If the first line begins with three or more colons, the text following the colons identifies the language. In both cases, the first line is removed from the code block before processing.
:::python
# Code goes here ...
Output:
Code goes here …
You can also designate a code block by surrounding it with lines of tildes. The type of code highlighting to apply will be inferred based on the code within, or you can specify like above.
My code
Output:
My code Includes You can embed another wiki page directly:
[[include ref=SamplePage]] No example output is available for this one because it only works on real wiki pages. Try it in your wiki!
Neighborhood Notifications You can list updates from all projects in a neighborhood by tool type. Max_number (default is 5) and sort (default is pubdate) are optional:
[[neighborhood_feeds tool_name=Wiki max_number=10 sort=pubdate]] Neighborhood Blog Posts You can view blog posts from all projects in a neighborhood. Max_number (default is 5) and sort (default is timestamp) are optional:
[[neighborhood_blog_posts max_number=10 sort=timestamp]] Project Blog Posts You can view blog posts from all blogs in a project. Max_number (default is 5), mount point (leave empty to view posts from all blog tools in a project), and sort (default is timestamp) are optional:
[[project_blog_posts max_number=10 sort=timestamp mount_point=news]] Download Button You can display a download button that links to the best download available for the active project. Please note that if you use this macro and there is no download associated with your project, the button will not appear.
[[download_button]] Project Screenshots You can show all the screenshots for the current project as thumbnails that are linked to the full-size image.
[[project_screenshots]] Thanks Thanks to John Gruber and Aaron Swartz for creating Markdown.
This page is based on some examples from Greg Schueler, greg@vario.us
Guides Rubyonrails Migrations (en and CN)
guides rubyonrails Migrations (EN and CN)
Migrations
Migrations are a convenient way for you to alter移动your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。
Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行rake db:migrate。Active Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。It will also update your db/schema.rb file to match the structure of your database.它还会更新你的db/schema.rb文件使其于你的数据库结构相匹配。
Migrations also allow you to describe these transformations using Ruby.Migrations同样允许你使用Ruby来描述这些转换。The great thing about this is that (like most of Active Record’s functionality) it is database independent: you don’t need to worry about the precise syntax of CREATE TABLE any more than you worry about variations on SELECT (you can drop down to raw SQL for database specific features).值得高兴的事情是(就像大多数的Active Record’s工厂)它是与数据独立的:你再也不需要担心准确的语法来CREATE TABLE也不需要担心SELECT 的变化。For example you could use SQLite3 in development, but MySQL in production.例如你可以使用SQLite3开发,但是在发布的产品中使用MySQL。
You’ll learn all about migrations including:下面你将了解到的migrations包括:
- The generators you can use to create them 你可以使用generators来创建他们(数据库表单)
- The methods Active Record provides to manipulate操纵your database Active Record提供方法来操纵你的数据库
- The Rake tasks that manipulate them 使用Rake命令操作这些(迁移)
- How they relate to schema.rb 它们是如何映射到schema.rb
1 Anatomy of a Migration
Before we dive into the details of a migration, here are a few examples of the sorts of things you can do:在深入migration的详细介绍之前,下面有一系列的例子你可以尝试一下:
class
CreateProducts
<
ActiveRecord::Migration
def
up
create_table
:products
do
|t|
t.string
:name
t.text
:description
t.timestamps
end
end
def
down
drop_table
:products
end
end
This migration adds a table called products with a string column called name and a text column called description.这次migration添加了一个名叫products的表它有一个叫name的字符串的列和一个叫description的文本框的列。A primary key column called id will also be added, however since this is the default we do not need to ask for this.一个名叫id的主键列也被添加,然而因为这是默认操作的不需要我们刻意添加。The timestamp columns created_at and updated_at which Active Record populates automatically will also be added.还添加了timestamp字段,products
表单
会在created_at和updated_at的时候通过Active Record自动填充timestamp字段。Reversing this migration is as simple as dropping the table.撤销这次migration就像dropping这个表。
Migrations are not limited to changing the schema.Migrations不限制更改schema。You can also use them to fix bad data in the database or populate new fields:你可以使用(schema)它们来修复坏的数据或者添加新的字段:
class
AddReceiveNewsletterToUsers
<
ActiveRecord::Migration
def
up
change_table
:users
do
|t|
t.boolean
:receive_newsletter,
:default
=>
false
end
User.update_all
[“receive_newsletter
=
?”,
true]
end
def
down
remove_column
:users,
:receive_newsletter
end
end
Some caveats apply to using models in your migrations.一些在model的migrations中的注意事项。
This migration adds a receive_newsletter column to the users table.(上面)这个migration添加一个receive_newsletter字段到user表。We want it to default to false for new users, but existing users are considered to have already opted in, so we use the User model to set the flag to true for existing users.我们希望对于新用户默认设置receive_newsletter字段为fasle,但是存在的用户被认为已经有(自己的)选择,因此我们通过在User model中存在的用户设置为True的标识(来保留以后信息)。
Rails 3.1 makes migrations smarter by providing a new change method.Rails3.1通过提供一个新的change方法,使得migrations更加智能化。This method is preferred首选for writing constructive migrations (adding columns or tables).这个方法是用来做(数据库)结构迁移(添加或删除字段)的首选。The migration knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate down method.migration知道怎样迁移你的数据库以及不需要单独的编写down方法来处理回滚是的migration。
class
CreateProducts
<
ActiveRecord::Migration
def
change
create_table
:products
do
|t|
t.string
:name
t.text
:description
t.timestamps
end
end
end
1.1 Migrations are Classes Migrations是一个类
A migration is a subclass of ActiveRecord::Migration that implements two methods: up (perform the required transformations) and down (revert them).一个migration类是ActiveRecord::Migration的子类,它实现了两个方法:up(执行所请求的转换)和down(撤销所做的更改)。
Active Record provides methods that perform common data definition tasks in a database independent way (you’ll read about them in detail later):Active Record提供了了在数据库中执行常见数据定义的方法(你将会在后面看到详细的介绍)。
- create_table
- change_table
- drop_table
- add_column
- change_column
- rename_column
- remove_column
- add_index
- remove_index
If you need to perform tasks specific to your database (for example create a foreignkey constraint约束) then the execute function allows you to execute arbitrary SQL.如果你在你的数据库中需要处理特殊的任务(例如创建一个foreign key约束)那么execute功能允许你执行任意的SQL(语句)。A migration is just a regular Ruby class so you’re not limited to these functions. migration仅仅是一个Ruby类,因此你不必仅仅局限于现有的这些功能。For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).例如在添加了一个字段之后你可以添加代码来设置这个字段在存在记录中的值(如果在你的model中需要)。
On databases that support transactions处理办理with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction.当数据库支持通过声明来改变数据库的结构(例如PostgreSQL和SQLite3),会在包含在migration处理中(直接改变数据库的结构)。If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. 如果数据库不支持这样的功能(比如MySQL)然后migration会有部分失败——成功添加的(数据)将不会回滚。You will have to unpick the changes that were made by hand.你必须手动的分开这些改变。
1.2 What’s in a Name (数据库文件)名称中的信息
Migrations are stored in files in db/migrate, one for each migration class.Mgirations以单个文件的形式被存放在文件夹db/migrate中。The name of the file is of the form YYYYMMDDHHMMSS_create_products.rb, that is to say a UTC timestamp时间戳identifying确定the migration followed by an underscore下划线followed by the name of the migration.(migration)文件是以form命名的,还看见了一个在migration完成之时的时间戳接着是下划线接着是migration的名字。The name of the migration class (CamelCased version) should match the latter part of the file name. migraiton类(使用驼峰命名法)的名字应该和migration文件的名称的最后部分相匹配。For example 20080906120000_create_products.rb should define CreateProducts and 20080906120001_add_details_to_products.rb should define AddDetailsToProducts. If you do feel the need to change the file name then you have to update the name of the class inside or Rails will complain about a missing class.如果你觉得需要改变migration文件的名字你必须同样修改文件里边migration类的名字,不然Rails会找不到migration类。
Internally Rails only uses the migration’s number (the timestamp) to identify them.在Rails内部只使用migration编号(时间戳)来确定他们。Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated.在Rails2.1之前migration编号从1开始然后在每次migration被创建过后增加。With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them.随着开发人员的增多这样会使的很容易产生冲突这就需要你回滚migrations和重新编号他们。developers With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them.Rails2.1的开发人员通过migraiton文件的创建时间指明每个文件在很大程度上避免了冲突(的发生)。You can revert to the old numbering scheme by adding the following line to config/application.rb.你可以还原带旧的版本通过在config/application.rb文件中添加如下行:
config.active_record.timestamped_migrations
=
false
The combination组合of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.时间戳和migrations的名字的组合使得Rails可以处理多个开发人员的普遍情况。
For example Alice adds migrations 20080906120000 and 20080906123000 and Bob adds 20080906124500 and runs it.比如Alice 添加了migration 20080906120000 and 20080906123000以及Bob添加并运行了20080906124500。Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Alice完成了他的更改并提交在他的migrationss中,并且Bob pull down了最新的更改。Rails knows that it has not run Alice’s two migrations so rake db:migrate would run them (even though Bob’s migration with a later timestamp has been run), and similarly migrating down would not run their down methods.
Of course this is no substitution for communication within the team.当然这些在团队交流中是不可避免的。For example, if Alice’s migration removed a table that Bob’s migration assumed to exist, then trouble would certainly strike.例如如果Alice在migration中移除了一个表但是Bob’s的migraion假设它还在,那么麻烦就来了。
1.3 Changing Migrations
Occasionally you will make a mistake when writing a migration.偶尔你在写入一个migration的时候犯个错误。If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run rake db:migrate.如果你已经运行了migration接着你不可能再去编辑和运行这个(错误的)migraion:Rails认为(你已经)运行了migration因此在你运rake db:migrate的时候不会做任何改变。You must rollback the migration (for example with rake db:rollback), edit your migration and then run rake db:migrate to run the corrected version.你必须回滚migation(例如rake db:rollback),编辑修改你的migraion然后运行正确的版本rake db:migrate。
In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines.在一般情况下,编辑存在的migrations不是一个好主意:因为这样你会给你自己或者你的合作成员产生额外的工作,头疼的原因是如果存在的migration已经在production机器中运行。Instead you should write a new migration that performs the changes you require.作为替代你应该编写一个新的migration来执行你需要的更改。Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless.编辑一个刚生成的还没有提交到软代码控制migration(或者更一般的情况还没有传播出你的开发机器)相对危害较轻。
1.4 Supported Types支持的类型
Active Record supports the following types:Active Record支持如下类型:
- :primary_key
- :string
- :text
- :integer
- :float
- :decimal 10进制
- :datetime
- :timestamp
- :time
- :date
- :binary
- :boolean
These will be mapped onto an appropriate underlying底层database type, for example with MySQL :string is mapped to VARCHAR(255).这些将会被映射为合适底层数据库的类型,例如使用MySQL :string类型将会映射成VARCHAR(255)。You can create columns of types not supported by Active Record when using the non-sexy syntax, for example你可以在创建Active Record不支持的字段,使用non-sexy语法,例如
create_table
:products
do
|t|
t.column
:name,
‘polygon’,
:null
=>
false
end
This may however hinder阻碍portability移植to other databases.不过这可能会阻碍移植到其它数据库。
2 Creating a Migration新建一个Migrateion
2.1 Creating a Model新建一个Model
The model and scaffold generators will create migrations appropriate for adding a new model.model和generators创建器会为添加的新的model创建合适的migrations。This migration will already contain instructions for creating the relevant有关table.这个migration已经包含在有关创建的的表的说明中。If you tell Rails what columns you want then statements声明for adding those will also be created. For example, running如果你告诉Rails将你随后声明的字段添加到创建migrations中,例如,运行
$
rails
generate
model
Product
name:string
description:text
will
create
a
migration
that
looks
like
this
(
rails
)将会新建一个像这样的
migraion
:
class
CreateProducts
<
ActiveRecord::Migration
def
change
create_table
:products
do
|t|
t.string
:name
t.text
:description
t.timestamps
end
end
end
You
can
append
as
many
column
name/type
pairs
as
you
want.
你可以随意增加字段或者类型对。
By
default
t.timestamps
(which
creates
the
updated_at
and
created_at
columns
that
are
automatically
populated
by
Active
Record)
will
be
added
for
you.
默认的
t.timestamps
(
Active
Record
会自动生成
updated_at和
created_at字段)会子添加在你的表中。
2.2 Creating a Standalone Migration新建一个独立的Migration
If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:如果你正在因为其他的目的新建migrations(例如添加一个字段到一个存在的表)你可以使用migration创建器:
$
rails
generate
migration
AddPartNumberToProducts
This will create an empty but appropriately named migration:这里会新建一个空的但是合适的migration:
class
AddPartNumberToProducts
<
ActiveRecord::Migration
def
change
end
end
If the migration name is of the form “AddXXXToYYY” or “RemoveXXXFromYYY” and is followed by a list of column names and types then a migration containing the appropriate add_column and remove_column statements will be created.如果migration命名为“AddXXXToYYY”或 “RemoveXXXFromYYY”格式并且后跟有一些字段名称或类型,那么一个migration包含合适的add_column和remove_column将会被新建。
$
rails
generate
migration
AddPartNumberToProducts
part_number:string
ps
:如果已经创建了一个
AddPartNumberToProducts
那么运行
rails
generate
migration
AddPartNumberToProducts
part_number:string
-f
进行覆盖新建
will
generate
将会生成:
class
AddPartNumberToProducts
<
ActiveRecord::Migration
def
change
add_column
:products,
:part_number,
:string
end
end
Similarly,
同样,
$
rails
generate
migration
RemovePartNumberFromProducts
part_number:string
generates
生成
class
RemovePartNumberFromProducts
<
ActiveRecord::Migration
def
up
remove_column
:products,
:part_number
end
def
down
add_column
:products,
:part_number,
:string
end
end
You
are
not
limited
to
one
magically
神奇
generated
column,
for
example
你不限制于一次只输入一个字段,例如
$
rails
generate
migration
AddDetailsToProducts
part_number:string
price:decimal
generates
生成
class
AddDetailsToProducts
<
ActiveRecord::Migration
def
change
add_column
:products,
:part_number,
:string
add_column
:products,
:price,
:decimal
end
end
As
always,
what
has
been
generated
for
you
is
just
a
starting
point.
You
can
add
or
remove
from
it
as
you
see
fit.
一如往常,使用
migration
创建器生成的
migration
只是一个起点,你可以添加或删除其中的字段直到你满意为止。
The
generated
migration
file
for
destructive
migrations
will
still
be
old-style
using
the
up
and
down
methods.
在创建的
migration
文件中对
migrations
进行破坏性的操作仍然使用老式的
up
和
down
方法。
This
is
because
Rails
doesn
’
t
know
the
original
data
types
defined
when
you
made
the
original
changes.
这是因为当你对初始
migration
做了更改,
Rails
不知道原始的数据类型定义。
3 Writing a Migration
Once you have created your migration using one of the generators it’s time to get to work!一旦你已经使用一种创建器创建了你的migration,现在是时候开始工作了。
3.1 Creating a Table创建一个表
Migration method create_table will be one of your workhorses. A typical use would be Migration方法create_table将会是一个你的驮马。下面是他的典型形式
create_table
:products
do
|t|
t.string
:name
end
which creates a products table with a column called name (and as discussed below, an implicit id column).上面的代码会创建一个products表,其中包含了一个name字段(和下面讨论的一样,一个隐藏的id字段)
The object yielded to the block allows you to create columns on the table.这个migration类产生代码块让你创建表中的字段。There are two ways of doing this: The first (traditional) form looks like有两种方法创建字段:首先(传统的)方式看起来像这样
create_table
:products
do
|t|
t.column
:name,
:string,
:null
=>
false
end
the second form, the so called “sexy” migration, drops the somewhat redundant column method.第二种方式,也称为“sexy” migration,丢掉了有些冗余的column方法。Instead, the string, integer, etc. methods create a column of that type.作为替代,如string,interger等等方法创建相应类型的字段。Subsequent parameters are the same.其后的参赛也在同一个字段中。
create_table
:products
do
|t|
t.string
:name,
:null
=>
false
end
HABTM,
hasAndBelongsToMany
这是
Active
Recored
功能里面比较
“
复杂
”
的一个东西。简单的来说,就是你有一个帖子,你给这个帖子定义了多个
tag
,
但是实际上
tag
也是独立的,一个
tag
会包含很多个不同的帖子。
By
default
create_table
will
create
a
primary
key
called
id.
通过默认的
create_table将会创建一个叫做id的主键。
You
can
change
the
name
of
the
primary
key
with
the
:primary_key
option
(don
’
t
forget
to
update
the
corresponding
相应的
model)
or
if
you
don
’
t
want
a
primary
key
at
all
(for
example
for
a
HABTM
join
table)
you
can
pass
:id => false.
你可以更改主键的名字通过使用
:primary_key选项(不要忘记更新其相应的model)或者你根本不想要主键(例如添加一个HABTM关系到表中)你可以通过:id => false。If
you
need
to
pass
database
specific
options
you
can
place
an
SQL
fragment
in
the
:options
option.
For
example
如果你需要数据库的特殊选项你可以放置一个
SQL
片段在
:options选项中。例如
create_table
:products,
:options
=>
“ENGINE=BLACKHOLE”
do
|t|
t.string
:name,
:null
=>
false
end
will append ENGINE=BLACKHOLE to the SQL statement used to create the table (when using MySQL the default is ENGINE=InnoDB).将会添加ENGINE=BLACKHOLE到SQL声明中用于创建表单(当使用MySQL默认的(ENGINE)是InnoDB)。
3.2 Changing Tables更改表单
A close cousin of create_table is change_table, used for changing existing tables. create_table的一个近亲是change_table,用于更改存在的表单。It is used in a similar fashion to create_table but the object yielded to the block knows more tricks.它使用的是与create_table类似的方法,但是其类产生的块能够知道更多的技巧。(如移除字段,重命名字段)For example例如
change_table
:products
do
|t|
t.remove
:description,
:name
t.string
:part_number
t.index
:part_number
t.rename
:upccode,
:upc_code
end
removes the description and name columns, creates a part_number column and adds an index on it. Finally it renames the upccode column.移除description字段和name字段,创建一个part_number字段并且添加一个index(索引)给part_number。最后它重命名了:upccode
。
This is the same as doing下面的代码也可以达到同样的效果
remove_column
:products,
:description
remove_column
:products,
:name
add_column
:products,
:part_number,
:string
add_index
:products,
:part_number
rename_column
:products,
:upccode,
:upc_code
You don’t have to keep repeating the table name and it groups all the statements related to modifying one particular table.你不必重复表单名称和组相关的所有声明,(而只需要重复)修改表的一部分。The individual个别transformation转换names are also shorter, for example remove_column becomes just remove and add_index becomes just index.个别的转换方法的名称也更加简短了,例如remove_column变成remove,add_index变成index。##这里是说的新旧两种方法比较
3.3 Special Helpers个别的Helpers
Active Record provides some shortcuts for common functionality.Active Record提供了一些一般功能的快捷操作。It is for example very common to add both the created_at and updated_at columns and so there is a method that does exactly that:例如很常见的通过created_at和updated_at添加字段等等,这里是一个方法的简要例子
create_table
:products
do
|t|
t.timestamps
end
will create a new products table with those two columns (plus the id column) whereas.将会新建一个products表单,有两个字段(加上id字段)。
change_table
:products
do
|t|
t.timestamps
end
adds those columns to an existing table.添加一些字段到存在的表中。
The other helper is called references (also available as belongs_to). In its simplest form it just adds some readability另一个helper被称为references(也可用belongs_to)。在其最简单的形式,它仅仅增加一些可读性。
create_table
:products
do
|t|
t.references
:category
end
will create a category_id column of the appropriate type.将会创建一个适当形式的category_id字段。Note that you pass the model name, not the column name. Active Record adds the id for you. 注意那是你关联的model名称,不是字段名称。Active Record会给你添加id。If you have polymorphic多态性belongs_to associations then references will add both of the columns required:
create_table
:products
do
|t|
t.references
:attachment,
:polymorphic
=>
{:default
=>
‘Photo’}
end
will add an attachment_id column and a string attachment_type column with a default value of ‘Photo’.将会添加一个attachment_id 和一个默认值是‘Photo’的字符串attachment_type字段。
The references helper does not actually create foreign key constraints for you. You will need to use execute for that or a plugin that adds foreignkeysupport.references helper没有真正创建外键约束给你。你需要使用execute或者一个plugin来添加外键支持。
If the helpers provided by Active Record aren’t enough you can use the execute function to execute arbitrary SQL.如果Active Record提供的helper不足以满足,你可以使用execute功能来执行任意SQL。
For more details and examples of individual个别methods check the API documentation, in particular the documentation for ActiveRecord::ConnectionAdapters::SchemaStatements (which provides the methods available in the up and down methods), ActiveRecord::ConnectionAdapters::TableDefinition (which provides the methods available on the object yielded by create_table) and ActiveRecord::ConnectionAdapters::Table (which provides the methods available on the object yielded by change_table).
3.4 Writing Your change Method
The change method removes the need to write both up and down methods in those cases that Rails know how to revert the changes automatically. change(migration类方法)删除需要写进up和down的方法,那样Rails知道怎样自动撤销更改。Currently, the change method supports only these migration definitions:目前,change方法支持如下的migration定义:
- add_column
- add_index
- add_timestamps
- create_table
- remove_timestamps
- rename_column
- rename_index
- rename_table
If you’re going to use other methods, you’ll have to write the up and down methods normally.如果你打算使用其他方法,你可以常规的写up和down方法。
3.5 Writing Your down Method
The down method of your migration should revert the transformations done by the up method.迁移的down方法应该撤销up方法做的转换。In other words the database schema架构should be unchanged if you do an up followed by a down.换句话说数据库架构应该没有改变,如果你在up方法过后又执行down方法。For example if you create a table in the up method you should drop it in the down method. It is wise to do things in precisely the reverse order to in the up method.例如如果你创建使用up方法一个表你应该在down方法中去除它。For example
class
ExampleMigration
<
ActiveRecord::Migration
def
up
create_table
:products
do
|t|
t.references
:category
end
#add
a
foreign
key
execute
<<-SQL
ALTER
TABLE
products
ADD
CONSTRAINT
fk_products_categories
FOREIGN
KEY
(category_id)
REFERENCES
categories(id)
SQL
add_column
:users,
:home_page_url,
:string
rename_column
:users,
:email,
:email_address
end
def
down
rename_column
:users,
:email_address,
:email
remove_column
:users,
:home_page_url
execute
“ALTER
TABLE
products
DROP
FOREIGN
KEY
fk_products_categories”
drop_table
:products
end
end
Sometimes your migration will do something which is just plain irreversible不可逆转的, for example it might destroy some data.有时候你的migration将会做一些不可逆转的事情。例如它可能会删除一些数据。In cases like those when you can’t reverse the migration you can raise ActiveRecord::IrreversibleMigration from your down method.在这样的情况中你不能撤销migration你可以在你的down方法中raise ActiveRecord::IrreversibleMigration。If someone tries to revert your migration an error message will be displayed saying that it can’t be done.如果有人尝试撤销你的migration那么一个错误消息将会被显示“it can’t be done”。
4 Running Migrations
Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. Rails提供给migrations一组rake任务,它大致归纳于运行若干的migrations。The very first migration related rake task you use will probably be db:migrate.你使用的非常靠前的migration相应于rake的任务恰好是db:migrate。In its most basic form it just runs the up method for all the migrations that have not yet been run.其中它是最基本的形式,它仅仅运行migrations 还没被运行的up方法。If there are no such migrations it exits.如果没有这样的迁移它将会退出。
Note that running the db:migrate also invokes the db:schema:dump task, which will update your db/schema.rb file to match the structure of your database.注意运行db:migrate也调用了db:schema:dump任务,它将会更新你的db/schema.rb来和你的数据库匹配。
If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version.如果你特别(指定了)一个目标版本,Active Record将会运行所请求的migrations(up或down)直到它已经达成了(与)指定版本(的匹配)。The version is the numerical prefix on the migration’s filename.版本号是migration‘s文件的数字前缀。For example to migrate to version 20080906120000 run例如迁移到20080906120000版本
$
rake
db:migrate
VERSION=20080906120000
If
this
is
greater
than
the
current
version
(i.e.
it
is
migrating
upwards)
this
will
run
the
up
method
on
all
migrations
up
to
and
including
20080906120000,
if
migrating
downwards
this
will
run
the
down
method
on
all
the
migrations
down
to,
but
not
including,
20080906120000.
如果执行的版本比当前版本更优(新)
(i.e.
it
is
migrating
upwards)
这将会执行包含
20080906120000
的所有版本的
migrations
up
方法,反之则执行不包括
20080906120000
的版本之外的所有版本的
migrations
down
方法。
4.1
Rolling
Back
A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it.一个常用的任务来回滚最新的migration,例如如果你在其中犯了一个错误希望Rails能够改正它。Rather than tracking down the version number associated with the previous migration you can run
$
rake
db:rollback
This
will
run
the
down
method
from
the
latest
migration.
这将会对最新的
migration
执行
down
方法。
If
you
need
to
undo
several
migrations
you
can
provide
a
STEP
parameter:
如果你需要撤销一系列的
migrations
你可以提供一个
STEP
参数:
$
rake
db:rollback
STEP=3
will
run
the
down
method
from
the
last
3
migrations.
将会对最近的
3
次迁移执行
down
方法。
The rake db:migrate:redo task is a shortcut for doing a rollback and then migrating back up again. rake db:migrate:redo任务是一个回滚后再次返回的快捷操作。As with the db:rollback task you can use the STEP parameter if you need to go more than one version back, for example通过db:rollback 任务你可以使用STEP参数如果你需要撤销超过一个版本,例如
$
rake
db:migrate:redo
STEP=3
Neither
of
these
Rake
tasks
do
anything
you
could
not
do
with
db:migrate,
they
are
simply
more
convenient
方便
since
you
do
not
need
to
explicitly
specify
the
version
to
migrate
to.
(通过)上两次
rake
任务,你没有对
db:migrate
任何事情,如果你不需要指定明确的版本来迁移这样做都非常方便。
Lastly, the db:reset task will drop the database, recreate it and load the current schema into it.最后,
db:reset任务将会drop数据库,重建它并且在其中导入正确的架构。
This
is
not
the
same
as
running
all
the
migrations
–
see
the
section
on
schema.rb.
这和运行所有的
migrations
不一样
——
阅读
schema.rb
章节。
4.2
Being
Specific
开始指定的
If you need to run a specific migration up or down the db:migrate:up and db:migrate:down tasks will do that. 如果你需要对一个指定的版本migration执行up或down,那么db:migrate:up and db:migrate:down任务会满足你。Just specify the appropriate version and the corresponding migration will have its up or down method invoked, for example指定适当的版本那么相应的migration就会调用它的up或down方法,例如
$
rake
db:migrate:up
VERSION=20080906120000
will
run
the
up
method
from
the
20080906120000
migration.
将会执行来自
20080906120000
版本的
migration
的
up
方法。
These
tasks
check
whether
the
migration
has
already
run,
so
for
example
db:migrate:up VERSION=20080906120000
will
do
nothing
if
Active
Record
believes
that
20080906120000
has
already
been
run.
这个任务会检查
migration
是否已经运行,如果
Active
Record
认为已经被运行过了
db:migrate:up VERSION=20080906120000将不会做任何事情。
4.3 Being Talkative开始唠叨
By default migrations tell you exactly what they’re doing and how long it took.通过默认的migrations会明确地告诉你他们做了什么以及花费了多长时间。A migration creating a table and adding an index might produce output like this一次迁移创建一个表并且添加一个index可能会产生这样的输出
20080906170109
CreateProducts:
migrating
—
create_table(:products)
–>
0.0021s
—
add_index(:products,
:name)
–>
0.0026s
20080906170109
CreateProducts:
migrated
(0.0059s)
Several methods are provided that allow you to control all this:一些方法(被证明)允许你像下面这样控制
- suppress_messages takes a block as an argument and suppresses抑制any output generated by the block. suppress_messages获取一个block作为一个参数并且抑制常规输出到block
- say takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent缩进or not. say获取一个message参数并且输出它。第二个布尔参数可以指定是否缩进
- say_with_time outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. say_with_time在文字旁边输出运行这个代码块花费了多少时间。如果block返回一个整数它假设这是受影响的代码行。
For example, this migration例如,这个migration
class
CreateProducts
<
ActiveRecord::Migration
def
change
suppress_messages
do
create_table
:products
do
|t|
t.string
:name
t.text
:description
t.timestamps
end
end
say
“Created
a
table”
suppress_messages
{add_index
:products,
:name}
say
“and
an
index!”,
true
say_with_time
‘Waiting
for
a
while’
do
sleep
10
250
end
end
end
generates the following output一般情况会得到如下输出
== CreateProducts: migrating =================================================
— Created a table
–> and an index!
— Waiting for a while
–> 10.0108s
–> 250 rows
== CreateProducts: migrated (10.0171s) =======================================
If you just want Active Record to shut up then running rake db:migrate VERBOSE=false will suppress抑制all output.如果你希望让所有Active Record闭嘴,那么运行rake db:migrate VERBOSE=false会抑制所有的输出。
5 Using Models in Your Migrations在你的migration中使用Moldels
When creating or updating data in a migration it is often tempting to use one of your models新建或更新一个migration的数据它通常会使你的models更有诱惑力。After all they exist to provide easy access to the underlying底层data.毕竟他们的存在提供了对底层数据的访问。This can be done, but some caution注意should be observed观察.这是可行的,但是有些地方需要注意。
For example, problems occur when the model uses database columns which are (1) not currently in the database and (2) will be created by this or a subsequent migration.例如,问题焦点当models使用数据库字段,但是migration(1)在数据库中没有这个字段而migration(2)将会被它本身的migration文件或者随后的migration创建。
Consider this example, where Alice and Bob are working on the same code base which contains a Product model:思考这个例子,Alice和Bob工作在同样的代码中,其主要包含一个Product模型:
Bob goes on vacation. Bob在休假
Alice creates a migration for the products table which adds a new column and initializes it. She also adds a validation to the Product model for the new column. Alice创建一个products表单的migration,其添加一个新的字段并初始化它。她也添加一个关于新字段的验证到Product模型。
rails generate migration AddFlagToProduct flag:int#然后再添加一部分这样通过创建器的话model文件也一起创建了
#
db/migrate/20100513121110_add_flag_to_product.rb
class
AddFlagToProduct
<
ActiveRecord::Migration
def
change
add_column
:products,
:flag,
:int
Product.all.each
{
|f|
f.update_attributes!(:flag
=>
‘false’)
}#
初始化置为
false
end
end
#
app/model/product.rb
class
Product
<
ActiveRecord::Base
validates
:flag,
:presence
=>
true
end
Alice
adds
a
second
migration
which
adds
and
initializes
another
column
to
the
products
table
and
also
adds
a
validation
to
the
Product
model
for
the
new
column.Alice
添加第二个
migration
,在这个
migration
中给
Products
表单添加并初始化了另一个字段,而且为
Product
model
的新字段添加并初始化了验证。
#
db/migrate/20100515121110_add_fuzz_to_product.rb
class
AddFuzzToProduct
<
ActiveRecord::Migration
def
change
add_column
:products,
:fuzz,
:string
Product.all.each
{
|f|
f.update_attributes!
:fuzz
=>
‘fuzzy’
}
end
end
#
app/model/product.rb
class
Product
<
ActiveRecord::Base
validates
:flag,
:fuzz,
:presence
=>
true
end
Both
migrations
work
for
Alice.
两个
migrations
都是
Alice
编写的。
Bob
comes
back
from
vacation
假期
and:Bob
休假回来然后接着下面的工作
updates
the
source
–
which
contains
both
migrations
and
the
latests
version
of
the
Product
model.
更新源代码
——
包含
migrations
和最新版本的
Product
模型。
- runs outstanding migrations with rake db:migrate, which includes the one that updates the Product model. 通过rake db:migrate执行突出(最新版本的migration)的migrations,其中包含了更新Product模型。
The migration crashes because when the model attempts尝试to save, it tries to validate the second added column, which is not in the database when the first migration runs.数据迁移冲突因为当model尝试保存时,它试图验证第二个添加的字段,然而它在第一次migration运行时不在数据库中。(没有弄明白呢感觉好像两个迁移是分开的才会出现这样的错误)
rake
aborted!
An
error
has
occurred,
this
and
all
later
migrations
canceled:
undefined
method
`fuzz'
for
#<Product:0x000001049b14a0>
A fix for this is to create a local model within the migration.解决这个错误是在migration创建一个本地的model。This keeps rails from running the validations, so that the migrations run to completion.这样在rails运行中保持了验证,使得migrations完成了验证。
When using a faux model, it’s a good idea to call Product.reset_column_information to refresh the ActiveRecord cache for the Product model prior前to updating data in the database.当使用一个人造的model,调用Product.reset_column_information来刷新在更新数据到数据库前的Product model的ActiveRecord缓存是一个好主意。
If Alice had done this instead, there would have been no problem:如果Alice这样做了就不会有问题了
#
db/migrate/20100513121110_add_flag_to_product.rb
class
AddFlagToProduct
<
ActiveRecord::Migration
class
Product
<
ActiveRecord::Base
end
def
change
add_column
:products,
:flag,
:int
Product.reset_column_information
Product.all.each
{
|f|
f.update_attributes!(:flag
=>
false)
}
end
end
class
AddFuzzToProduct
<
ActiveRecord::Migration
class
Product
<
ActiveRecord::Base
end
def
change
add_column
:products,
:fuzz,
:string
Product.reset_column_information
Product.all.each
{
|f|
f.update_attributes!
:fuzz
=>
‘fuzzy’
}
end
end
6 Schema Dumping and You架构模式和你
6.1 What are Schema Files for?
Migrations, mighty as they may be, are not the authoritative source for your database schema. Migrations,很可能不是你的数据库schema的授权源。That role falls to either db/schema.rb or an SQL file which Active Record generates by examining the database.这一规则影响到db/schema.rb和SQL文件它是Active Record创建器通过检查数据库生成的。They are not designed to be edited, they just represent the current state of the database.他们的设计(原意)是不被编辑的,他们仅仅是当前数据库状态的一个表现。
There is no need (and it is error prone) to deploy部署a new instance of an app by replaying the entire整个migration history. It is much simpler and faster to just load into the database a description of the current schema.这里没有必要(并且是错误倾向)去通过replaying整个migration历史来部署一个app的新的实例。更明智和更快捷的方式是load数据库的描述(也就是)当前schema。
For example, this is how the test database is created: the current development database is dumped (either to db/schema.rb or db/development.sql) and then loaded into the test database.例如,这是测试数据库怎样被创建的:当前的开发数据库已经被转储(要么db/schema.rb 要么db/development.sql)然后被导入test数据库。
Schema files are also useful if you want a quick look at what attributes an Active Record object has. Schema文件在你希望快速的查看Active Record object有些什么属性的时候也很有帮助。This information is not in the model’s code and is frequently spread across several migrations but is all summed up in the schema file. 这些信息不是model中的代码并且频繁分布在多个migrations但是是所有架构文件的总结。The annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest. annotate_models 插件,它能够自动在每个model汇总的顶部的添加(更新)comments,可能你会有兴趣。
6.2 Types of Schema Dumps架构转储的形式
There are two ways to dump the schema. This is set in config/application.rb by the config.active_record.schema_format setting, which may be either :sql or :ruby.有两种方式来转储架构。这是(相关)设置在config/application.rb通过config.active_record.schema_format来设置,其形式要么是:sql要么是:ruby.
If :ruby is selected then the schema is stored in db/schema.rb. If you look at this file you’ll find that it looks an awful可怕lot like one very big migration:如果:ruby被选中那么schema被存储在db/schema.rb。如果你查看这个文件你将会发现它看起来就像一个很大的migration:
ActiveRecord::Schema.define(:version
=>
20080906171750)
do
create_table
“authors”,
:force
=>
true
do
|t|
t.string
“name”
t.datetime
“created_at”
t.datetime
“updated_at”
end
create_table
“products”,
:force
=>
true
do
|t|
t.string
“name”
t.text
“description”
t.datetime
“created_at”
t.datetime
“updated_at”
t.string
“part_number”
end
end
In many ways this is exactly what it is.在很多情况下它能够准确的(反应出schema)的信息。This file is created by inspecting the database and expressing its structure using create_table, add_index, and so on.这个文件被创建来检查数据库以及表达它的结构使用create_table, add_index等等。Because this is database independent it could be loaded into any database that Active Record supports.由于数据库的独立性它可以被导入到Active Record支持的任何数据库中。This could be very useful if you were to distribute an application that is able to run against multiple databases.这会非常有用如果你发行一个应用程序它可能面对多个数据库运行。
There is however a trade-off: db/schema.rb cannot express database specific items such as foreign key constraints, triggers or stored procedures.然而,有一个权衡:DB/ schema.rb不能表达数据库的具体项目,如外键约束,触发器或存储过程。While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database.虽然在一个migration你可以执行自定义的SQL表达式,schema储存器不能修复一些SQL的表达式(不能实现migration的rollback等功能)。If you are using features like this then you should set the schema format to :sql.如果你正在使用这样的特性接着你应该设置schema 格式为:sql。
Instead of using Active Record’s schema dumper the database’s structure will be dumped using a tool specific to that database (via the db:structure:dump Rake task) into db/#{Rails.env}_structure.sql. For example for PostgreSQL the pg_dump utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.
By definition this will be a perfect copy of the database’s structure but this will usually prevent loading the schema into a database other than the one used to create it.根据定义,这将是一个完美的复制数据库的结构,但通常会阻止加载到比其他用于创建一个数据库的架构。
6.3 Schema Dumps and Source Control
Because schema dumps are the authoritative source for your database schema, it is strongly recommended建议that you check them into source control.因为架构转储是你的数据库架构的授权源,强烈建议你检查他们到源控制。
7 Active Record and Referential参照Integrity完整
The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.
Validations such as validates :foreign_key, :uniqueness => true are one way in which models can enforce data integrity. The :dependent option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
Although Active Record does not provide any tools for working directly with such features, the execute method can be used to execute arbitrary SQL. There are also a number of plugins such as foreign_key_migrations which add foreign key support to Active Record (including support for dumping foreign keys in db/schema.rb).
HTML4和HTML5之间的10个主要不同
HTML4和HTML5之间的10个主要不同
HTML5是最新的HTML标准,或迟或早,所有的web程序员都会发现需要使用到这个最新的标准,而且,很多人都会感觉到,重新开发一个HTML5的网站,要比把一个网站从HTML4迁移到HTML5上容易的多,这是因为这两个版本之间有很大不同之处。
事实上,HTML5并没有对HTML4做什么重大的修改,它们很多东西都是相似的。
可是,其中有一些很重要的区别你需要知道。下面列出的就是一些HTML4和HTML5之间主要的不同之处(并不是全部,全部列出来是不可能的):
- HTML5标准还在制定中 这头一个不同之处显而易见,但非常重要,我需要先从它开始。也许你已经注意到了关于HTML5很酷的言论到处都是,但是事实情况是,HTML5是一个还未完成的标准。HTML4已经有10岁了,但它仍是当前正式的标准的事实没有改变。
另一方面,HTML5仍处在早期阶段,以后的修改会不断的出现。你必须考虑到这些,因为你在网站上使用的这些新增加或修改的网页元素会每年都出现一些变化,你需要不停的更新升级你的网站,这可不是你希望的。这就是目前为止,你最好在产品里使用HTML4,只在实验里使用HTML5的原因。
简化的语法 更简单的doctype声明是HTML5里众多新特征之一。现在你只需要写<!doctype html>,这就行了。HTML5的语法兼容HTML4和XHTML1,但不兼容SGML。
一个替代Flash的新 <canvas> 标记 对于Web用户来说,Flash既是一个惊喜,也是一种痛苦。有很多的Web开发人员对HTML5对Flash产生的威胁很不满。但对于那些忍受着要花几年时间加载和运行的臃肿的Flash视频的人来说,用新的 <canvas> 标记生成视频的技术已经到来。
目前, <canvas> 标记并不能提供所有的Flash具有的功能,但假以时日,Flash必将从web上淘汰。我们拭目以待,因为很多人还并不认同这种观点。
- 新的 <header> 和 <footer> 标记 HTML5的设计是要更好的描绘网站的解剖结构。这就是为什么这些<header> 和<footer> 等新标记的出现,它们是专门为标志网站的这些部分设计的。
在开发网站时,你不在需要用<div>标记来标注网页的这些部分。
- 新的 <section> 和 <article> 标记 跟<header> 和 <footer>标记类似,HTML5中引入的新的<section> 和 <article> 标记可以让开发人员更好的标注页面上的这些区域。
据推测,除了让代码更有组织外,它也能改善SEO效果,能让搜索引擎更容易的分析你的页面。
- 新的 <menu> 和 <figure> 标记 新的<menu>标记可以被用作普通的菜单,也可以用在工具条和右键菜单上,虽然这些东西在页面上并不常用。
类似的,新的 <figure> 标记是一种更专业的管理页面上文字和图像的方式。当然,你可以用样式表来控制文字和图像,但使用HTML5内置的这个标记更适合。
- 新的 <audio> 和 <video> 标记 新的<audio> 和 <video> 标记可能是HTML5中增加的最有用处的两个东西了。正如标记名称,它们是用来嵌入音频和视频文件的。
除此之外还有一些新的多媒体的标记和属性,例如<track>,它是用来提供跟踪视频的文字信息的。有了这些标记,HTML5使Web2.0特征变得越来越友好。问题在于,在HTML5还未被广泛的接受之前,Web2.0还是老的Web2.0。
全新的表单设计 新的 <form> 和 <forminput> 标记对原有的表单元素进行的全新的修改,它们有很多的新属性(以及一些修改)。如果你经常的开发表单,你应该花时间更详细的研究一下。
不再使用 <b> 和 <font> 标记 对我个人来说,这是一个让我不太理解的改动。我并不认为去除 <b> 和 <font>标记会带来多大的好处。我知道,官方的指导说这些标记可以通过CCS来做更好的处理,但这样一来,为了在文章一两个地方出现的这种标记,你就需要在独立的css和文本两个地方来实现这一的功能,岂不笨拙。也许我们以后会习惯这种方法。
不再使用 <frame>, <center>, <big> 标记 事实上,我已经记不清曾经何时用过这些标记了,所以,我并不为去除这些标记感到悲哀。相同的原因,有更好的标记能实现它们的功能——这很好,任何作废的标记从标准中剔除都是受欢迎的。
这10个HTML5和HTML4之间的不同只是整个新的规范中的一小部分。除了这些主要的变动外,我还可以略提一下一些次要的改动,比如修改了<ol> 标记的属性,让它能够倒排序,对<u>标记也做了修改。
所有这些次要的改动数量众多。而且新的修改也在不断的增加,因此,如果你想实时跟踪最新的动向,你需要经常的查看w3.org的HTML4 和 HTML5之间的不同这个页面。如果你很心急,想在你的工作中使用这些新的标记和属性,我劝告你最好只是做实验,原因已经说的很清楚了,这些新标记和新属性在将来也许会有很大的改变,所以,除非你不断的更新你的代码,它们很可能会过期失效。
尽管如今大多数流行的浏览器的最新版都支持HTML5,但有些新的(或修改的)标记和属性它们并不支持,所以你的网页在用户的屏幕上有可能前后显示的不一致。耐心等待,等待HTML5真正可以实用时候。目前还不是时候。
日历js代码
苹果饥饿营销自造尴尬 “黄牛”堵门叫卖iPhone4s
苹果饥饿营销自造尴尬 “黄牛”堵门叫卖iPhone4s
iPhone4s已经在几十个国家和地区上市销售,并于本月11日正式登陆中国香港,但因为iPhone4s在内地上市时间迟迟未定,已经望穿秋水的“果粉”只能继续守候下去。然而想继续通过饥饿营销积累客户的苹果,不得不面对自己制造的“尴尬”。随着昨日大量港版iPhone4s抵京,大批黄牛来到苹果三里屯官方零售店外堵门叫卖。
“iPhone4s要吗?港版无锁的!”在苹果三里屯零售店门口,一群手拿着苹果白色包装盒的黄牛,不断地招揽着过往行人,一发现有顾客从苹果店走出来,就立刻围上去。
一位刘姓男子透露,他卖的iPhone4s是昨天刚从深圳发到北京的,黑色16G版的现在卖5850元,不讲价。虽然比起4150元的官方定价,这名黄牛一口气加了1700元,但他表示这个价格还是港版上市后降了的,以前澳版的iPhone4s要价都超过6500元。这名黄牛“备货”十分充足,身后的书包里装了近百台iPhone4s。
隔着一道通明的玻璃墙,苹果店员自然对门外的黄牛一清二楚,除了在店门口立起一块不大的警示牌提醒消费者黄牛手中的iPhone4s“来源不明”外,苹果对黄牛们似乎无能为力。“iPhone4s的上市时间还没定,所以现在我们还没有相应的预订服务。”三里屯苹果旗舰店的一位负责人坦言,想从苹果官方渠道买到iPhone4s,可能还要等上很长一段时间。
面对苹果没货,黄牛堵门的情况,不禁有“果粉”发出疑问,“为什么iPhone4s在国内迟迟不能上市”。对此,苹果中国的负责人解释称,iPhone4s进入国内需要经过多重审核,以取得工信部的入网许可证,所以上市时间迟迟未定。然而苹果这一解释无法服众,一位业内人士表示,工信部的入网许可并不难拿,一般企业提出申请后10个工作日就能通过相应审核,而iPhone4s从发布至今已经1个多月。
“苹果之所以迟迟不定iPhone4s在中国内地的上市时间,无非还是想玩饥饿营销,吊起消费者的胃口。”这位业内人士直言,但如此营销造成黄牛堵门,不知苹果有何感想。
码农熬夜指南
码农熬夜指南
Getting Started With Rails-(EN-CN)
Getting Started with Rails-(EN-CN)
Getting Started with Rails
This guide covers getting up and running with Ruby on Rails. After reading it, you should be familiar with:本指导手册覆盖了入门和运行ruby on rails,通过阅读本指导,你会了解到:
- Installing Rails, creating a new Rails application, and connecting your application to a database
- The general layout#布局of a Rails application 应用程序的一般布局
- The basic principles of MVC (Model, View Controller) and RESTful design MVC的基本原则和RESTful设计(理念)
- How to quickly generate the starting pieces of a Rails application 如何快捷的开始一个Rails应用
ThisGuideisbasedonRails3.1.SomeofthecodeshownherewillnotworkinearlierversionsofRails.这个指导手册适用与Rails3.1,有些代码在Rails早期版本可能会不工作。
1 Guide Assumptions假设
1 Guide Assumptions
This guide is designed for beginners who want to get started with a Rails application from scratch#刻痕. It does not assume#假设that you have any prior#预先experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:本指导设计给那些想大概的了解Rails应用创建的初学者。这里假设你对Rails没有任何预先的了解。不管怎样要得到(本手册的)知识,你需要预先安装:
- The Ruby language version 1.8.7 or higher
Note that Ruby 1.8.7 p248 and p249 have marshaling#封送处理bugs that crash#意外崩溃紧急Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. Ruby 1.8.7 p248 and p249在发行版1.8.7-2010.02 已经得到解决。On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
- The RubyGemspackaging system
- If you want to learn more about RubyGems, please read the RubyGemsUserGuide 如果你想了解更多关于RubyGems,请阅读RubyGemsUserGuide
- A working installation of the SQLite3Database
Rails is a web application framework running on the Ruby programming language. If you have no prior experience with Ruby, you will find a very steep#陡learning curve diving straight into Rails. There are some good free resources on the internet for learning Ruby, including: Rails是一个基于Ruby程序语言的web程序框架。如果你没有预先的学习ruby,你会发现直接的入门Rails学习很有跨度。这里有一些学习ruby的免费互联网资源。
2 What is Rails?什么Rails
Rails is a web application development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions#完成about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Experienced#经验丰富的Rails developers also report that it makes web application development more fun.Rails是一个使用Ruby语言编写的的web框架应用程序。其设计目的是为了让每个着手开始编写web应用程序的开发人员更加容易的完成工作。它允许你写最少的代码完成超过其他任何语言和框架所完成的工作。经验丰富的Rails开发人员还告诉我们通过Rails使设计web应用程序更快乐。
Rails is opinionated#自以为是software. It makes the assumption that there is a “best” way to do things, and it’s designed to encourage#鼓励that way – and in some cases to discourage alternatives替代品. If you learn “The Rails Way” you’ll probably discover a tremendous increase in productivity. If you persist in bringing old habits#习惯from other languages to your Rails development, and trying to use patterns模式you learned elsewhere, you may have a less happy experience.Rails是一个自以为是的软件。它使得我们以最好的方式去做事情,它还鼓励——and in some cases to discourage alternatives,如果你学习“Rails方式”你将会适时的发现生产力的巨大增长。如果你固守在来自其他语言的旧的习惯去进行你的Rails开发,以以它地方学来的模式尝试Rails,那么你将会得到很少的快乐的经历。
The Rails philosophy理念includes several guiding principles:Rails理念包含几个指导原则
- DRY – “Don’t Repeat Yourself” – suggests that writing the same code over and over again is a bad thing. 不要自己重复——建议一次又一次编写同样的代码是一件坏事请。
- Convention#约定Over Configuration(约定优于配置) – means that Rails makes assumptions about what you want to do and how you’re going to do it想做怎么做, rather than requiring you to specify every little thing through endless configuration files. 约定优于配置——意思是Rails对于你想做什么以及你想怎么做,你刻意的做很少的事情比编写无尽的配置文件更好
- REST is the best pattern模式for web applications – organizing your application around resources#资源and standard HTTP verbs#动词is the fastest way to go. REST是开发web应用程序的最好模式——环绕resources和标准的HTTP动作组织你的应用程序
2.1 The MVC Architecture#MVC架构
At the core of Rails is the Model, View, Controller architecture, usually just called MVC. MVC benefits include:Model,View,Controller架构是Rails的核心,通常称之为MVC。
- Isolation分离of business logic逻辑from the user interface 界面#从用户界面的业务逻辑的分离
- Ease of keeping code DRY# “Don’t Repeat Yourself”
- Making it clear where different types of code belong for easier maintenance #维护明确代码的不同之处使之跟容易维护
2.1.1 Models
A model represents代表the information (data) of the application and the rules to manipulate that data.模型代表了应用程序的信息(数据)和操纵这些数据的规则。In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding#相应的database table. In most cases, each table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models.在Rails中,models主要用于管理数据表和相应的规则的互动。在大多数情况,在你数据库中的每个表都会和你的应用程序互动。你应用程序的逻辑业务将会集中放置在models中。
2.1.2 Views
Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that perform执行tasks related solely to the presentation#演示of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.View代表了应用程序的用户界面。在Rails中,Views通常是嵌入了执行演示数据任务的Ruby代码的HTML文件。Views完成了给web浏览器或者其他工具用于提出来自你的程序的请求提供数据。
2.1.3 Controllers
Controllers provide the “glue” between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.Controllers提供了models和视图间的粘合。在Rails中,controllers相应来自web浏览器请求的进程,向models询问数据以及传递数据给views用于演示。
2.2 The Components of Rails Rials的组件
Rails ships as many individual components.Rails关联着许多独立的组件。Each of these components are briefly explained below. 对这些组建在下面给出简要的解释。If you are new to Rails, as you read this section, don’t get hung up on the details of each component, as they will be explained in further detail later. 如果你是Rails的新人,当你阅读到这个部分,不要抛弃任何一个组件的描述,而且他们还会做进一步解释。For instance, we will bring up Rack applications, but you don’t need to know anything about them to continue with this guide.例如,我们将会构造应用骨架,但是在这里,你不需要知道关于他们的更进一步的任何知识。
- Action Pack动作行为组
- Action Controller 行为控制
- Action Dispatch 行为传输
- Action View 行为视图
- Action Mailer 行为信封
- Active Model
- Active Record
- Active Resource
- Active Support
- Railties
2.2.1 Action Pack
Action Pack is a single gem that contains Action Controller, Action View and Action Dispatch. The “VC” part of “MVC”.Action Pack是一个单独的包它包含了Action Controller, Action View and Action Dispatch传输调度。是MVC的VC部分
2.2.1.1 Action Controller
Action Controller is the component that manages the controllers in a Rails application.The Action Controller framework processes incoming requests to a Rails application, extracts parameters, and dispatches them to the intended action. Services provided by Action Controller include session management, template rendering, and redirect management. Action Controller是在Rails中管理控制的组件。The Action Controller框架进程收到Rails应用程序的请求,提取参数,以及调度他们到具有相应义务的动作。这些服务是由Action Controller包含了会话管理部分,模板渲染(翻译),重定向部分。
2.2.1.2 Action View
Action View manages the views of your Rails application. It can create both HTML and XML output by default. Action View manages rendering templates, including nested and partial templates, and includes built-in AJAX support. View templates are covered in more detail in another guide called LayoutsandRendering.Action View管理你的Rails应用程序的视图。它可以以创建HTML和XML作为默认输出。Action View管理模板渲染,包含嵌套和组装模板,还包含了内置的AJAX支持。
2.2.1.3 Action Dispatch
Action Dispatch handles routing of web requests and dispatches them as you want, either to your application or any other Rack application. Rack applications are a more advanced topic and are covered in a separate guide called RailsonRack.行为调度处理了你的和其他部分的应用程序的路由——这些web请求以及一些你所想要的调度,
2.2.2 Action Mailer
Action Mailer is a framework for building e-mail services. You can use Action Mailer to receive and process incoming email and send simple plain text or complex multipart emails based on flexible templates.Action Mailer是一个营造E-mail服务的框架。你可以使用Action Mailer去发送、接收emial以及发送一些简单的计划文本或者基于灵活模板的复杂的多重的电子邮件。
2.2.3 Active Model
Active Model provides a defined interface between the Action Pack gem services and Object Relationship Mapping gems such as Active Record. Active Model allows Rails to utilize other ORM frameworks in place of Active Record if your application needs this.Active Model提供了Action Pack gem服务和Object Relationship Mapping gems之间的接口定义,比如Active记录。Active Model允许Rails在Active Record部分去采用其他ORM框架如果你的应用程序需要。
2.2.4 Active Record
Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.Active Record是一个Rails应用程序的models根本。它提供独立的数据库,基于CRUD功能,先进(高级)的查找能力,和与另一个models关联的能力,几乎所有其他服务。
2.2.5 Active Resource
Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.Active Resource提供一个管理目标业务和RESTful web服务之间连接的框架。它实现了使用CRUD语义测绘web-base 资源到本地目标。
2.2.6 Active Support
Active Support is an extensive collection of utility classes and standard Ruby library extensions that are used in Rails, both by the core code and by your applications.Active Support是一个广泛收集实用工具类和标准的Ruby库的扩展,它们由的核心代码和您的应用程序(决定)。
2.2.7 Railties
Railties is the core Rails code that builds new Rails applications and glues the various frameworks and plugins together in any Rails application.Railties是在Rails代码中创建新Rails应用和在任何Rails应用中粘和各种插件在一起的核心。
2.3 REST
Rest stands for Representational State Transfer and is the foundation of the RESTful architecture.Rest作为具有代表性的状态传输是RESTful架构的基础。This is generally considered to be Roy Fielding’s doctoral thesis博士论文, ArchitecturalStylesandtheDesignofNetwork-basedSoftwareArchitectures. While you can read through the thesis, REST in terms of Rails boils down to two main principles:当你阅读这篇论文REST在Rails归纳为主要亮点原则:
- Using resource identifiers such as URLs to represent resources.使用资源标识符比如URLs去表现资源
- Transferring representations of the state of that resource between system components.转移(传送)在系统组件之间代表资源的状态
For example, the following HTTP request:
DELETE /photos/17
would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action – deleting that resource. (系统)将会明白参照ID为14的phone资源,注明删除该资源。REST is a natural style for the architecture of web applications, and Rails hooks into this shielding屏蔽you from many of the RESTful complexities and browser quirks.REST自然风格去架构web应用程序,通过组件这样的钩子,使你避免了许多复杂的RESTful和浏览器之间的差异。
If you’d like more details on REST as an architectural#架构风格style, these resources are more approachable平易近人than Fielding’s thesis:
- ABriefIntroductiontoREST by Stefan Tilkov
- AnIntroductiontoREST (video tutorial) by Joe Gregorio
- RepresentationalStateTransfer article in Wikipedia
- HowtoGETaCupofCoffee by Jim Webber, Savas Parastatidis & Ian Robinson
3 Creating a New Rails Project
If you follow this guide, you’ll create a Rails project called blog, a (very) simple weblog.如果你跟随这个指导,你将会创建一个叫做blog的Rails项目——一个非常简单的网络博客。Before you can start building the application, you need to make sure that you have Rails itself installed.当你准备开始构建这个项目之前你需要确保Rails已经完全安装。
3.1 Installing Rails安装Rails
In most cases, the easiest way to install Rails is to take advantage of RubyGems:在大多数情况下,最安装Rails简单的方式是通过方便的RubyGems
Usually
run
this
as
the
root
user:
#
gem
install
rails#
一般这样安装的
rails
都是最新的
release
版本
If
you
’
re
working
on
Windows,
you
can
quickly
install
Ruby
and
Rails
with
RailsInstaller.
如果你的工作环境是
Windows
,你可以通过
Rails
Installer
快速的安装
Rails
。
/////////////////////////////
###gem
help
commands
➜
~
gem
install
rails&&sudo
gem
install
rails
ERROR:
Could
not
find
a
valid
gem
‘rails’
(>=
0)
in
any
repository
ERROR:
While
executing
gem
…
(Gem::RemoteFetcher::FetchError)
Errno::ETIMEDOUT:
Connection
timed
out
–
connect(2)
(
http://rubygems.org/latest_specs.4.8.gz)
经检查和
GFW
无关,是
rubygems
的
DNS
调整问题
问题解决的最好方法方法
jhjguxin@jhjguxin-virtual-machine:~/blog$
gem
update
—system
ERROR:
gem
update
—system
is
disabled
on
Debian,
because
it
will
overwrite
the
content
of
the
rubygems
Debian
package,
and
might
break
your
Debian
system
in
subtle
ways.
The
Debian-supported
way
to
update
rubygems
is
through
apt-get,
using
Debian
official
repositories.
If
you
really
know
what
you
are
doing,
you
can
still
update
rubygems
by
setting
the
REALLY_GEM_UPDATE_SYSTEM
environment
variable,
but
please
remember
that
this
is
completely
unsupported
by
Debian.
jhjguxin@jhjguxin-virtual-machine:~/blog$
export
REALLY_GEM_UPDATE_SYSTEM=1
jhjguxin@jhjguxin-virtual-machine:~/blog$
gem
update
–
system
###
正是
gem
出问题了
结果直接用
sudo
apt-get
install
rails,
悲剧了
ubuntu
现在还是用的
rails2.3
的包然后导致,后面有些命令无法执行
3.2
Creating
the
Blog
Application
The best way to use this guide is to follow each step as it happens, no code or step needed to make this example application has been left out, so you can literally follow along step by step.学习(使用)本指导的最好方式是跟随这里描述的每一步,不写代码或者没有例子所需的步骤会使得这个例子被冷落,你可以根据文字描述的步骤一步接着一步的操作。If you need to see the completed code, you can download it from GettingStartedCode.如果你需要完整的代码你可以从这里下载GettingStartedCode。
To begin, open a terminal, navigate to a folder where you have rights to create files, and type:开始,打开一个terminal,导航至一个你有权限创建文件的文件夹,并输入:
rails
new
blog#—skip-bundle]
#
Don’t
run
bundle
install
这样在国内就不会由于连不上
gem
即便上能够上也会很慢半天没反映
#
请确保你的
rials
版本是
3.1
不然的话这只能用下面的了
The
guides
for
Rails
2.3
are
still
available
at
http://guides.rubyonrails.org/v2.3.11/
.
jhjguxin@jhjguxin-virtual-machine:~$
rails
new
blog#
这里建立的文件夹名称为
new
额估计是新版本发生了或者有些地方没弄通(
ubuntu
11.10
Rails
2.3.14
ruby
1.8.7
(2011-06-30
patchlevel
352)
[i686-linux]
)
jhjguxin@jhjguxin-virtual-machine:~$
rails
blog
jhjguxin@jhjguxin-Aspire-4750:~$
sudo
gem
install
json
-v
1.6.1
Invalid
gemspec
in
[/var/lib/gems/1.8/specifications/tilt-1.3.3.gemspec]:
invalid
date
format
in
specification:
“2011-08-25
00:00:00.000000000Z”
Invalid
gemspec
in
[/var/lib/gems/1.8/specifications/json-1.6.1.gemspec]:
invalid
date
format
in
specification:
“2011-09-18
00:00:00.000000000Z”
Invalid
gemspec
in
[/var/lib/gems/1.8/specifications/tilt-1.3.3.gemspec]:
invalid
date
format
in
specification:
“2011-08-25
00:00:00.000000000Z”
Invalid
gemspec
in
[/var/lib/gems/1.8/specifications/json-1.6.1.gemspec]:
invalid
date
format
in
specification:
“2011-09-18
00:00:00.000000000Z”
出现这个问题的时候我
是改
成
s.date
=
%
q{2011-09-18}
在
rails
3.1
中由于要创建
bundler
信息(
Gemfile
文件)会提示你安装
sqlite3
等数据库信息,估计也就是你应用程序所用到的一些必须的
modules
。
这里用的是
sqlite3
做数据库,提示的是安装的是
sqlite3.
sudo
apt-get
install
sqlite3
sqlite3-doc
libsqlite3-ruby
还是
ERROR:
Failed
to
build
gem
native
extension.
的话
sudo
apt-get
install
libxslt1-dev
libxml2-dev
libxslt-ruby
libxslt1.1
libxsltc-java-gcj
libxslt-ruby
libxslt1-dbg
libxsltc-java
ibxslthl-java
libxslt-ruby1.8
sudo
apt-get
install
libsqlite3-ruby
libsqlite3-dev
sudo
gem
install
therubyracer
####
对于本地安装的
rails
(就是爬不出去墙的)
sudo
gem
install
sqlite3-1.3.4.gem
成功了!
Your
bundle
is
complete!
Use
bundle</em></span></span></code><code></code><code><span style="color: #000000;"><span style="font-size: small;"><em>show</em></span></span></code><code></code><code><span style="color: #000000;"><span style="font-size: small;"><em>[gemname]
to
see
where
a
bundled
gem
is
installed.
This
will
create
a
Rails
application
called
Blog
in
a
directory
called
blog.
这里将创建一个名叫
Blog
的
Rails
应用程序在名称为
blog
的目录中。
You
can
see
all
of
the
switches
that
the
Rails
application
builder
accepts
by
running
rails new -h.
你可以通过运行
rails
new
-h
,查看
rails
应用程序创建器的所有命令(开关)。
After
you
create
the
blog
application,
switch
to
its
folder
to
continue
work
directly
in
that
application:
当你创建了这个
blog
程序,跳转到它所在的文件夹中(直接对这个程序编辑)
.
$cd
blog
In
any
case,
Rails
will
create
a
folder
in
your
working
directory
called
blog.
在任何情况下,
Rails
将会创建一个名为
blog
的文件夹在你的工作目录中。
Open
up
that
folder
and
explore
its
contents.
打开这个文件夹浏览其中的内容。
Most
of
the
work
in
this
tutorial
will
happen
in
the
app/
folder,
but
here
’
s
a
basic
rundown#
概述
on
the
function
of
each
folder
that
Rails
creates
in
a
new
application
by
default:
在这个体验中的大多数的工作都是在
app/
这个文件夹中完成的,这里对
Rails
创建的新应用中的每一个文件夹的功能做出了一个概述:
File/Folder | Purpose#目的 |
---|---|
Gemfile | This file allows you to specify what gem dependencies are needed for your Rails application. See section on Bundler, below.这个文件让你可以(添加)你的Rails所需要的特殊的gem依赖关系。 |
README | This is a brief instruction manual#手册for your application. You should edit this file to tell others what your application does, how to set it up, and so on.这是一个简单的说明手册。你需要编辑这个文件告诉其他人你的应用程序可以做什么,怎么安装等等。 |
Rakefile | This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.这个文件定位和载入能够在命令行中运行的任务。这个应该是域吧(任务定义)贯穿整个Rials的组件定义。除了修改Rakefile,你还需要添加你自己的任务的文件到你的应用程序的lib/tasks目录。 |
app/ | Contains the controllers, models, views and assets for your application. You’ll focus on this folder for the remainder of this guide.包含controllers, models, views和你应用程序的assets(资源),再接下面的手册中你主要的注意力应该放在这里。 |
config/ | Configure your application’s runtime rules, routes, database, and more.配置你的应用程序的运行的规则,(url)路由,数据库和其他。 |
config.ru | Rack configuration for Rack based servers used to start the application.基于Rack服务器使用这个应用程序的Rack配置 |
db/ | Shows your current database schema, as well as the database migrations. You’ll learn about migrations shortly.显示你当前的数据表,同样也显示数据迁移。你将会简单的了解关于(数据)迁移。 |
doc/ | In-depth documentation for your application.应用程序的(深入)全面的文档。 |
lib/ | Extended modules for your application (not covered in this guide).应用程序用到的扩展库(本手册没有涉及) |
log/ | Application log files.应用程序的日志文件 |
public/ | The only folder seen to the world as-is. Contains the static files and compiled assets.这是外部可见的唯一文件夹。包含静态文件和编译资源。 |
script/ | Contains the rails script that starts your app and can contain other scripts you use to deploy#部署配置or run your application.包含运行你的app的rails脚本,或者其他用来配置或运行你的应用程序的scripts。 |
test/ | Unit tests, fixtures, and other test apparatus. These are covered in TestingRailsApplications单元测试,fixtures,或者其他test工具。他们在TestingRailsApplications里面有完整的讲述。 |
tmp/ | Temporary files模板文件 |
vendor/ | A place for all third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.放置第三方代码的地方。在一个典型的Rails应用程序中,这里包含Ruby Gems,Rails源代码(如果你把Rails安装到你的项目中)还包含一些预先包装好的额外的插件 |
3.3
Configuring
a
Database
配置数据库
Just about every Rails application will interact with a database.基本上每个Rails应用程序都会和一个数据库互动。The database to use is specified in a configuration file, config/database.yml.使用的数据库在config/database.yml的配置文件中指定的。If you open this file in a new Rails application, you’ll see a default database configuration using SQLite3. The file contains sections for three different environments in which Rails can run by default:如果你在一个新的Rails应用程序中打开这个文件,你将会看到一个默认的数据库配置使用的是SQLite3.这个文件包含Rails可以默认运行的三个不同的环境会话。
- The development environment is used on your development computer as you interact manually with the application. 开发环境使用在开发计算机上,让你和你的应用程序手动交互
- The test environment is used to run automated tests. 测试环境用于运行自动测试
- The production environment is used when you deploy your application for the world to use.产品环境在你向外发布过后使用
3.3.1 Configuring an SQLite3 Database
Rails comes with built-in support for SQLite3, which is a lightweight serverless database application.Rails内置并支持SQLite3,SQLite3是一个轻量级的数据库服务器。While a busy production environment may overload SQLite, it works well for development and testing.但是一个繁忙的产品环境可能会导致SQLite超载,SQLite3更适合在开发或测试中使用。Rails defaults to using an SQLite database when creating a new project, but you can always change it later.Rails在创建一个新项目的时候默认使用一个SQLite数据库,但是你可以在之后随时更改。
Here’s the section of the default configuration file (config/database.yml) with connection information for the development environment:这是在开发环境中配置连接信息的默认配置文件的节选:
development:
adapter:
sqlite3
database:
db/development.sqlite3
pool:
5
timeout:
5000
In this guide we are using an SQLite3 database for data storage, because it is a zero configuration database that just works. 在这个手册中使用一个SQLite3数据库存存储数据,因为它不需要我们再去配置就能工作。Rails also supports MySQL and PostgreSQL “out of the box”, and has plugins for many database systems.Rails同样支持MySQL和PostgreSQL ,它还有许多支持其他数据库系统的插件If you are using a database in a production environment Rails most likely has an adapter for it.如果你在产品环境中使用数据库对Rails来说仅仅是添加一个适配器而已。
3.3.2 Configuring a MySQL Database配置一个MySQL数据库
If you choose to use MySQL instead of the shipped SQLite3 database, your config/database.yml will look a little different.如果你选择MySQL代替SQLite3数据库,你的配置文件会有一点不同。Here’s the development section:这是开发环境下(的配置):
development:
adapter:
mysql2
encoding:
utf8
database:
blog_development
pool:
5
username:
root
password:
socket:
/tmp/mysql.sock
If your development computer’s MySQL installation includes a root user with an empty password, this configuration should work for you. 如果你的开发计算机中安装的MySQL包含一个root用户和空的密码,这个配置文件就可以工作。Otherwise, change the username and password in the development section as appropriate.否则,修改合适的开发环境中使用的用户名和密码。
3.3.3 Configuring a PostgreSQL Database配置一个PostgreSQL数据库
If you choose to use PostgreSQL, your config/database.yml will be customized to use PostgreSQL databases:
development:
adapter:
postgresql
encoding:
unicode
database:
blog_development
pool:
5
username:
blog
password:
3.3.4 Configuring an SQLite3 Database for JRuby Platform配置在JRuby平台中使用的SQLite3数据库
If you choose to use SQLite3 and using JRuby, your config/database.yml will look a little different. Here’s the development section:
development:
adapter:
jdbcsqlite3
database:
db/development.sqlite3
3.3.5 Configuring a MySQL Database for JRuby Platform配置在JRuby平台中使用的MySQL数据库
If you choose to use MySQL and using JRuby, your config/database.yml will look a little different. Here’s the development section:
development:
adapter:
jdbcmysql
database:
blog_development
username:
root
password:
3.3.6 Configuring a PostgreSQL Database for JRuby Platform配置在JRuby平台中使用的PostgreSQL数据库
Finally if you choose to use PostgreSQL and using JRuby, your config/database.yml will look a little different. Here’s the development section:
development:
adapter:
jdbcpostgresql
encoding:
unicode
database:
blog_development
username:
blog
password:
Change the username and password in the development section as appropriate.修改在开发会话中使用的合适的用户名和密码
You don’t have to update the database configurations manually. 你不是必须手动的配置数据库If you look at the options of the application generator, you will see that one of the options is named —database. 如果你查看应用程序产生器,你将会发现一个选项(-d, —database=name Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3/frontbase/ibm_db).)
This option allows you to choose an adapter from a list of the most used relational databases. You can even run the generator repeatedly: cd .. && rails blog —database=mysqlWhen you confirm确认the overwriting of the config/database.yml file, your application will be configured for MySQL instead of SQLite.
3.4 Creating the Database创建数据库
Now that you have your database configured,it’s time to have Rails create an empty database for you. 现在你已经有了你的数据库配置文件了,是时候创建一个空的数据库了。You can do this by running a rake command:
$
rake
db:create
#
确保你的系统中已经有一个
JavaScript runtime.
#
可能还需要执行
sudo
gem
install
therubyracer
&&
sudo
gem
install
execjs
jhjguxin@jhjguxin-virtual-machine:~/blog$ rake db:create
rake aborted!
Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes.
one
way:
并且
In
your
Gemfile
write this gem 'execjs' gem 'therubyracer' and then run bundle install
#
在
ubuntu
环境中安装
sqlite3
库
sudo
apt-get
install
libsqlite3-ruby
libsqlite3-dev
也尝试过使用
gem
install
sqlite3-ruby
但是网上说是
gem
nds
有问题
安装
ruby
的
mysql
库
sudo
apt-get
install
libmysql-ruby
another way:
参照上面的网址:我找了一个ubuntu里面有的Node.js就可以不用那个therubyracer了
jhjguxin@jhjguxin-virtual-machine:~/blog$ rake db:create
This will create your development and test SQLite3 databases inside the db/ folder.将会在db/文件夹中创建你的开发环境的数据库文件development.sqlite3
Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running rake -T.Rake是一个通用的命令行可以帮助Rails用户完成很多事情。你可以通过运行rake -T 查看可用的rake命令
ruby script/server#或者script/server
4 Hello, Rails!
One of the traditional places to start with a new language is by getting some text up on screen quickly. To do this, you need to get your Rails application server running.传统的方式之一,开始使用一种新的(命令)语法并得到快速掠过的文字。要得到这样的结果你需要使你的Rails程序运行。
$
rails
server
#——-rails2.3.1
$ ruby script/server#
This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). 这里默认将开启一个WEBrick服务器的的实例(Rails也可能使用一些其他的web服务器)。To see your application in action, open a browser window and navigate to http://localhost:3000. You should see Rails’ default information page:查看你的应用程序的行为,打开一个浏览器并且导航到127.0.0.0:3000你将会看到一个Rails默认的信息页面。
To stop the web server, hit Ctrl+C in the terminal window where it’s running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.要终止web服务,在命令运行的终端中按下Ctrl+C。在开发环境模式中,Rails一般不需要你停止服务;你所做的更改将自动的编译进需要的文件中并且重启服务。
The “Welcome Aboard#船” page is the smoke test for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page.这个欢迎界面体现了一个新的Rails应用程序创建成功(通过了Rails的自检)。You can also click on the About your application’s environment link to see a summary of your application’s environment.你可以点击‘ About your application’s environment’查看你的应用程序运行环境摘要信息。
4.2 Say “Hello”, Rails
To get Rails saying “Hello”, you need to create at minimum a controller and a view.要使Rails说出(显示)“你好”,你还需要创建一个最小的a controller and a view。Fortunately, you can do that in a single command. Enter this command in your terminal:幸运的是,你可以完成这些通过一行命令。在终端中输入:
rails
generate
controller
home
index
##rails2.3.1 script/generate
controller
home
index##
If you get a command not found error when running this command, you need to explicitly pass Rails rails commands to Ruby: ruby \path\to\your\application\script\rails generate controller home index.如果你在输入这个命令的时候出现没有这个命令错误,你需要明确的使用ruby来执行Rails命令。这里就没有rails这个文件应该是rubyscript/generatecontrollerhomeindex
Rails will create several files for you, including app/views/home/index.html.erb. Rails将会为你创建一些文件,包含‘app/views/home/index.html.erb’。This is the template that will be used to display the results of the index action (method) in the home controller.这个模板会用来显示在home controller中的index action (method)的结果。Open this file in your text editor and edit it to contain a single line of code:在文本编辑器中打开这个文件并输入:
<h1>Hello,
Rails!</h1>
4.3 Setting the Application Home Page
Now that we have made the controller and view, we need to tell Rails when we want “Hello Rails” to show up. 现在我们已经创建了controller 和view,我们还需要告诉Rails我们想在什么时候显示出来。In our case, we want it to show up when we navigate to the root URL of our site, http://localhost:3000, instead of the “Welcome Aboard” smoke test.在本例中,我们想让它在我们导航至站点url根目录的时候替代“Welcome Aboard”显示。
The first step to doing this is to delete the default page from your application:首先移除应用程序中的默认页面。
rm
public/index.html
We need to do this as Rails will deliver#提供,传输any static file in the public directory in preference#偏好优先to any dynamic#动态content we generate from the controllers.我们必须这样做因为,Rails将会传送任何在public的静态文件优先于我们在controllers生成的动态(显示)内容。
Now, you have to tell Rails where your actual home page is located.Open the file config/routes.rb in your editor. This is your application’s routing file which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. Find the line beginning with root :to, uncomment it and change it like the following:现在你还必须告诉Rails 你实际上的主页在哪里。在文本编辑器中打开config/routes.rb 。这是你应用程序的路由文件,它采用DSL语言囊括了告诉Rails怎样连接请求信息到controllers and actions.的所有条目。
这个文件包含许多简单的路由命令,其中一条实际上是用于告诉我们怎样连接你站点根目录到一个指定的controller and acti。找到以root :to开头的那一行,注释掉它改成如下内容:
Blog::Application.routes.draw
do
get
“home/index”
root
:to
=>
“home#index”
//////////////////this
depend
on
rails
2.3.1/////////
Blog::Application.routes.draw
do
#…
#
You
can
have
the
root
of
your
site
routed
with
“root”
#
just
remember
to
delete
public/index.html.
map.root
:controller
=>
“home”
or
map.root
:controller
=>
“home”,:action
=>
‘index’
##root
:to
=>
“home#index”
The root :to => “home#index” tells Rails to map the root action to the home controller’s index action.
ps-ef|grepwebrick
kill-9上面得到的id强制结束WEBrick服务
Now if you navigate to http://localhost:3000 in your browser, you’ll see Hello, Rails!.现在你在浏览器中导航至http://localhost:3000 ,你将会看到“Hello, Rails!”.
For more information about routing, refer to RailsRoutingfromtheOutsideIn.更多的信息请参见RailsRoutingfromtheOutsideIn。
5 Getting Up and Running Quickly with Scaffolding使用Scaffolding快速创建并运行
Rails scaffolding is a quick way to generate some of the major pieces of an application. Rails scaffolding是一个快速的方法产生应用程序的一些主要的部分。If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.如果你想使用一种简单的操作为新资源创建models,views和controllers,Scaffolding是一个不错的工具。
6 Creating a Resource创建一个资源
In the case of the blog application, you can start by generating a scaffolded Post resource: this will represent#代表,表现表示a single blog posting. To do this, enter this command in your terminal:在本示例中的blog应用程序,你可以使用scaffolded产生post资源:它表现为一个简单的blog posting。要完成这些,在终端输入如下命令:
$
rails
generate
scaffold
Post
name:string
title:string
content:text
rails 2.3.1 script/generate scaffold Post name:string title:string content:text
The scaffold generator will build several files in your application, along with some folders, and edit config/routes.rb. Here’s a quick overview of what it creates:scaffold创建器将会在应用程序中的一些文件夹中生成一些文件,并且还会编辑config/routes.rb。下面这些产生的文件的大概说明:
File | Purpose |
---|---|
db/migrate/20100207214725_create_posts.rb | Migration to create the posts table in your database (your name will include a different timestamp)将创建的posts表单迁移到你的数据库(会在你的命名前面加上时间) |
app/models/post.rb | The Post model Post模型 |
test/fixtures/posts.yml | Dummy posts for use in testing 模拟测试post |
app/controllers/posts_controller.rb | The Posts controller |
app/views/posts/index.html.erb | A view to display an index of all posts 显示所有post |
app/views/posts/edit.html.erb | A view to edit an existing post编辑post |
app/views/posts/show.html.erb | A view to display a single post显示一条post |
app/views/posts/new.html.erb | A view to create a new post创建post |
app/views/posts/_form.html.erb | A partial to control the overall look and feel of the form used in edit and new views一个用于控制编辑和创建新视图的整体视效表 |
app/helpers/posts_helper.rb | Helper functions to be used from the post views使用post的帮助功能views |
app/assets/stylesheets/scaffolds.css.scss | Cascading style sheet#层叠样式to make the scaffolded views look better #css使scaffolded视图具有更好的效果 |
app/assets/stylesheets/posts.css.scss | Cascading style sheet for the posts controller#post controller的css效果 |
app/assets/javascripts/posts.js.coffee | CoffeeScript for the posts controller |
test/unit/post_test.rb | Unit testing harness#利用for the posts model post models的Unit测试 |
test/functional/posts_controller_test.rb | Functional testing harness for the posts controller post controller的功能测试 |
test/unit/helpers/posts_helper_test.rb | Unit testing harness for the posts helper post helper的Uint 测试 |
config/routes.rb | Edited to include routing information for posts 加入posts 路由信息 |
While scaffolding will get you up and running quickly, the code it generates is unlikely to be a perfect fit for your application.即便是scaffolding使你创建和运行非常快捷,但是产生的代码不可能完美的适合你的应用程序。You’ll most probably want to customize the generated code.你大多数都需要定制产生的代码。Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch. 很多有经验的Rails开发人员完全不使用scaffolding,宁愿从头编写全部的代码。Rails, however, makes it really simple to customize templates for generated models, controllers, views and other source files.Rails,无论如何,使得为生成的models,controllers,views或者其他代码编定制模板非常简单。You’ll find more information in the CreatingandCustomizingRailsGenerators&Templates guide.你可以在CreatingandCustomizingRailsGenerators&Templates看到更多信息。
6.1 Running a Migration
One of the products of the rails generate scaffold command is a database migration. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it’s possible to undo a migration after it’s been applied to your database. Migration filenames include a timestamp to ensure that they’re processed#处理in the order that they were created.script/generate scaffold的一个产品就是数据迁移。Migrations是一个ruby类被设计用来使数据库表单的创建和修改变得简单。Rails使用rake命令来执行迁移,它还可以撤销已经应用的修改。Migration filenames include a timestamp确保了迁移能够完成。
If you look in the db/migrate/20100207214725_create_posts.rb file (remember, yours will have a slightly different name记住,你得到的可能会有略微不同), here’s what you’ll find:
class
CreatePosts
<
ActiveRecord::Migration
def
change
create_table
:posts
do
|t|
t.string
:name
t.string
:title
t.text
:content
t.timestamps
end
end
end
The above migration creates a method name change which will be called when you run this migration.整个migration创建了一个名叫change的方法,该方法在你运行这个migration的时候被调用。The action defined in that method is also reversible#可逆, which means Rails knows how to reverse the change made by this migration, in case you want to reverse it at later date.这个方法中定义的行为也是可逆的,那就是说Rails知道怎样逆向改变这个migration,如果你需要恢复到上一次数据。By default, when you run this migration it will creates a posts table with two string columns and a text column. 默认情况下,当你运行这个migration,他将会创建一个包含两个字符串列和一个text列的表单。It also creates two timestamp fields to track record creation and updating. More information about Rails migrations can be found in the RailsDatabaseMigrations guide.关于Rails migration的更多信息请阅读RailsDatabaseMigrations手册。
At this point, you can use a rake command to run the migration:这个时候,你可以使用rake命令运行migration了:
rake
db:migrate
Rails
will
execute
this
migration
command
and
tell
you
it
created
the
Posts
table.Rails
将会执行这个
migration
命令并且通知你它创建了
Post
表单。
==
CreatePosts:
migrating
====================================================
—
create_table(:posts)
–>
0.0030s
==
CreatePosts:
migrated
(0.0032s)
===========================================
Because
you
’
re
working
in
the
development
environment
by
default,
this
command
will
apply
to
the
database
defined
in
the
development
section
of
your
config/database.yml
file.
If
you
would
like
to
execute
migrations
in
other
environment,
for
instance
in
production,
you
must
explicitly
pass
it
when
invoking
the
command:
rake db:migrate RAILS_ENV=production.
由于你默认工作在开发环境中,这个命令将会应用于开发环境会话的数据库位于你的
config/database.yml 中。如果你想执行
migration
在其他环境中,比如以产品(环境)为实例,你必须明确调用的通过命令行中执行:
rake
db:migrate
RAILS_ENV=production
。
6.2
Adding
a
Link
添加要给
link
(到
blog
)
To hook the posts up to the home page you’ve already created, you can add a link to the home page.把你已经创建好的post挂到主页上,你可以通过添加一个link到主页。Open app/views/home/index.html.erb and modify it as follows:打开app/views/home/index.html.erb并且按照下面所示更改:
<h1>Hello,
Rails!</h1>
<%=
link_to
“My
Blog”,
posts_path
%>
The link_to method is one of Rails’ built-in view helpers.这个链接方法是Rails在view helpers 的内建方法之一。It creates a hyperlink based on text to display and where to go – in this case, to the path for posts.它创建一个基于文字的超级链接并显示到哪里,在这个实例中(跳转)到posts。
6.3 Working with Posts in the Browser
Now you’re ready to start working with posts. To do that, navigate to http://localhost:3000 and then click the “My Blog” link:现在你已经准备好在posts中工作了。导航至http://localhost:3000,并且点击“My Blog”链接。
This
is
the
result
of
Rails
rendering
the
index
view
of
your
posts.
这就是
Rails
渲染你的
posts
视图的结果。
There
aren
’
t
currently
any
posts
in
the
database,
but
if
you
click
the
New Post
link
you
can
create
one.
在你点击
“
New
Post
”
链接并创建一个新的
post
之前,数据库里面是没有任何
post
的。
After
that,
you
’
ll
find
that
you
can
edit
posts,
look
at
their
details,
or
destroy
them.
随后你可以编辑,查看详细内容,或者删除他们。
All
of
the
logic
and
HTML
to
handle
this
was
built
by
the
single
rails generate scaffold
command.post
的所有的
logic
和
HTML
都是通过
rails generate scaffold 生成的。
In
development
mode
(which
is
what
you
’
re
working
in
by
default),
Rails
reloads
your
application
with
every
browser
request,
so
there
’
s
no
need
to
stop
and
restart
the
web
server.
在开发模式中(你的默认工作模式),
Rails
会在每个浏览器请求的时候重新载入你的应用程序,因此你不需要停止或者重启
web
服务。
Congratulations,
you
’
re
riding
the
rails!
Now
it
’
s
time
to
see
how
it
all
works.
恭喜,你已经驯服了
rails
!现在是时候去看看它的所有工作了。
6.4
The
Model
The model file, app/models/post.rb is about as simple as it can get:
class
Post
<
ActiveRecord::Base
end
There
isn
’
t
much
to
this
file
–
but
note
that
the
Post
class
inherits
from
ActiveRecord::Base.
这里有可能不一致
——
但是切记
Post
类继承于
ActiveRecord::Base。
Active
Record
supplies
a
great
deal
of
functionality
to
your
Rails
models
for
free,
including
basic
database
CRUD
(Create,
Read,
Update,
Destroy)
operations,
data
validation,
as
well
as
sophisticated#
复杂
search
support
and
the
ability
to
relate
multiple
models
to
one
another.
Active
Record
免费为你的
models
提供了强大的功能,包括基本数据库的
CRUD
(创建,读取,更新,删除)操作,数据验证,以及复杂的的查询与其它数据表单多关联的字段的支持能力。
6.5
Adding
Some
Validation
添加一些验证
Rails includes methods to help you validate the data that you send to models. Rails包含一些帮助你验证发送到models的数据的方法。Open the app/models/post.rb file and edit it:打开app/models/post.rb并编辑:
class Post < ActiveRecord::Base
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
end
These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. 这些更改会确保所有的post都有一个name和titile并且title长度至少五个字符。Rails can validate a variety of各种,很多conditions#名词(字段)in a model, including the presence or uniqueness独特of columns, their format, and the existence of associated相关objects. Rails可以验证很多种字段,比如字段能否为空和独特性,字段的格式,以及字段的关联。
6.6 Using the Console
To see your validations in action, you can use the console.要想在action里面查看你的验证你可以使用console。The console is a command-line tool that lets you execute#执行运行Ruby code in the context of your application:console是一个可以让你在你的应用程序的上下文中执行Ruby代码的命令行工具:
$
rails
console
The
default
console
will
make
changes
to
your
database.
默认的
console
将会改变你的数据库。
You
can
instead
open
a
console
that
will
roll
back
any
changes
you
make
by
using
rails console —sandbox.
你可以通过运行
rails
console
–
sandbox
or
rails
console
-s,
这样你可以回滚你的任何操作。
jhjguxin@jhjguxin-Aspire-4750:~/blog$ rails console -s
Loading development environment in sandbox (Rails 3.1.1)
Any modifications you make will be rolled back on exit
irb(main):001:0> p=Post.new(:content=>“A new post to test console”)
=> #<Post id: nil, name: nil, title: nil, content: “A new post to test console”, created_at: nil, updated_at: nil>
irb(main):002:0> p.save
(0.2ms) SAVEPOINT active_record_1
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
irb(main):003:0> p.errors
=> #<ActiveModel::Errors:0xb6765d48 @base=#<Post id: nil, name: nil, title: nil, content: “A new post to test console”, created_at: nil, updated_at: nil>, @messages=#<OrderedHash {:title=>[“is too short (minimum is 5 characters)”, “can’t be blank”], :name=>[“can’t be blank”]}>>
This code shows creating a new Post instance, attempting to save it and getting false for a return value (indicating that the save failed), and inspecting the errors of the post.这段代码演示了创建一个Post实例,并企图保存到数据库并得到一个失败的返回值(说明保存失败的原因),检查post的错误信息。
When you’re finished, type exit and hit return to exit the console.当你操作完成,输入exit并回车退出console。
Unlike the development web server, the console does not automatically load your code afresh for each line.不像开发环境的web服务器console不会自动导入你每行输入的新的代码。If you make changes to your models while the console is open, type reload! at the console prompt to load them.如果你改变了你的moels并且console是打开的,输入reload!那么console会立即导入他们。
jhjguxin@jhjguxin-Aspire-4750:~/blog$ rails console
Loading development environment (Rails 3.1.1)
irb(main):001:0> p=Post.new(:title=>“test console”,:name=>“jhjguxin”,:content=>“A new post to test console”)
=> #<Post id: nil, name: “jhjguxin”, title: “test console”, content: “A new post to test console”, created_at: nil, updated_at: nil>
irb(main):002:0> p.save
SQL (13.4ms) INSERT INTO “posts” (“content”, “created_at”, “name”, “title”, “updated_at”) VALUES (?, ?, ?, ?, ?) [[“content”, “A new post to test console”], [“created_at”, Sat, 05 Nov 2011 15:55:17 UTC +00:00], [“name”, “jhjguxin”], [“title”, “test console”], [“updated_at”, Sat, 05 Nov 2011 15:55:17 UTC +00:00]]
=> true
irb(main):003:0> reload!
Reloading…
=> true
###如果是rails console —sandbox could not reload! Successfully.
6.7 Listing All Posts
The easiest place to start looking at functionality is with the code that lists all posts. 寻找所有功能的地方是使用代码列出所有的post。Open the file app/controllers/posts_controller.rb and look at the index action:打开文件app/controllers/posts_controller.rb,并且查看index action:
def
index
@posts
=
Post.all
respond_to
do
|format|
format.html
#
index.html.erb
format.json
{
render
:json
=>
@posts
}
end
end
Post.all calls the Post model to return all of the posts currently in the database. Post.all调用Post model并返回当前在数据库中的所有post。The result of this call is an array of posts that we store in an instance variable called @posts.本次调用的结果是一个post的数组,并且我们将这个数组存储在一个叫做@posts的实例变量中。
For more information on finding records with Active Record, see ActiveRecordQueryInterface.查找有关Active Record更多的信息,可以查看ActiveRecordQueryInterface相关记录。
The respond_to block handles both HTML and JSON calls to this action.这个respond_to块处理了这个动作的HTML和JSON请求。If you browse to http://localhost:3000/posts.json, you’ll see a JSON containing all of the posts.如果你浏览http://localhost:3000/posts.json,你将会看到一个JSON包含着所有的post。The HTML format looks for a view in app/views/posts/ with a name that corresponds to the action name. 这个HTML格式在app/views/posts/的view中查找相对应的动作名称。Rails makes all of the instance variables from the action available to the view.Rails使来自action的所有的实例变量对应到(可用)view。Here’s app/views/posts/index.html.erb:
<h1>Listing
posts</h1>
<table>
<tr>
<th>Name</th>
<th>Title</th>
<th>Content</th>
<th></th>
<th></th>
<th></th>
</tr>
<%
@posts.each
do
|post|
%>
<tr>
<td><%=
post.name
%></td>
<td><%=
post.title
%></td>
<td><%=
post.content
%></td>
<td><%=
link_to
‘Show’,
post
%></td>
<td><%=
link_to
‘Edit’,
edit_post_path(post)
%></td>
<td><%=
link_to
‘Destroy’,
post,
:confirm
=>
‘Are
you
sure?’,
:method
=>
:delete
%></td>
</tr>
<%
end
%>
</table>
<br
/>
<%=
link_to
‘New
post’,
new_post_path
%>
This view iterates#迭代over the contents of the @posts array to display content and links.这个view迭代@posts数组所有的内容并显示相关的内容和链接。A few things to note in the view:关于视图note一些信息:
- link_to builds a hyperlink to a particular destination link_to创建一个超链接到一个地方
- edit_post_path and new_post_path are helpers that Rails provides as part of RESTful routing.edit_post_path and new_post_path是Rails提供的RESTful路由向导。You’ll see a variety of these helpers for the different actions that the controller includes. 不同的具有controller的actions中你将会看到一系列的这样的向导。
In previous versions of Rails, you had to use <%=h post.name %> so that any HTML would be escaped before being inserted into the page.在以前的版本的Rails中,你必须使用<%=h post.name %> 以避免一些HTML可能会转义在插入到页面之前。In Rails 3.0, this is now the default.在Rails 3.0,作为默认。To get unescaped HTML, you now use <%= raw post.name %>.得到一个非转义的HTML,你现在使用<%=raw post.name%>.
For more details on the rendering process, see LayoutsandRenderinginRails.了解更过关于渲染处理流程,阅读LayoutsandRenderinginRails.
6.8 Customizing the Layout定制布局
The view is only part of the story of how HTML is displayed in your web browser.view仅仅告诉HTML要显示什么(内容)在你的web浏览器里面。Rails also has the concept of layouts, which are containers for views.Rails也有关于布局的概念(定义),那就是(布局)是对viewi的包装。When Rails renders a view to the browser, it does so by putting the view’s HTML into a layout’s HTML.当Rails渲染一个view到浏览器,它通常是(这样做)把view的HTML放进布局的HTML中。In previous versions of Rails, the rails generate scaffold command would automatically create a controller specific layout, like app/views/layouts/posts.html.erb, for the posts controller. 在以前的版本中,rails generate scaffold 命令将会自动创建controller对应指定的布局。However this has been changed in Rails 3.0. An application specific layout is used for all the controllers and can be found in app/views/layouts/application.html.erb. 然而在rails3.0中有所不同了。一个应用程序指定的布局适用于所有的controllers,可以在app/views/layouts/application.html.erb 中找到(这就好像是django的base.html)。Open this layout in your editor and modify the body tag:打开这个layout在你的编辑器中并且修改body标签:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag “application” %>
<%= javascript_include_tag “application” %>
<%= csrf_meta_tags %>
</head>
<body style=“background: #EEEEEE;”>
<%= yield %>
</body>
</html>
Now when you refresh the /posts page, you’ll see a gray background to the page. This same gray background will be used throughout all the views for posts.现在你刷新/posts页面,你将会看到一个灰色的页面背景。并且相同的灰色背景将会使用在posts的所有视图中。
6.9 Creating New Posts
Creating a new post involves two actions. The first is the new action, which instantiates an empty Post object:创建一个new post 包含两个actions。首先是new action,它会实例化一个空的Post对象:
def
new
@post
=
Post.new
respond_to
do
|format|
format.html
#
new.html.erb
format.json
{
render
:json
=>
@post
}
end
end
The new.html.erb view displays this empty Post to the user: 这个new.html.erb视图显示这个空的post给用户:
<h1>New
post</h1>
<%=
render
‘form’
%>
<%=
link_to
‘Back’,
posts_path
%>
The <%= render ‘form’ %> line is our first introduction to partials in Rails.<%= render ‘form’ %>是我们第一个介绍的Rails的partials。A partial is a snippet of HTML and Ruby code that can be reused in multiple locations.一个partial是一个HTML代码片段和Ruby代码的组合可以在多目标对象中重用。(类似于django的include ‘other.html’)In this case, the form used to make a new post, is basically identical to a form used to edit a post, both have text fields for the name and title and a text area for the content with a button to make a new post or update the existing post.在本例中,form用于创建new post,它相当于一个用于编辑一个post的表单,这个表单有一个name text fields 和title text fields 以及一个content的text area还有一个用于添加一个新的post或者更新已经存在的post的按钮。
If you take a look at views/posts/form.html.erb file, you will see the following:如果你看一下views/posts/form.html.erb这个文件,你将会发现下面的内容:
<%= form_for(@post) do |f| %>
<% if @post.errors.any? %>
<div id=“error_explanation”>
<h2><%= pluralize(@post.errors.count, “error”) %> prohibited this post from being saved:</h2>
<ul>
<% @post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div>
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div>
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div>
<%= f.submit %>
</div
<% end %>
This partial receives all the instance variables defined in the calling view file, so in this case, the controller assigned the new Post object to @post and so, this is available in both the view and partial as @post.这个partial接收在view文件中定义的所有的实例变量,因此在本例中,controller申请新的Post对象给@post,@post在viewi和partial都是可用的。
For more information on partials, refer to the LayoutsandRenderinginRails guide.有关partials的更多信息,参考LayoutsandRenderinginRails指导。
The form_for block is used to create an HTML form.form_for代码块用于创建一个HTML表单。Within this block, you have access to methods to build various controls on the form. 在这个代码块中你可以在访问方法的基础上在表单上创建各种控制。For example, f.text_field :name tells Rails to create a text input on the form, and to hook it up to the name attribute of the instance being displayed.比如,f.test_field :name 告诉Rails在表单中创建一个text input。You can only use these methods with attributes of the model that the form is based on (in this case name, title, and content).form使用的方法基于model的相对应的字段属性(类型如text_field or text_area)(例如本例中的name,title,content)。Rails uses form_for in preference偏好优先to having you write raw HTML because the code is more succinct#简洁, and because it explicitly明确ties关系the form to a particular model instance.Rails使用(偏向于使用)form_for列出你要输入的HTML行因为这样代码更加简洁,并却这样使得form和particular model实例关系更加明显。
The form_for block is also smart enough to work out if you are doing a New Post or an Edit Post action, and will set the form action tags and submit button names appropriately in the HTML output.form_for代码块同样也足够你定制你的New Post和Edit Post action,并且将会设置form action标签和在HTML输出中显示的提交按钮名称。
If you need to create an HTML form that displays arbitrary#任意fields, not tied to a model, you should use the form_tag method, which provides shortcuts for building forms that are not necessarily tied to a model instance.如果你需要创建一个HTML表单显示任意的域,而不绑定到model字段中,你应该使用form_tag方法,它快捷的保证了建立forms不必在绑定到一个model实例。
When the user clicks the Create Post button on this form, the browser will send information back to the create method of the controller (Rails knows to call the create method because the form is sent with an HTTP POST request; that’s one of the conventions that I mentioned earlier):当用户点击这张表单上面的创建Post按钮,浏览器将会发送信息——controller的方法回服务器(Rails知道调用create方法,因为form是以HTTP POST请求发送,这是我随后提到的一种协议之一)
def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
format.html { redirect_to @post, :notice => ‘Post was successfully created.’ }
format.json { render :json => @post, :status => :created, :location => @post }
else
format.html { render :action => “new” }
format.json { render :json => @post.errors, :status => :unprocessable_entity }
end
end
end
The create action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the params hash.create action实例化一个新的Post对象,这个对象给form提供数据支持。After successfully saving the new post, create returns the appropriate format that the user has requested (HTML in our case).当成功的保存了新post,create返回用户请求的适当的格式(在本例中是HTML)。It then redirects the user to the resulting post show action and sets a notice to the user that the Post was successfully created.然后重定向用户页面到结果显示的post show action页面并且给出提示Post成功的创建了。
If the post was not successfully saved, due to a validation error, then the controller returns the user back to the new action with any error messages so that the user has the chance to fix the error and try again.如果post没有保存成功,是因为(数据)验证错误,然后controller控制用户页面回到new action(包含验证错误新息)给用户。
The “Post was successfully created.” message is stored inside of the Rails flash hash, (usually just called the flash) so that messages can be carried#载over to another action, providing the user with useful information on the status of their request. “Post was successfully created.” 这条消息被存储在Rails的flash的hash表中,(通常之叫它flash)因此消息可以转载到另一个action,在请求状态中提供有用的信息给用户。In the case of create, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record.在这个新建例子(数据验证失败)中,用户实际上从来不看任何在页面创建进程中的渲染页面,因为它立刻重定向页面到new Post当Rails保存了这个记录。The Flash carries over a message to the next action, so that when the user is redirected back to the show action, they are presented with a message saying “Post was successfully created.”Flash装载消息到接下来的action,因此当用户被重定向到了show action,他们立刻收到了一条消息“Post was successfully created.”。
6.10 Showing an Individual Post显示一条单个的Post
When you click the show link for a post on the index page, it will bring you to a URL like http://localhost:3000/posts/1. 当你在posts 的主页面点击一个post的show这个超链接,他将会产生一个url http://localhost:3000/posts/1。Rails interprets解释this as a call to the show action for the resource, and passes in 1 as the :id parameter.Rails解释这是一个到show action的resource 调用。Here’s the show action:这里是show action:
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @post }
end
end
The show action uses Post.find to search for a single record in the database by its id value.这里的show action使用Post.find通过对应记录的id来查找单个记录。After finding the record, Rails displays it by using show.html.erb:当找到记录,Rails使用show.html.erb来显示它:
<p id=“notice”><%= notice %></p>
<p>
<b>Name:</b>
<%= @post.name %>
</p>
<p>
<b>Title:</b>
<%= @post.title %>
</p>
<p>
<b>Content:</b>
<%= @post.content %>
</p>
<%= link_to ‘Edit’, edit_post_path(@post) %> |
<%= link_to ‘Back’, posts_path %>
6.11 Editing Posts编辑Posts
Like creating a new post, editing a post is a two-part process.类似创建一个新的post,编辑一个post也(分为)两部分。The first step is a request to edit_post_path(@post) with a particular post. This calls the edit action in the controller:首先是到edit_post_path(@post)请求一个特定的post。这里是调用的在controller中的edit action:
def
edit
@post
=
Post.find(params[:id])
end
After finding the requested post, Rails uses the edit.html.erb view to display it:在找到了请求的post,Rails使用edit.html.erb试图来显示它:
<h1>Editing
post</h1>
<%=
render
‘form’
%>
<%=
link_to
‘Show’,
@post
%>
|
<%=
link_to
‘Back’,
posts_path
%>
Again, as with the new action, the edit action is using the form partial, this time however, the form will do a PUT action to the PostsController and the submit button will display “Update Post”再一次的,就像new action,edit action也使用form部分,这次有所不同,form将会提交一个PUT action到PostsController并且提交按钮将会显示“Update Post”
Submitting the form created by this view will invoke调用the update action within the controller:提交的form由上面这个视图创建的并且还会调用controller中的update action:
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, :notice => ‘Post was successfully updated.’ }
format.json { head :ok }
else
format.html { render :action => “edit” }
format.json { render :json => @post.errors, :status => :unprocessable_entity }
end
end
end
In the update action, Rails first uses the :id parameter passed back from the edit view to locate the database record that’s being edited.在update action中,Rails首先使用:id参数从edit view(传值到)数据库记录下刚才编辑的内容。The update_attributes#更新model的属性call then takes the rest of the parameters from the request and applies them to this record. update_attributes在应用一些(更多)参数的来自request的数据到recode时被调用。If all goes well, the user is redirected to the post’s show view. 如果一切成功,用户会被重定向到post的show视图。If there are any problems, it’s back to the edit view to correct them.如果期间发生了任何错误,它将回到edit视图并(要求)改正他们。
6.12 Destroying a Post 摧毁一个post
Finally, clicking one of the destroy links sends the associated id to the destroy action:最后,点击一个destroy链接发送相关的id到destroy动作:
def
destroy
@post
=
Post.find(params[:id])
@post.destroy
respond_to
do
|format|
format.html
{
redirect_to
posts_url
}
format.json
{
head
:ok
}
end
end
The destroy method of an Active Record model instance removes the corresponding record from the database.这个destroy是Active Recordmodel的实例(功能是)从数据库中移除相应的记录。After that’s done, there isn’t any record to display, so Rails redirects the user’s browser to the index view for the model.当这个(操作)完成,这里没有任何记录显示,因此Rails重定向用户的浏览器到model的主页视图。
7 Adding a Second Model添加第二个Model(comment)
Now that you’ve seen how a model built with scaffolding looks like, it’s time to add a second model to the application.你已经知道了通过scaffolding生成的model看起来是怎样的。The second model will handle comments on blog posts.第二个model用来处理blog post的评论。
7.1 Generating a Model构造一个model
Models in Rails use a singular name, and their corresponding database tables use a plural name. Rails 中的Models使用一个单数名称,同时它们相关的数据库表使用一个复数名称。For the model to hold comments, the convention is to use the name Comment.对于评论在models中的代名词,习惯上使用的的是Comment。Even if you don’t want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers.即使你不想完完全全的使用scaffolding,大多数的Rails仍然使用生成器来做这些事情比如models和controllers。To create the new model, run this command in your terminal:要创建一个新的model,在终端中运行下面这条命令:
rails
generate
model
Comment
commenter:string
body:text
post:references
#references
引用
This command will generate four files:这条命令将会生成四个文件:
- app/models/comment.rb – The model 模型
- db/migrate/20111108080402_create_comments.rb – The migration 数据迁移
- test/unit/comment_test.rb andtest/fixtures/comments.yml – The test harness. 测试工具
First, take a look at comment.rb:首先,看一看comment.rb:
class
Comment
<
ActiveRecord::Base
belongs_to
:post
end
This is very similar to the post.rb model that you saw earlier.这和你刚刚看到post.rb很近似。The difference is the line belongs_to :post, which sets up an Active Record association. 不同的是这行belongs_to :post,他会安装一个Active Record association。You’ll learn a little about associations in the next section of this guide.你将会在接下来的guide学习一点有关associations的内容。
In addition to the model, Rails has also made a migration to create the corresponding database table:除了模型,Rails同样也产生了一个migration来创建相应的数据库表单:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :post
t.timestamps
end
add_index :comments, :post_id
end
end
The t.references line sets up a foreign key column for the association between the two models.对于t.references这行,会在两个models之间生成一个外键列从而形成一个关系(组合)。And the add_index line sets up an index for this association column. Go ahead and run the migration:而且add_index line生成一个首页关联到这个关系行:
$
rake
db:migrate
Rails is smart enough to only execute the migrations that have not already been run against the current database, so in this case you will just see:Rails能够智能的只针对对没有被运行过的表单,执行migrations生成当前的数据库,因此这里你只会看到:
7.2 Associating Models关联models
Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way:Active Record associations让你很容易的申明两个models之间的关系。在本例中的comments和posts,你可以写出这样描述关系:
- Each comment belongs to one post 一条comment对应于一个post
- One post can have many comments 一个post可以对应于多个comments
In fact, this is very close to the syntax that Rails uses to declare this association.实际上,这也很接近Rails申明的association 的语法。You’ve already seen the line of code inside the Comment model that makes each comment belong to a Post:你已经看到了在Comment model中的使每个comment对应于一个post的代码。
You’ll need to edit the post.rb file to add the other side of the association:你将会需要编辑post.rb文件来添加其他association 盟友。
class
Post
<
ActiveRecord::Base
validates
:name,
:presence
=>
true
validates
:title,
:presence
=>
true,
:length
=>
{
:minimum
=>
5
}
has_many
:comments
end
These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable @post containing a post, you can retrieve all the comments belonging to that post as the array @post.comments.
For more information on Active Record associations, see the ActiveRecordAssociations guide.
7.3 Adding a Route for Comments给Comments添加路由
As with the home controller, we will need to add a route so that Rails knows where we would like to navigate to see comments.作为home controller,我们将还需要添加一个路由让Rails知道我们导航到哪里可以看到评论。Open up the config/routes.rb file again, you will see an entry that was added automatically for posts near the top by the scaffold generator, resources :posts, edit it as follows:再次打开config/routes.rb文件,你将会看到scaffold创建器在顶部为posts自动添加的入口,resources :posts,把它改成如下:
resources :posts do
resources :comments
end
This creates comments as a nested resource within posts.这里把comments作为一个嵌套资源放在posts中。This is another part of capturing the hierarchical relationship that exists between posts and comments.这是在存在的posts和comments的分层关系的表现。
For more information on routing, see the RailsRoutingfromtheOutsideIn guide.关于routing的更多的信息,查看RailsRoutingfromtheOutsideIn guide。
7.4 Generating a Controller构造一个Controller
With the model in hand, you can turn your attention to creating a matching controller.model已经到手了,你可以把你的注意力放到创建一个匹配的controller上了。Again, there’s a generator for this:类似的,像这样构造:
$
rails
generate
controller
Comments
This creates six files and one empty directory:这里新建了6个文件和一个空目录。
- app/controllers/comments_controller.rb – The controller
- app/helpers/comments_helper.rb – A view helper file
- test/functional/comments_controller_test.rb – The functional tests for the controller
- test/unit/helpers/comments_helper_test.rb – The unit tests for the helper
- app/views/comments/ – Views of the controller are stored here
- app/assets/stylesheets/comment.css.scss – Cascading style sheet for the controller
- app/assets/javascripts/comment.js.coffee – CoffeeScript for the controller
Like with any blog, our readers will create their comments directly after reading the post, and once they have added their comment, will be sent back to the post show page to see their comment now listed.就像大多数blog,我们的读者将会直接发表他们的评论在他们阅读post的时候,并且一旦他们添加评论成功,将会回到postshow页面去查看他们刚刚列出的评论。Due to this, our CommentsController is there to provide a method to create comments and delete SPAM comments when they arrive.正因为这样(的考虑),我们的CommentsController如下,它提供一个方法来创建comments和删除垃圾评论。
<p><%= notice %></p>
<p>
<b>Name:</b>
<%= @post.name %>
</p>
<p>
<b>Title:</b>
<%= @post.title %>
</p>
<p>
<b>Content:</b>
<%= @post.content %>
</p>
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<div>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div>
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
<%= link_to ‘Edit Post’, edit_post_path(@post) %> |
<%= link_to ‘Back to Posts’, posts_path %> |
This adds a form on the Post show page that creates a new comment, which will call the CommentsController create action, so let’s wire that up:这里添加一个forms在Post show页面用来创建一个新的评论,它将会调用CommentsController的creat action,因此让我们补充上下面的内容:
class
CommentsController
<
ApplicationController
def
create
@post
=
Post.find(params[:post_id])
@comment
=
@post.comments.create(params[:comment])
redirect_to
post_path(@post)
end
end
You’ll see a bit more complexity here than you did in the controller for posts.这里你看到的会比你在controller中为posts做的要复杂点。That’s a side-effect of the nesting that you’ve set up; each request for a comment has to keep track#踪迹of the post to which the comment is attached, thus the initial find action to the Post model to get the post in question.那就是你刚刚你刚补充的副作用的根源;每个面向comment的请求都保持了它所依附的post的踪迹,因此这样初始化find action的时候匹配相应的post model(时)得到了答案。
In addition, the code takes advantage of some of the methods available for an association.此外,上面的代码带来的好处就是使得一些对于association的方法可用。We use the create method on @post.comments to create and save the comment.我们使用@post.comments中的create方法来新建和保存comment。This will automatically link the comment so that it belongs to that particular post.这里将会自动连接到link使得comment依附于指定的post。
Once we have made the new comment, we send the user back to the original post using the post_path(@post) helper.一旦我们评论过后,我们使用post_path(@post)助手导引用户到先前的post。As we have already seen, this calls the show action of the PostsController which in turn renders the show.html.erb template. 正如我们已经看到的,这里调用PostsController的show action它将反过来渲染show.html.erb 模板。This is where we want the comment to show, so let’s add that to the app/views/posts/show.html.erb.这里也是我们想让comment显示的地方,因此让我们添加(那些代码)到app/views/posts/show.html.erb。
<p><%= notice %></p>
<p>
<b>Name:</b>
<%= @post.name %>
</p>
<p>
<b>Title:</b>
<%= @post.title %>
</p>
<p>
<b>Content:</b>
<%= @post.content %>
</p>
<h2>Comments</h2>
<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<div>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div>
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
<br />
<%= link_to ‘Edit Post’, edit_post_path(@post) %> |
<%= link_to ‘Back to Posts’, posts_path %> |
Now you can add posts and comments to your blog and have them show up in the right places.现在你可以添加posts和comments到你的blog同时随后他们会在相应的地方显示出来。
8 Refactoring重构
Now that we have Posts and Comments working, if we take a look at the app/views/posts/show.html.erb template, it’s getting long and awkward. 现在我们已经有Posts和Comments开始工作了,如果我们注意一下app/views/posts/show.html.erb 模板,发现它变得太长而且很别扭。We can use partials to clean this up.我们可以使用partials来整理它。
8.1 Rendering Partial(局部)Collections
First we will make a comment partial to extract showing all the comments for the post. 首先我们会创建一个comment partial来专门显示post的所有的comments。Create the file app/views/comments/comment.html.erb and put the following into it:创建app/views/comments/comment.html.erb文件并输入下面的代码:
<p>
<b>Commenter:</b>
<%=
comment.commenter
%>
</p>
<p>
<b>Comment:</b>
<%=
comment.body
%>
</p>
Then in the app/views/posts/show.html.erb you can change it to look like the following:然后在app/views/posts/show.html.erb你可以相应的这样更改:
<p
class=“notice”><%=
notice
%></p>
<p>
<b>Name:</b>
<%=
@post.name
%>
</p>
<p>
<b>Title:</b>
<%=
@post.title
%>
</p>
<p>
<b>Content:</b>
<%=
@post.content
%>
</p>
<h2>Comments</h2>
<%=
render
@post.comments
%>
<h2>Add
a
comment:</h2>
<%=
form_for([@post,
@post.comments.build])
do
|f|
%>
<div
class=“field”>
<%=
f.label
:commenter
%><br
/>
<%=
f.text_field
:commenter
%>
</div>
<div
class=“field”>
<%=
f.label
:body
%><br
/>
<%=
f.text_area
:body
%>
</div>
<div
class=“actions”>
<%=
f.submit
%>
</div>
<%
end
%>
<br
/>
<%=
link_to
‘Edit
Post’,
edit_post_path(@post)
%>
|
<%=
link_to
‘Back
to
Posts’,
posts_path
%>
|
This will now render the partial in app/views/comments/comment.html.erb once for each comment that is in the @post.comments collection.这里会对@post.comments的每一个comment渲染app/views/comments/comment.html.erb模板。As the render method iterates over the @post.comments collection, it assigns each comment to a local variable named the same as the partial, in this case comment which is then available in the partial for us to show.当渲染方法迭代@post.comments收集器的时候,它声明每个comment为本地变量命名和partial相同(这里为comment),通过这样在partial中的comment就可以显示给我们的用户了。
8.2 Rendering a Partial Form
Let’s also move that new comment section out to its own partial.同样让我们移动new comment部分到它自己的地方吧。Again, you create a file app/views/comments/form.html.erb and in it you put:类似的,创建一个文件app/views/comments/form.html.erb并且在里面放入下面代码:
<%=
form_for([@post,
@post.comments.build])
do
|f|
%>
<div
class=“field”>
<%=
f.label
:commenter
%><br
/>
<%=
f.text_field
:commenter
%>
</div>
<div
class=“field”>
<%=
f.label
:body
%><br
/>
<%=
f.text_area
:body
%>
</div>
<div
class=“actions”>
<%=
f.submit
%>
</div>
<%
end
%>
Then you make the app/views/posts/show.html.erb look like the following:接着你这样修改app/views/posts/show.html.erb文件:
<p
class=“notice”><%=
notice
%></p>
<p>
<b>Name:</b>
<%=
@post.name
%>
</p>
<p>
<b>Title:</b>
<%=
@post.title
%>
</p>
<p>
<b>Content:</b>
<%=
@post.content
%>
</p>
<h2>Comments</h2>
<%=
render
@post.comments
%>
<h2>Add
a
comment:</h2>
<%=
render
“comments/form”
%>
<br
/>
<%=
link_to
‘Edit
Post’,
edit_post_path(@post)
%>
|
<%=
link_to
‘Back
to
Posts’,
posts_path
%>
|
The second render just defines the partial template we want to render, comments/form, Rails is smart enough to spot the forward slash in that string and realize that you want to render the form.html.erb file in the app/views/comments directory.第二个render仅仅定义了一个我们想渲染的partial template comments/form,Rails可以智能的识别字符串代表的含义,并且知道你是想render form.html.erb模板。
The @post object is available to any partials rendered in the view because we defined it as an instance variable.@post可以在任何的视图中partials rendered,因为我们把它定义成的实例变量。
9 Deleting Comments
Another important feature on a blog is being able to delete SPAM comments.另一个重要的功能就是可以删除垃圾评论。To do this, we need to implement a link of some sort in the view and a DELETE action in the CommentsController.要达到这样的效果,我们需要在view中实现某种链接和在CommentsController中的DELETE动作。
So first, let’s add the delete link in the app/views/comments/comment.html.erb partial:首先,在app/views/comments/comment.html.erb partial中添加delete link:
<p>
<b>Commenter:</b>
<%=
comment.commenter
%>
</p>
<p>
<b>Comment:</b>
<%=
comment.body
%>
</p>
<p>
<%=
link_to
‘Destroy
Comment’,
[comment.post,
comment],
:confirm
=>
‘Are
you
sure?’,
:method
=>
:delete
%>
</p>
Clicking this new “Destroy Comment” link will fire off a DELETE /posts/:id/comments/:id to our CommentsController, which can then use this to find the comment we want to delete, so let’s add a destroy action to our controller:点击“Destroy Comment”,link将会发送DELETE /posts/:id/comments/:id到我们的CommentsController,CommentsController将会利用刚刚收到的(消息)找到我们想删除哪条评论,因此让我们接着添加一个destroy action到我们的controller:
class
CommentsController
<
ApplicationController
def
create
@post
=
Post.find(params[:post_id])
@comment
=
@post.comments.create(params[:comment])
redirect_to
post_path(@post)
end
def
destroy
@post
=
Post.find(params[:post_id])
@comment
=
@post.comments.find(params[:id])
@comment.destroy
redirect_to
post_path(@post)
end
end
The destroy action will find the post we are looking at, locate the comment within the @post.comments collection, and then remove it from the database and send us back to the show action for the post.destroy action将会找到那个我们正在阅读的post,并且定位comment在@post.comments收集器,然后从数据库remove它,最后传回到post的show action。
9.1 Deleting Associated Objects删除关联对象
If you delete a post then its associated comments will also need to be deleted. 如果你删除一个了post,那么与之相关联的comments也需要被删除。Otherwise they would simply occupy space in the database.否则他们将会只是在数据库中占用空间(别无它用)。Rails allows you to use the dependent option of an association to achieve this. Modify the Post model, app/models/post.rb, as follows:Rails允许你通过关系的依赖选项完成(上述功能)。修改Post model。
class
Post
<
ActiveRecord::Base
validates
:name,
:presence
=>
true
validates
:title,
:presence
=>
true,
:length
=>
{
:minimum
=>
5
}
has_many
:comments,
:dependent
=>
:destroy
end
10 Security
If you were to publish your blog online, anybody would be able to add, edit and delete posts or delete comments.如果你就这样publish你的blog在互联网,任何人都可以添加,编辑和删除post或者删除comments。
Rails provides a very simple HTTP authentication system that will work nicely in this situation.Rails提供了一个非常简单的HTTP认证系统在这样的情况下会非常适合。
In the PostsController we need to have a way to block access to the various actions if the person is not authenticated, here we can use the Rails http_basic_authenticate_with method, allowing access to the requested action if that method allows it.在PostsController中我们需要一个方法来阻止没有通过认证的用户的操作,这里我们可以使用Rails的http_basic_authenticate_with这个方法,准许方法允许的请求的action。
To use the authentication system, we specify it at the top of our PostsController, in this case, we want the user to be authenticated on every action, except for index and show, so we write that:要是用这个认证系统,我们需要在PostsController 的顶部在指定(即引用)它,这样我们希望用户在进行每个action的时候都是通过授权的,除了index和show,因此我们这样写:
class
PostsController
<
ApplicationController
http_basic_authenticate_with
:name
=>
“dhh”,
:password
=>
“secret”,
:except
=>
:index
#
GET
/posts
#
GET
/posts.json
def
index
@posts
=
Post.all
We also only want to allow authenticated users to delete comments, so in the CommentsController we write:我们同样希望只有授权用户能够删除评论,因此在CommentsController这样写:
class
CommentsController
<
ApplicationController
http_basic_authenticate_with
:name
=>
“dhh”,
:password
=>
“secret”,
:only
=>
:destroy
def
create
@post
=
Post.find(params[:post_id])
Now if you try to create a new post, you will be greeted with a basic HTTP Authentication challenge.现在如果你尝试创建一个新的post,你将会迎来一个基于HTTP认证的挑战。
11 Building a Multi-Model Form构建一个多模型表单
Another feature of your average blog is the ability to tag posts. To implement this feature your application needs to interact with more than one model on a single form. Rails offers support for nested forms.另一个功能你的平衡的blog是能够给posts添加tag。要想在你的程序中实现这个功能需要在一个form中与超过一个model互动。Rails提供了嵌套forms。
To demonstrate this, we will add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:为了演示这个(功能),你将会在你创建post的form中添加post的多tag支持。首先创建一个new model来存放tags:
$
rails
generate
model
tag
name:string
post:references
Again, run the migration to create the database table:再次运行migration来创建数据库表单:
$
rake
db:migrate
Next, edit the post.rb file to create the other side of the association, and to tell Rails (via the accepts_nested_attributes_for macro) that you intend to edit tags via posts:接下来:编辑post.rb文件来创建来创建另一个成员,并且告诉Rails(通过the accepts_nested_attributes_for 宏)你打算通过posts form来编辑tags。
class Post < ActiveRecord::Base
validates :name, :presence => true
validates :title, :presence => true
validates :content,:presence => true,
:length => { :minimum => 5 }
has_many :comments, :dependent => :destroy
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
The :allow_destroy option on the nested attribute declaration tells Rails to display a “remove” checkbox on the view that you’ll build shortly.对于:allow_destroy嵌套属性的声明是告诉Rails显示一个“remove”复选框在视图中那样你可以快速创建(tags)。The :reject_if option prevents saving new tags that do not have any attributes filled in.对于:reject_if保证不保存没有任何内容的tags。
We will modify views/posts/form.html.erb to render a partial to make a tag:我们将要修改views/posts/form.html.erb来render(form的)一部分来创建tag:
<% @post.tags.build %>
<%= form_for(@post) do |post_form| %>
<% if @post.errors.any? %>
<div id=“errorExplanation”>
<h2><%= pluralize(@post.errors.count, “error”) %> prohibited this post from being saved:</h2>
<ul>
<% @post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class=“field”>
<%= post_form.label :name %><br />
<%= post_form.text_field :name %>
</div>
<div class=“field”>
<%= post_form.label :title %><br />
<%= post_form.text_field :title %>
</div>
<div class=“field”>
<%= post_form.label :content %><br />
<%= post_form.text_area :content %>
</div>
<h2>Tags</h2>
<%= render :partial => ‘tags/form’,
:locals => {:form => post_form} %>
<div class=“actions”>
<%= post_form.submit %>
</div>
<% end %>
Note that we have changed the f in form_for(@post) do |f| to post_form to make it easier to understand what is going on.注意:我们已经更改form_for(@post)
do
|f|
为form_for(@post)
do
|post_form|
这样会更加容易明白是怎么回事。
This
example
shows
another
option
of
the
render
helper,
being
able
to
pass
in
local
variables,
in
this
case,
we
want
the
local
variable
form
in
the
partial
to
refer
to
the
post_form
object.
这个例子在
render
helper
中使用另个方式(使用
f
),是为了说明我们希望的是在
form
中使用局部变量指向的
post_form
对象。
We also add a @post.tags.build at the top of this form. This is to make sure there is a new tag ready to have its name filled in by the user. If you do not build the new tag, then the form will not appear as there is no new Tag object ready to create.我们还在form的顶部添加@post.tags.build。这里是为了确保每个新的tag都被用户填上了name。如果你不创建新tag,form将不会显示它。
Now create the folder app/views/tags and make a file in there called form.html.erb which contains the form for the tag:现在创建一个app/views/tags文件夹并且在里面新建一个form.html.erb包含以下内容:
<%=
form.fields_for
:tags
do
|tag_form|
%>
<div
class=“field”>
<%=
tag_form.label
:name,
‘Tag:’
%>
<%=
tag_form.text_field
:name
%>
</div>
<%
unless
tag_form.object.nil?
||
tag_form.object.new_record?
%>
<div
class=“field”>
<%=
tag_form.label
:_destroy,
‘Remove:’
%>
<%=
tag_form.check_box
:_destroy
%>
</div>
<%
end
%>
<%
end
%>
Finally, we will edit the app/views/posts/show.html.erb template to show our tags.最后编辑app/views/posts/show.html.erb模板显示我们的tags:
<p
class=“notice”><%=
notice
%></p>
<p>
<b>Name:</b>
<%=
@post.name
%>
</p>
<p>
<b>Title:</b>
<%=
@post.title
%>
</p>
<p>
<b>Content:</b>
<%=
@post.content
%>
</p>
<p>
<b>Tags:</b>
<%=
@post.tags.map
{
|t|
t.name
}.join(“,
”)
%>
</p>
<h2>Comments</h2>
<%=
render
@post.comments
%>
<h2>Add
a
comment:</h2>
<%=
render
“comments/form”
%>
<%=
link_to
‘Edit
Post’,
edit_post_path(@post)
%>
|
<%=
link_to
‘Back
to
Posts’,
posts_path
%>
|
With these changes in place, you’ll find that you can edit a post and its tags directly on the same view.通过这写修改,你会发现你可以直接在post form中编辑tags。
However, that method call @post.tags.map { |t| t.name }.join(“, ”) is awkward, we could handle this by making a helper method.另外,@post.tags.map { |t| t.name }.join(“, ”)这个方法很别扭,我们可以通过编写一个helper method。
###上面都还只能一次创建一个tag在post form
12 View Helpers
View Helpers live in app/helpers and provide small snippets of reusable code for views.View Helpers放置在app/helpers,它提供了可重用的小代码片段给view。In our case, we want a method that strings a bunch of objects together using their name attribute and joining them with a comma.在本例,我们想要一个方法把(tag)放在一起(一个字符串中),并且使用逗号分割。As this is for the Post show template, we put it in the PostsHelper.要想这样在Post show模板,我们在PostHelper中写入:
Now you can edit the view in app/views/posts/show.html.erb to look like this:现在你可以在app/views/posts/show.html.erb中更改:
<p
class=“notice”><%=
notice
%></p>
<p>
<b>Name:</b>
<%=
@post.name
%>
</p>
<p>
<b>Title:</b>
<%=
@post.title
%>
</p>
<p>
<b>Content:</b>
<%=
@post.content
%>
</p>
<p>
<b>Tags:</b>
<%=
join_tags(@post)
%>
</p>
<h2>Comments</h2>
<%=
render
@post.comments
%>
<h2>Add
a
comment:</h2>
<%=
render
“comments/form”
%>
<%=
link_to
‘Edit
Post’,
edit_post_path(@post)
%>
|
<%=
link_to
‘Back
to
Posts’,
posts_path
%>
|
13 What’s Next?接下来做什么呢?
Now that you’ve seen your first Rails application, you should feel free to update it and experiment on your own. But you don’t have to do everything without help.现在你已经看到了你的第一个Rails应用程序,你应该可以很轻松的继续更新它或者试验一下你的想法。As you need assistance getting up and running with Rails, feel free to consult these support resources:当你在更新和运行Rails的时候需要援助,咨询下面推荐的资源会让你感到轻松:
- The RubyonRailsguides
- The RubyonRailsTutorial
- The RubyonRailsmailinglist
- The #rubyonrails channel on irc.freenode.net
- The RailsWiki
Rails also comes with built-in help that you can generate using the rake command-line utility:Rails同样也带有内置的帮助你可以使用rake命令实用工具在你的应用程序中创建帮助文档:
- Running rake doc:guides will put a full copy of the Rails Guides in the doc/guides folder of your application. Open doc/guides/index.html in your web browser to explore the Guides. 运行rake doc:guides将会输出所有Rails Guides的文档到你的应用程序中的doc/guides中。在你的浏览器中打开/guides/index.html浏览Guides。
- Running rake doc:rails will put a full copy of the API documentation for Rails in the doc/api folder of your application. Open doc/api/index.html in your web browser to explore the API documentation. 运行 rake doc:rails将会输出所有Rails API 的文档到你的应用程序中的doc/api中。
因为redcloth的问题文档支持有点故障,虽然找到了一种解决方法但是不够完美以待官方或者来人修复。
14 Configuration Gotchas配置陷阱
The easiest way to work with Rails is to store all external data as UTF-8.Rails使用Rails最简单的工作方式是存储所有的外部数据为UTF-8编码。If you don’t, Ruby libraries and Rails will often be able to convert your native data into UTF-8, but this doesn’t always work reliably, so you’re better off ensuring that all external data is UTF-8.如果不那样做,Ruby libraries和Rails通才会转换你的自然数据成UTF-8编码,但是这样不是很可靠,因此你最好保证所有的外部数据是UTF-8编码。
If you have made a mistake in this area, the most common symptom is a black diamond with a question mark inside appearing in the browser. 如果你在这里犯了错误,一般的症状就是在浏览器中出现钻石符号(可能是^)变成了问号。Another common symptom is characters like “ü” appearing instead of “ü”. 另一个普遍症状是“ü”变成了 “ü”。Rails takes a number of internal内部steps to mitigate减轻common causes of these problems that can be automatically detected检测and corrected. However, if you have external data that is not stored as UTF-8, it can occasionally偶尔result in these kinds of issues that cannot be automatically detected by Rails and corrected更正.
Two very common sources of data that are not UTF-8:两种非常普遍的不是UTF-8 编码的源数据:
- Your text editor: Most text editors (such as Textmate), default to saving files as UTF-8. If your text editor does not, this can result in special characters that you enter in your templates (such as é) to appear as a diamond with a question mark inside in the browser. This also applies to your I18N translation files. Most editors that do not already default to UTF-8 (such as some versions of Dreamweaver) offer a way to change the default to UTF-8. Do so.
- Your database. Rails defaults to converting data from your database into UTF-8 at the boundary. However, if your database is not using UTF-8 internally, it may not be able to store all characters that your users enter. For instance, if your database is using Latin-1 internally, and your user enters a Russian, Hebrew, or Japanese character, the data will be lost forever once it enters the database. If possible, use UTF-8 as the internal storage of your database.