Francis's Octopress Blog

A blogging framework for hackers.

Ruby-debug in 30 Seconds (We Don’t Need No Stinkin’ GUI!)

ruby-debug in 30 seconds (we don’t need no stinkin’ GUI!)

Many people (including me) have complained about the lack of a good GUI debugger for Ruby. Now that some are finally getting usable, I’ve found I actually prefer IRB-style ruby-debug to a GUI.

There’s good tutorial links on the ruby-debug homepage, and a very good Cheat sheet, but I wanted to give a bare-bones HOWTO to help you get immediately productive with ruby-debug.

Install the latest gem

$ gem install ruby-debug 

Install the cheatsheet

$ gem install cheat $ cheat rdebug 

Set autolist, autoeval, and autoreload as defaults

$ vi ~/.rdebugrc set autolist set autoeval set autoreload 

Run Rails (or other app) via rdebug

$ rdebug script/server 

Breakpoint from rdebug

(rdb:1) b app/controllers/my_controller.rb:10 

Breakpoint in source

require 'ruby-debug' debugger my_buggy_method('foo') 

Catchpoint

(rdb:1) cat RuntimeError 

Continue to breakpoint

(rdb:1) c 

Next Line (Step Over)

(rdb:1) n 

Step Into

(rdb:1) s 

Continue

(rdb:1) c 

Where (Display Frame / Call Stack)

(rdb:1) where 

List current line

(rdb:1) l= 

Evaluate any var or expression

(rdb:1) myvar.class 

Modify a var

(rdb:1) @myvar = 'foo' 

Help

(rdb:1) h 

There are many other commands, but these are the basics you need to poke around. Check the Cheat sheet for details.

This can also be used directly from any IDE that supports input into a running console (such as Intellij Idea).

That should get you started. So, before you stick in another ‘p’ to debug, try out ruby-debug instead!

require ‘rubygems’ require ‘ruby-debug’

$ruby breakpoint_test.rb

vim breakpoint_test.rb

:!ruby breakpoint_test.rb

Next Line (Step Over)

(rdb:1) n

Step Into

(rdb:1) s

to debug in a function which is called from other place,before this function call,type ’s' and you will found your are inside this function.

def leap_year year leap = case breakpoint when year % 400 == 0: true when year % 100 == 0: false else year % 4 ==0 end puts leap end if FILE == $0

tests…

puts “year=2000” leap_year 2000 puts “year=2004” leap_year 2004 puts “year=2002” leap_year 2002 end

标签: breakpoint debug rails ruby shell

Action Controller Overview

Action Controller Overview

Controllers

Action Controller Overview
This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.
Rails Routing from the Outside In
This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here.

Action Controller Overview

In this guide you will learn how controllers work and how they fit into the request cycle in your application. After reading this guide, you will be able to:

在这个gudie中你将会学习到controllers是怎样工作的以及它们在你的应用程序中是怎用配合完成request cycle

  • Follow the flow of a request through a controller

跟随requset流向一个controller

  • Understand why and how to store data in the session or cookies

明白在会话和cookies中为什么以及怎样存储data

  • Work with filters to execute code during request processing

使用filters来工作在request过程中执行code

  • Use Action Controller’s built-in HTTP authentication

使用Action Controller的内置的HTTP认证

  • Stream data directly to the user’s browser

Stream data直接(到)用户的浏览器

  • Filter sensitive parameters so they do not appear in the application’s log

过滤敏感参数使得它们不会在应用程序的log中出现

  • Deal with exceptions that may be raised during request processing

处理可能在request进程之间抛出的意外

1 What Does a Controller Do?

Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible. Action ControllerMVC中的C。在routing已经决定了对于一个request使用哪个controller,你的controller负责请求的实际意图并且产生合适的输出。幸运的是,Action Controller为了做了大多数的基础工作并且使用智能方便的(方式)使得C能够尽可能的直接干脆。

For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that’s not a problem, this is just the most common way for a controller to work.

对于大多数的传统的RESTful应用程序,controller将会收到请求(这对于开发者你来说是无形的),从一个model刷新或者savee数据或者使用一个视图来创建HTML输出。如果你的controller需要做些略微不同的事情,这不是问题,这仅仅是controller在工作中大多数通常的方式。

A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model.

一个controller能够这样,作为在modelsviews之间的中间人。它使得model数据可以用户view因此它可以显示这些数据给用户,同时它从用户保存或者更新数据到model

For more details on the routing process, see RailsRoutingfromtheOutsideIn.

2 Methods and Actions

A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.

一个controller是一个Ruby类它继承至ApplicationController同时像其他类一样它也有方法。当你的应用程序收到一个requestrouting将会决定那个controlleraction会运行,然后Rails创建一个controller的实例同时运行与action名字相同的方法。

class ClientsController < ApplicationController

def new

end

end

As an example, if a user goes to /clients/new in your application to add a new client, Rails will create an instance of ClientsController and run the new method. Note that the empty method from the example above could work just fine because Rails will by default render the new.html.erb view unless the action says otherwise. The new method could make available to the view a @client instance variable by creating a new Client:

正如一个例子,如果一个user导航至/clients/new 在你的应用程序中添加一个new clientRails将会创建一个ClientsController的实例并且运行new方法。注意例子中空的方法完全可以工作因为Rails将会默认render名称为new.html.erb的视图除非action指定了另外的。new方法通过创建一个新的Client将使得可以查看@client实例变量。

def new

@client = Client.new

end

The Layouts&RenderingGuide explains this in more detail.

ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you’re curious to see what’s in there, you can see all of them in the API documentation or in the source itself.

ApplicationController继承至ActionController::Base,器定义了很多helpful方法。这个教材将会涵盖这些,但是如果你好奇与其中有些什么,你可以在API文档中或者他的源代码中看到所有的方法。

Only public methods are callable as actions. It is a best practice to lower the visibility of methods which are not intended to be actions, like auxiliary methods or filters.

对于action只有公共的方法才是可调用的。它是低敏感度方法的最好实践,而这些对actions没有义务,像辅助方法或者过滤器。

3 Parameters

You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after “?” in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from an HTML form which has been filled in by the user. It’s called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the params hash in your controller:

在你的controller action中你可能想访问用户或者其他参数发送的数据。一个web应用程序中这里可能有两种参数。第一种是作为URL一部分发送的参数,叫做query string parameters(字符串查询参数)。查询字符串是在URL?”后面的所有字符。第二种类型的参数通常被简称为POST data。这类信息通常来至于一个被用户填写的HTML表单。它被称为POST data因为它仅能通过HTTP POST 请求的一部分发送。Rails在字符串查询参数和POST data参数之间不做任何区分,它们在你的controllerparams字典中都是可用的。

class ClientsController < ActionController::Base

This action uses query string parameters because it gets run

by an HTTP GET request, but this does not make any difference

to the way in which the parameters are accessed. The URL for

this action would look like this in order to list activated

clients: /clients?status=activated

def index

if params[:status] == “activated”

@clients = Client.activated

else

@clients = Client.unactivated

end

end

 

This action uses POST parameters. They are most likely coming

from an HTML form which the user has submitted. The URL for

this RESTful request will be “/clients”, and the data will be

sent as part of the request body.

def create

@client = Client.new(params[:client])

if @client.save

redirect_to @client

else

This line overrides the default rendering behavior, which

would have been to render the “create” view.

render :action => “new”

end

end

end

3.1 Hash and Array Parameters

The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append an empty pair of square brackets “[]” to the key name:

params hash字典不限于一维的关键字和值。它能够包含数组和(嵌套)字典。要发送一个数组值,添加一个空的[]”key name

GET /clients?ids[]=1&ids[]=2&ids[]=3

The actual URL in this example will be encoded as/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3as[and]are not allowed in URLs. Most of the time you don’t have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.但是如果你发现你自己不得不手动的发送这些请求到服务器你必须注意。

The value of params[:ids] will now be [“1”, “2”, “3”]. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.

To send a hash you include the key name inside the brackets:

<form accept-charset=“UTF-8” action=“/clients” method=“post”>

<input type=“text” name=“client[name]” value=“Acme” />

<input type=“text” name=“client[phone]” value=“12345” />

<input type=“text” name=“client[address][postcode]” value=“12345” />

<input type=“text” name=“client[address][city]” value=“Carrot City” />

</form>

When this form is submitted, the value of params[:client] will be {“name” =>Acme,phone=>12345,address=> {“postcode” =>12345,city=>Carrot City}}. Note the nested hash in params[:client][:address].

Note that the params hash is actually an instance of HashWithIndifferentAccess from Active Support, which acts like a hash that lets you use symbols and strings interchangeably互换as keys.

3.2 JSON/XML parameters

If you’re writing a web service application, you might find yourself more comfortable on accepting parameters in JSON or XML format. Rails will automatically convert your parameters into params hash, which you’ll be able to access like you would normally do with form data.

如果你打算写一个web服务程序,你可能会发现使用JSON或者XML格式接收参数会更加舒服。Rails将会自动的转换你的参数到params hash字典中,其中你将可以像正常表单数据那样接收数据。

So for example, if you are sending this JSON parameter:

{ "company": { "name": "acme", "address": "123 Carrot Street" } }

You’ll get params[:company] as { :name =>acme,address=>123 Carrot Street}.

Also, if you’ve turned on config.wrap_parameters in your initializer or calling wrap_parameters in your controller, you can safely omit the root element in the JSON/XML parameter. The parameters will be cloned and wrapped in the key according to your controller’s name by default. So the above parameter can be written as:

同样,如果你已经打开config.wrap_parameters在你的controller中初始化或者调用wrap_parameters,在JSON/XML参数中你可以安全的忽略root element。参数将会克隆和包装在key默认会依照你的controllername。因此完整的参数可以这样写:

{ "name": "acme", "address": "123 Carrot Street" }

And assume假设that youre sending the data to CompaniesController, it would then be wrapped in :company key like this:

{ :name => “acme”, :address => “123 Carrot Street”, :company => { :name => “acme”, :address => “123 Carrot Street” }}

You can customize the name of the key or specific parameters you want to wrap by consulting the APIdocumentation

3.3 Routing Parameters

The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values. Any other parameters defined by the routing, such as :id will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the :status parameter in a “pretty” URL:

params hash字典总会包含关键字:controller and :action,但是你应该使用方法controller_name and action_name作为替代访问这些值。也有一些其他的值被routing定义,例如:id也是可以的。例如,思考一个clients的列表能够显示active或者inactive client。我们可以添加一个route它会捕获在aprettyURL(一个漂亮的URL)中的:status参数:

match ‘/clients/:status’ => ‘clients#index’, :foo => “bar”

In this case, when a user opens the URL /clients/active, params[:status] will be set to “active”. When this route is used, params[:foo] will also be set to “bar” just like it was passed in the query string. In the same way params[:action] will contain “index”.

3.4 default_url_options

You can set global default parameters that will be used when generating URLs with default_url_options. To do this, define a method with that name in your controller:

class ApplicationController < ActionController::Base

The options parameter is the hash passed in to ‘url_for’

def default_url_options(options)

{:locale => I18n.locale}

end

end

These options will be used as a starting-point when generating URLs, so it’s possible they’ll be overridden by url_for. Because this method is defined in the controller, you can define it on ApplicationController so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.

4 Session会话

Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms:

你的应用程序对每一位用户都有一个会话通过它你可以存储少量的数据这会将request区分开来。

  • CookieStore – Stores everything on the client.
  • DRbStore – Stores the data on a DRb server.
  • MemCacheStore – Stores the data in a memcache.
  • ActiveRecordStore – Stores the data in a database using Active Record.

All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).

每个会话使用一个cookie存储并且每个会话都有一个独特的ID(你必须使用一个cookieRails不允许你在URL中传送会话ID因为这样不安全)。

For most stores this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store – the CookieStore – which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited).

大多数(时候)存储这个ID是用来在server上面查找session data,例如,存放在一个数据库table中。这里有一个例外,并且这是默认以及推荐的session存储-CookieStore-它存储所有的session数据在它的cookie中(如果你需要ID同样是可用的)。这里的优势是十分轻量级并且在一个新应用程序中使用session是零安装。cookie数据以加密签名的方式以防篡改,但是它没有将内容译成迷文,因此任何人访问它都可以阅读它的内容但是不能编辑它(Rails将不会接受被编辑过的cookie)。

The CookieStore can store around 4kB of datamuch less than the othersbut this is usually enough. Storing large amounts of data in the session is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error.

CookieStore能够存储大约4KB的数据——远少于其他——但是这通常足够。在你应用程序使用的session中,存储大量的数据是无比泄气的。你应该尤其避免存储复杂的对象(任何其他的Ruby基本对象,最普遍的例子是model实例)在session,因为server可能不能在不同的请求间重组它们,结果它将得到错误。

Read more about session storage in the SecurityGuide.

If you need a different session storage mechanism, you can change it in the config/initializers/session_store.rb file:

如果你需要一个不同的session存储机制,你可以在这里更改config/initializers/session_store.rb

Use the database for sessions instead of the cookie-based default,

which shouldn’t be used to store highly confidential information

(create the session table with “script/rails g session_migration”)

YourApp::Application.config.session_store :active_record_store

Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in config/initializers/session_store.rb:

Rails当注册session data的时候会设置一个session key(cookie的名字)。也可以在这里修改config/initializers/session_store.rb

Be sure to restart your server when you modify this file.

 

YourApp::Application.config.session_store :cookie_store, :key => ‘_your_app_session’

You can also pass a :domain key and specify the domain name for the cookie:

你也可以通过一个:domain关键字指定cookie的域名:

Be sure to restart your server when you modify this file.

 

YourApp::Application.config.session_store :cookie_store, :key => ‘_your_app_session’, :domain => “.example.com”

Rails sets up (for the CookieStore) a secret key密钥used for signing the session data. This can be changed in config/initializers/secret_token.rb

Be sure to restart your server when you modify this file.

 

Your secret key for verifying the integrity of signed cookies.

If you change this key, all old signed cookies will become invalid!

Make sure the secret is at least 30 characters and all random,

no regular words or you’ll be exposed to dictionary attacks.

YourApp::Application.config.secret_token = ‘49d3f3de9ed86c74b94ad6bd0…’

Changing the secret when using the CookieStore will invalidate废除all existing sessions.

当使用CookieStore的时候改变密钥将会废除所有存在的sessions

4.1 Accessing the Session

In your controller you can access the session through the session instance method.

在你的controller你可以访问session通过session实例方法。

Sessions are lazily loaded. If you dont access sessions in your actions code, they will not be loaded. Hence因此you will never need to disable sessions, just not accessing them will do the job.

Session values are stored using key/value pairs like a hash:

session的值使用key/value对存储就像一个hash字典:

class ApplicationController < ActionController::Base

 

private

 

Finds the User with the ID stored in the session with the key

:current_user_id This is a common way to handle user login in

a Rails application; logging in sets the session value and

logging out removes it.

def current_user

@_current_user ||= session[:current_user_id] &&

User.find_by_id(session[:current_user_id])

end

end

To store something in the session, just assign it to the key like a hash:

要存储一些信息在session中,仅仅需要将其指派给key就像一个hash字典一样:

class LoginsController < ApplicationController

“Create” a login, aka “log the user in”

def create

if user = User.authenticate(params[:username], params[:password])

Save the user ID in the session so it can be used in

subsequent requests

session[:current_user_id] = user.id

redirect_to root_url

end

end

end

To remove something from the session, assign that key to be nil:

session从移除一些信息,分派那个keynil

class LoginsController < ApplicationController

“Delete” a login, aka “log the user out”

def destroy

Remove the user id from the session

@_current_user = session[:current_user_id] = nil

redirect_to root_url

end

end

To reset the entire session, use reset_session.从值整个session,使用reset_session

4.2 The Flash

The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let’s use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:

flashsession中是一个特殊的部分它它清楚于每个(下一次)请求。这里的意思是存储的值仅仅在下一个请求中是可用的,这在存储错误信息等是非常有用的。它的访问方式与session的访问方式有着很大的相同之处,就像(访问)一个hash字典那样。让我们使用登出动作作为一个例子。controller会发送一个消息这个消息在下一个请求的时候将会显示给用户:

class LoginsController < ApplicationController

def destroy

session[:current_user_id] = nil

flash[:notice] = “You have successfully logged out”

redirect_to root_url

end

end

Note it is also possible to assign a flash message as part of the redirection.

redirect_to root_url, :notice => “You have successfully logged out”

The destroy action redirects to the application’s root_url, where the message will be displayed. Note that it’s entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It’s conventional to display eventual errors or notices from the flash in the application’s layout:

destroy动作重定向到应用程序的root_url,在这里消息(刚添加的)将会被显示。注意:它完全取决于next action来决定做什么,如果有(next action),它将会完成previous action放入flash中的信息。

<html>

<!— <head/> —>

<body>

<% if flash[:notice] %>

<p><%= flash[:notice] %></p>

<% end %>

<% if flash[:error] %>

<p><%= flash[:error] %></p>

<% end %>

<!— more content —>

</body>

</html>

This way, if an action sets an error or a notice message, the layout will display it automatically

通过这种方式,如果action设定一条error或者通知消息,laout将会自动的显示它。

If you want a flash value to be carried over to another request, use the keep method:

如果你想一个flash值转接到另一个request,使用keep方法:

class MainController < ApplicationController

Let’s say this action corresponds to root_url, but you want

all requests here to be redirected to UsersController#index.

If an action sets the flash and redirects here, the values

would normally be lost when another redirect happens, but you

can use ‘keep’ to make it persist for another request.

def index

Will persist all flash values.

flash.keep

 

You can also use a key to keep only some kind of value.

flash.keep(:notice)

redirect_to users_url

end

end

4.2.1 flash.now

By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the create action fails to save a resource and you render the new template directly, that’s not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use flash.now in the same way you use the normal flash:

默认情况,添加到flash中的值在next request中是可用的,但是有时你可以想在同样的request中访问这些值。例如,如果create action保存resource失败并且你直接render new template,这样在一个newrequest中没有得到result,但是你仍然希望使用flash显示消息。这样做,你可以就像使用flash那样使用flash.now

class ClientsController < ApplicationController

def create

@client = Client.new(params[:client])

if @client.save

else

flash.now[:error] = “Could not save client”

render :action => “new”

end

end

end

5 Cookies

Your application can store small amounts of data on the client — called cookies — that will be persisted across requests and even sessions. Rails provides easy access to cookies via the cookies method, which — much like the session — works like a hash:

你的应用程序可以存储少量dataclient——它被称之为cookies——它会保持访问的请求以及曾经sessionsRails提供简单的方式来访问cookies通过cookies方法,就像session——hash字典那样工作(存储data):

class CommentsController < ApplicationController

def new

Auto-fill the commenter’s name if it has been stored in a cookie

@comment = Comment.new(:name => cookies[:commenter_name])

end

 

def create

@comment = Comment.new(params[:comment])

if @comment.save

flash[:notice] = “Thanks for your comment!”

if params[:remember_name]#:remember_name这里相当于用户的的反馈是否记住name

Remember the commenter’s name.

cookies[:commenter_name] = @comment.name

else

Delete cookie for the commenter’s name cookie, if any.

cookies.delete(:commenter_name)

end

redirect_to @comment.article

else

render :action => “new”

end

end

end

Note that while for session values you set the key to nil, to delete a cookie value you should use cookies.delete(:key).

6 Rendering xml and json data

ActionController makes it extremely easy to render xml or json data. If you generate a controller using scaffold then your controller would look something like this.

ActionController使得render xml或者json数据相当简单。如果你使用scaffold创建一个controller那么你的controller看起来将会像这样:

class UsersController < ApplicationController

def index

@users = User.all

respond_to do |format|

format.html # index.html.erb

format.xml { render :xml => @users}

format.json { render :json => @users}

end

end

end

Notice that in the above case code is render :xml => @users and not render :xml => @users.to_xml. That is because if the input is not string then rails automatically invokes to_xml .

注意在render中的完整示例代码是render :xml => @users并不是render :xml => @users.to_xml,那是因为如果输入的不是string那么rails自动调用to_xml

7 Filters过滤器

Filters are methods that are run before, after or “around” a controller action.

Filters是一个方法它运行在controller action之前或者伴随着controller action

Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application.

Filters是可继承的,因此如果你在ApplicationController 设置了一个filter,他将会在你的应用程序的每个controller中运行。

Before filters may halt the request cycle. A common before filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:

filters之前可能会停止request周期。一个通常情况在filter之前的是用户logged inrequires使之对应的action被运行。你可以这样定义filter方法:

class ApplicationController < ActionController::Base

before_filter :require_login

 

private

 

def require_login

unless logged_in?

flash[:error] = “You must be logged in to access this section”

redirect_to new_login_url # halts request cycle

end

end

 

The logged_in? method simply returns true if the user is logged

in and false otherwise. It does this by “booleanizing” the

current_user method we created previously using a double ! operator.

Note that this is not common in Ruby and is discouraged unless you

really mean to convert something into true or false.

def logged_in?

!!current_user

end

end

The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a before filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter they are also cancelled.

这个方法简单的存储一个错误消息在flash中并且重定向到login表单如果用户没有登录。如果是一个在filter之前的renderes或者重定向,这个action将不会运行。如果这里有额外附加的filters计划运行并且在这个filter之后,它们也将取消(执行)。

In this example the filter is added to ApplicationController and thus all controllers in the application inherit it. This will make everything in the application requiretheusertobeloggedininordertouseit. For obvious reasons (the user wouldn’t be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with skip_before_filter:

明显的原因(在第一次登录的时候不能登录),并不是所有的controllers或者actions需要登录,你可以使用skip_before_filter 在部分action运行之前阻止这个filter

class LoginsController < ApplicationController

skip_before_filter :require_login, :only => [:new, :create]

end

Now, the LoginsController’s new and create actions will work as before without requiring the user to be logged in. The :only option is used to only skip this filter for these actions, and there is also an :except option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.

现在,LoginsControllernewcreate action工作之前不再需要用户已经登录站点来。:only选项使用于仅仅在这些action中略过filter,同样这里也有:except选项一相反的方式工作。这些选项也可以在添加filters的时候使用,因此你可以添加一个filter其仅仅在选择的action第一次运行的时候执行(忽略)。

7.1 After Filters and Around Filters

In addition to before filters, you can also run filters after an action has been executed, or both before and after.

除了在filters之前,你也可以在action被执行之后运行filters,或者在之前和过后都执行。

After filters are similar to before filters, but because the action has already been run they have access to the response data that’s about to be sent to the client. Obviously, after filters cannot stop the action from running.

After filters(执行action)和before filters类似,但是因为action已经被运行,这些filter会访问response data并且,response data会全部发送给client。明显的,after filters不能够停止action的运行。

Around filters are responsible for running their associated actions by yielding, similar to how Rack middlewares work.

Around filters能够通过yielding运行(Around filters执行的action的)关联的action,类似于Rack middlewares工作方式。

For example, in a website where changes have an approval workflow an administrator could be able to preview them easily, just apply them within a transaction:

例如,在一个website更改操作有一个批准工作流程,管理员可以很容易预览这些内容,仅仅在一个transaction中就可以实现这个功能的应用:

class ChangesController < ActionController::Base

around_filter :wrap_in_transaction, :only => :show

 

private

 

def wrap_in_transaction

ActiveRecord::Base.transaction do

begin

yield

ensure

raise ActiveRecord::Rollback

end

end

end

end

Note that an around filter wraps also rendering. In particular, if in the example above the view itself reads from the database via a scope or whatever, it will do so within the transaction and thus present the data to preview.

注意around filter同样也会render。特别的是,如果在上述例子中,view自己通过一个scpoe或者其他从数据库中读取(数据),他将会在transaction中实现approval workflow并且将当前数据预览呈现。

They can choose not to yield and build the response themselves, in which case the action is not run.

他们(admin)可以选择不yield并且建立他们自己的response,这样(更改的)action将不会运行。

7.2 Other Ways to Use Filters 过滤器的其他使用方式

While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.

即使通常使用过滤器的方式是创建私有的方法并且使用*_filteraround_filter :wrap_in_transaction, :only => :show)来添加他们,这里有两种其他的方法来做同样的事情。

The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the require_login filter from above could be rewritten to use a block:

第一种方法是使用一个*_filter methodsblock(块)。这个block接收controller为一个参数,,并且从above(上下文中的上文)could be rewritten to use a block#这个例子中也是第一次登录无法完成,没有忽略登录action

class ApplicationController < ActionController::Base

before_filter do |controller|

redirect_to new_login_url unless controller.send(:logged_in?)

end

end

Note that the filter in this case uses send because the logged_in? method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.

注意这个例子中的filter使用的是send因为logged_in?方法是私有的并且filter并不在controller的范围内运行。这里并不是实施这个特别的filter的推荐的方式,但是在很多简单的情况下这可能会有用。

The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex and can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:

第二种方式是使用一个class(实际上,响应正确方法的任何对象都是可以的)来处理filtering。这在非常复杂的情况下,在只读不能implemented,重复使用两种其他方法是非常有用的。在例子中,你可以使用一个class来再次重写login filter

class ApplicationController < ActionController::Base

before_filter LoginFilter

end

 

class LoginFilter

def self.filter(controller)

unless controller.send(:logged_in?)

controller.flash[:error] = “You must be logged in”

controller.redirect_to controller.new_login_url

end

end

end

Again, this is not an ideal example for this filter, because it’s not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method filter which gets run before or after the action, depending on if it’s a before or after filter. Classes used as around filters can also use the same filter method, which will get run in the same way. The method must yield to execute the action. Alternatively, it can have both a before and an after method that are run before and after the action.

同样的,对于这种filter这不是一个(好)主意,因为它在controller的范围中不会运行但是获取controller为一个参数。filter 类有一个类方法filter,它在before或者after action运行,这依赖于它是一个before还是after filter。使用around filters的类同样也可以使用filter 方法,其也会以相同的方式运行。(filter)方法必须yield to 执行的action。另外,它也可以同时有beforeafter方法在before after action中运行。

8 Request Forgery Protection伪造请求保护

Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user’s knowledge or permission.

跨站伪造请求是一种典型的攻击方式,在这种攻击方式中,一个网站诱骗用户发送请求到另一个网站,可能在这个站点中添加,修改或者删除数据并没有用户的信息或者权限。

The first step to avoid this is to make sure all “destructive” actions (create, update and destroy) can only be accessed with non-GET requests. If you’re following RESTful conventions you’re already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that’s where the request forgery protection comes in. As the name says, it protects from forged requests.

避免这样的事情发生的第一步是确保所有的destructive”(破坏性)actions(create, update and destroy)只能接受non-GET请求。如果你遵循RESTful公约你已经这样做了。然而,一个恶意的网站仍然可以轻易的发送一个non-GET请求到你的站点,到这里就该伪造请求保护(request forgery protection)出场了。正如名字所说,它防护伪造请求(对你站点的攻击)。

If you generate a form like this:如果你像下面这样生成表单:

<%= form_for @user do |f| %>

<%= f.text_field :username %>

<%= f.text_field :password %>

<% end %>

You will see how the token gets added as a hidden field:

你将会看到令牌是怎样作为一个hidden field添加:

<form accept-charset=“UTF-8” action=“/users/1” method=“post”>

<input type=“hidden”

value=“67250ab105eb5ad10851c00a5621854a23af5489”

name=“authenticity_token”/>

<!— fields —>

</form>

Rails adds this token to every form that’s generated using the formhelpers, so most of the time you don’t have to worry about it. If you’re writing a form manually or need to add the token for another reason, it’s available through the method form_authenticity_token:

Rails为每个使用form helpers生成的form添加这样的令牌。因此大多数时间你不需要担心这样的问题。如果你打算手动编写一个form或者因为其他的原因需要添加令牌,这也是可以的通过form_authenticity_token方法来实现:

form_authenticity_token生成一个有效的认证令牌。这在Rails不会自动添加的地方非常有用,比如在定制Ajax调用的时候。

The SecurityGuide has more about this and a lot of other security-related issues that you should be aware of when developing a web application.

SecurityGuide 有更多的关于这些以及许多其他安全相关的问题(的解决方法)你在开发一个web application的时候应该保持清醒。

9 The Request and Response Objects

In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The request method contains an instance of AbstractRequest and the response method returns a response object representing what is going to be sent back to the client.

在每个controller有两个指向与目前正在执行的请求周期相关的request response 对象的访问方法。request方法包含一个AbstractRequest的实例以及response方法返回一个response对象表示什么打算发送回client

9.1 The request Object

The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the APIdocumentation. Among the properties that you can access on this object are:

request对象包含很多来自发出请求的client的有用信息。要得到可用方法的完整列表,参考APIdocumentation。其中你可以在这个对象中访问的属性有:

 

Property of request Purpose
host The hostname used for this request.
domain(n=2) The hostname’s first n segments, starting from the right (the TLD).
format The content type requested by the client.
method The HTTP method used for the request.
get?, post?, put?, delete?, head? Returns true if the HTTP method is GET/POST/PUT/DELETE/HEAD.
headers Returns a hash containing the headers associated with the request.
port The port number (integer) used for the request.
protocol Returns a string containing the protocol used plus “://”, for example “http://”.
query_string The query string part of the URL, i.e., everything after “?”.
remote_ip The IP address of the client.
url The entire URL used for the request.

 

9.1.1 path_parameters, query_parameters, and request_parameters

Rails collects all of the parameters sent along with the request in the params hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The query_parameters hash contains parameters that were sent as part of the query string while the request_parameters hash contains parameters sent as part of the post body. The path_parameters hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.

Rails收集所有的parametersparams hash字典中于request一起发送,无论是作为query 字符串的一部分发送还是在post body中发送。request有三个访问器提供给你访问这些参数这依赖于它来自哪里。query_parameters hash字典包含作为查询字符串的一部分发送的参数,而request_parameters包含作为post body的一个部分发送的参数。path_parameters hash字典包含被routing组织作为指向特殊的controlleraction的参数。

9.2 The response Object

The response object is not usually used directly, but is during the execution of the action and rendering of the data that is being sent back to the user, but sometimes – like in an after filter – it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.

response object通常并不直接使用,但是它在action的执行与rendering的数据被发送回给用户期间建立,但是有的时候——像在一个after filter——它在直接访问responseobject)会非常有用。这些访问器中的一些methods也有setters,允许你改变它们的值。

 

Property of response Purpose
body This is the string of data being sent back to the client. This is most often HTML.
status The HTTP status code for the response, like 200 for a successful request or 404 for file not found.
location The URL the client is being redirected to, if any.
content_type The content type of the response.
charset The character set being used for the response. Default is “utf-8”.
headers Headers used for the response.

 

9.2.1 Setting Custom Headers

If you want to set custom headers for a response then response.headers is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them automatically. If you want to add or change a header, just assign it to response.headers this way:

response.headers[“Content-Type”] = “application/pdf”

10 HTTP Authentications

Rails comes with two built-in HTTP authentication mechanisms:

  • Basic Authentication
  • Digest Authentication

10.1 HTTP Basic Authentication

HTTP basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser’s HTTP basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, http_basic_authenticate_with.

基于HTTP的认证是一个认证结构它被主流的浏览器以及其他的HTTP clients支持。作为一个例子,思考一个管理员section它仅仅可以输入usernamepassword到浏览器的基于HTTP对话框窗口。使用内建的认证是十分简单的仅仅需要你使用一个方法,http_basic_authenticate_with

class AdminController < ApplicationController

http_basic_authenticate_with :name => “humbaba”, :password => “5baa61e4”

end

With this in place, you can create namespaced controllers that inherit from AdminController. The filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication.

通过将这段代码,你可以创建继承至AdminControllernamespaced controller。过滤器将自动对这些controllers中的所有的action运行这样的(认证),使用基于HTTP的认证来保护它们。

##in demo

class PostsController < ApplicationController

http_basic_authenticate_with :name => “test”, :password => “123456”, :except => :index

end

10.2 HTTP Digest Authentication

HTTP digest authentication is superior to the basic authentication as it does not require the client to send an unencrypted password over the network (though HTTP basic authentication is safe over HTTPS). Using digest authentication with Rails is quite easy and only requires using one method, authenticate_or_request_with_http_digest.

HTTP 精简认证优于基础认证因为它不需要client发送一个未加密的穿过网络(即使HTTP基础的认证比HTTPS更安全)。在Rails中使用 digest authentication相当容易并且仅仅需要使用一个方法,authenticate_or_request_with_http_digest

class AdminController < ApplicationController

USERS = { “lifo” => “world” }

 

before_filter :authenticate

 

private

 

def authenticate

authenticate_or_request_with_http_digest do |username|

USERS[username]

end

end

end

As seen in the example above, the authenticate_or_request_with_http_digest block takes only one argument – the username. And the block returns the password. Returning false or nil from the authenticate_or_request_with_http_digest will cause authentication failure.

正如上面看到例子, authenticate_or_request_with_http_digest代码块仅仅获取一个参数——username。并且blockdo…end之间的block)返回password。从 authenticate_or_request_with_http_digest返回false或者nil将会导致认证失败。

11 Streaming and File Downloads

Sometimes you may want to send a file to the user instead of rendering an HTML page. All controllers in Rails have the send_data and the send_file methods, which will both stream data to the client. send_file is a convenience method that lets you provide the name of a file on the disk and it will stream the contents of that file for you.

有时你可能希望发送一个文件给用户代替rendering一个HTML页面。Rails中的所有controllers都有send_datasend_file方法,它们都将会(将文件)以流数据形式发送给clientsend_file是一个方便的方法它让你提供在disk上的一个文件的name并且它将会把那个文件的内容以stream的形式发送给你。

To stream data to the client, use send_data:

require “prawn”

class ClientsController < ApplicationController

Generates a PDF document with information on the client and

returns it. The user will get the PDF as a file download.

def download_pdf

client = Client.find(params[:id])

send_data generate_pdf(client),

:filename => “#{client.name}.pdf”,

:type => “application/pdf”

end

 

private

 

def generate_pdf(client)

Prawn::Document.new do

text client.name, :align => :center

text “Address: #{client.address}”

text “Email: #{client.email}”

end.render

end

end

The download_pdf action in the example above will call a private method which actually generates the PDF document and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the :disposition option to “inline”. The opposite and default value for this option is “attachment”.

上面例子中的 download_pdf action将会call一个私有的方法这个方法实际上创建PDF文档并且将其作为一个string形式返回。这个string将会随后以stream的形式到client作为一个文件下载并且一个文件名将会建议给用户。提到图形,例如,它可以被嵌入HTML页面。要告诉浏览器一个文件不是(用来)被下载的,你可以设置:disposition选项为‘inline’。这个选项相反的和默认的值是”attachment”

11.1 Sending Files

If you want to send a file that already exists on disk, use the send_file method.

如果你想发送一个已经在硬盘中存在的文件,使用send_file方法。

class ClientsController < ApplicationController

Stream a file that has already been generated and stored on disk.

def download_pdf

client = Client.find(params[:id])

send_file(“#{Rails.root}/files/clients/#{client.id}.pdf”,

:filename => “#{client.name}.pdf”,

:type => “application/pdf”)

end

end

This will read and stream the file 4kB at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the :stream option or adjust the block size with the :buffer_size option.

这将会readstream文件一次4kB,避免一次将整个文件导入内存。你可以用:stream选项关闭流传送使或者设置适当的block size通过:buffer_size选项。

Be careful when using data coming from the client (params, cookies, etc.) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.

在使用来自client的数据的时候要小心(params,cookies,etc.)到(服务器)本地硬盘中的文件,因为这是一个安全风险,可能会允许有人获得并不意味着他们看到的文件。

 

It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack. Although if you do need the request to go through Rails for some reason, you can set the :x_sendfile option to true, and Rails will let the web server handle sending the file to the user, freeing up the Rails process to do other things. Note that your web server needs to support the X-Sendfile header for this to work.

并不推荐你stream静态文件通过Rails如果作为替代你可以存放他们在你的服务器的一个公共的文件夹中。直接使用Apache或者其他web服务器让用户下载这些文件更加有效,避免不必要的request通过整体的Rails堆栈。即使如果因为某些原因你需要request通过Rails,你可以设置:x_sendfile option to trueRails将会让web server handle发送这些文件给用户,释放Rails进程去做其他的事情。注意你的web server需要支持 X-Sendfile header来做这个工作。

11.2 RESTful Downloads

While send_data works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing “RESTful downloads”. Here’s how you can rewrite the example so that the PDF download is a part of the show action, without any streaming:

send_data工作良好,如果你正在创建的一个RESTful应用程序有单独的actions来做file下载通常是不需要的。在REST术语中,来自例子中的PDF文件完全可以被认为仅仅另一种client资源的代表。Rails提供一个容易和相当光滑的方式“RESTful downloads”来做这些。这里重写例子以便于PDF下载是show action的一部分,没有任何的streaming

class ClientsController < ApplicationController

The user can request to receive this resource as HTML or PDF.

def show

@client = Client.find(params[:id])

 

respond_to do |format|

format.html

format.pdf { render :pdf => generate_pdf(@client) }

end

end

end

In order for this example to work, you have to add the PDF MIME type to Rails. This can be done by adding the following line to the file config/initializers/mime_types.rb:

为了是这个例子能够工作,你必须添加 PDF MIME typeRails。可以添加下面的代码行到 config/initializers/mime_types.rb

Mime::Type.register “application/pdf”, :pdf

Configuration files are not reloaded on each request, so you have to restart the server in order for their changes to take effect.

Now the user can request to get a PDF version of a client just by adding “.pdf” to the URL:

GET /clients/1.pdf

12 Parameter Filtering

Rails keeps a log file for each environment in the log folder. These are extremely useful when debugging what’s actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. You can filter certain request parameters from your log files by appending them to config.filter_parameters in the application configuration. These parameters will be marked [FILTERED] in the log.

Rail对于每个环境保留一个log文件在log文件夹中。这些文件相当有用,在当你调试的时候他们是在你应用程序中实际运行的记录,但是在一个live的应用程序中你可以不希望每bit的信息都被存储在log文件中。你可以从你的log文件中过滤某些request parameters,通过添加它们到 config.filter_parametersapplication configurationThese parameters will be marked [FILTERED] in the log.

config.filter_parameters << :password

13 Rescue挽救

Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the ActiveRecord::RecordNotFound exception.

就像你的应用程序包含某些bugs或者另一方面抛出一个异常需要被处理。例如,如果用户follows一个link到一个在数据库中并不存在的resourece,Active Record将会抛出 ActiveRecord::RecordNotFound异常。

Rails’ default exception handling displays a “500 Server Error” message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple “500 Server Error” message to the user, or a “404 Not Found” if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they’re displayed to the user. There are several levels of exception handling available in a Rails application:

Rails的默认异常处理方式是对于所有的异常显示一个“500 Server Error”消息。如果request(相关的定制)在服务器被生成,一个漂亮的traceback以及添加一些信息来显示使得你可以指出什么地方出错了并且怎样处理它。如果requestremote Rails将会仅仅显示一个简单的“500 Server Error” 消息给用户,或者如果这里有一个routing错误或者一个记录不能被发现“404 Not Found”将会发送给用户。有时你可以能希望定制这些错误怎样引起以及为什么它们被显示给用户。在Rails应用程序中这里有一些级别的异常处理可用:

13.1 The Default 500 and 404 Templates

By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the public folder, in 404.html and 500.html respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can’t use RHTML or layouts in them, just plain HTML.

通过一个默认生成的应用程序将会render一个404或者500错误消息。这些消息被包含在public文件夹的静态HTML文件中,分别在 404.html and 500.html。你可以定制这些文件来添加一些其他的信息和layout,但是记住他们是静态的;i.e. you can’t use RHTML or layouts in them, 仅仅是纯HTML

13.2 rescue_from

If you want to do something a bit more elaborate when catching errors, you can use rescue_from, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses.

如果你想做一些事情在catching 错误的时候做多一点的阐述,你可以使用 rescue_from,其处理一种明确的异常类型(或者多种)在整个controller以及它的子类中。

When an exception occurs which is caught by a rescue_from directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the :with option. You can also use a block directly instead of an explicit Proc object.

当一个异常发生其会直接引发 rescue_from,这个exception对象被传递给handler。这个handler可以是一个方法或者一个被传递给:with选项的Proc对象。你也可以直接使用一个block替代准确的Proc对象。

Here’s how you can use rescue_from to intercept all ActiveRecord::RecordNotFound errors and do something with them.

这里是你如何使用 rescue_from来截取所有的ActiveRecord::RecordNotFound errors并且为其做一些事情。

class ApplicationController < ActionController::Base

rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found

 

private

 

def record_not_found

render :text => “404 Not Found”, :status => 404

end

end

Of course, this example is anything but elaborate and doesn’t improve on the default exception handling at all, but once you can catch all those exceptions you’re free to do whatever you want with them. For example, you could create custom exception classes that will be thrown when a user doesn’t have access to a certain section of your application:

当然,这个例子的所有事情仅仅只是阐述以及没有改善默认的异常处理,但是一旦你可以catch所有的异常你可以自由的做你想做的。例如,你可以创建定制的异常类它将在用户访问没有访问权限的应用程序的某些部分时候抛出:

class ApplicationController < ActionController::Base

rescue_from User::NotAuthorized, :with => :user_not_authorized

 

private

 

def user_not_authorized

flash[:error] = “You don’t have access to this section.”

redirect_to :back

end

end

 

class ClientsController < ApplicationController

Check that the user has the right authorization to access clients.

before_filter :check_authorization

 

Note how the actions don’t have to worry about all the auth stuff.

def edit

@client = Client.find(params[:id])

end

 

private

 

If the user is not authorized, just throw the exception.

def check_authorization

raise User::NotAuthorized unless current_user.admin?

end

end

Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik’s article on the subject for more information.

14 Force HTTPS protocol强制HTTPS协议

Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reason. Since Rails 3.1 you can now use force_ssl method in your controller to enforce that:

有时候因为安全原因你可能希望强制特定的cotroller仅仅在HTTPS协议下是可访问的。从Rails 3.1你可以在你的controller使用 force_ssl来强制执行:

class DinnerController

force_ssl

end

Just like the filter, you could also passing :only and :except to enforce the secure connection only to specific actions.

class DinnerController

force_ssl :only => :cheeseburger

or

force_ssl :except => :cheeseburger

end

Please note that if you found yourself adding force_ssl to many controllers, you may found yourself wanting to force the whole application to use HTTPS instead. In that case, you can set the config.force_ssl in your environment file.

请注意如果你发现你自己的添加 force_ssl到许多controllers,你可能发现你自己希望强制整个应用程序使用HTTPS替代。因为这样的原因,你可以在你的环境文件中设置config.force_ssl

controller filter guide rails ruby

Rails Form Helpers

Rails Form helpers

Rails Form helpers

Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.

Formsweb应用程序中是一个必不可少的提供用户输入的interface(接口)。然而,形式标记的编写和维护很快就变得十分乏味是因为表单控件的命名和它们的许多属性。Rails处理这些复杂的(事物)通过提供view helpers来创建form markup。然而,自从有了不同的用户需求,开发人员需要知道所有的similar helper 方法间的的不同在使用它们之前。

In this guide you will:在这个guide中你可以了解到:

  • Create search forms and similar kind of generic forms not representing any specific model in your application在你的应用程序中,创建search forms和生成类似的forms它们不代表任何指定的model
  • Make model-centric forms for creation and editing of specific database records制造model-centric forms来创建和编辑指定的数据库记录
  • Generate select boxes from multiple types of data 从多种数据生成select boxes
  • Understand the date and time helpers Rails provides 明白Rails提供的datetime helpers
  • Learn what makes a file upload form different 学习什么使得一个文件upload form变得不同
  • Learn some cases of building forms to external resources 学习某些情况下为external resources(外部资源)构建forms
  • Find out where to look for complex forms 找出在哪里查找复杂的forms

This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit theRailsAPIdocumentation for a complete reference.

本教程不大算成为一个包含可用的form helpers和它们的参数的完整的文档。请访问theRailsAPIdocumentation得到完整的参考。

1 Dealing with Basic Forms

The most basic form helper is form_tag.最基本的form helperform_tag

<%= form_tag do %>

Form contents

<% end %>

When called without arguments like this, it creates a <form> tag which, when submitted, will POST to the current page. For instance, assuming the current page is /home/index, the generated HTML will look like this (some line breaks added for readability):

当像这样无参调用时,它创建一个<form>标签,当提交这个表单时,会提交到当前页面。作为实例,假设当前页面是/home/index,创建的HTML将会像这样(有一些换行为了增加可读性):

<form accept-charset=“UTF-8” action=“/home/index” method=“post”>

<div style=“margin:0;padding:0”>

<input name=“utf8” type=“hidden” value=“&#x2713;” />

<input name=“authenticity_token” type=“hidden” value=“f755bb0ed134b76c432144748a6d4b7a7ddf2b71” />

</div>

Form contents

</form>

Now, you’ll notice that the HTML contains something extra: a div element with two hidden input elements inside. This div is important, because the form cannot be successfully submitted without it. The first input element with name utf8 enforces browsers to properly respect your form’s character encoding and is generated for all forms whether their actions are “GET” or “POST”. The second input element with name authenticity_token is a security feature of Rails called cross-site request forgery protection, and form helpers generate it for every non-GET form (provided that this security feature is enabled). You can read more about this in the SecurityGuide.

现在,你将会注意到HTML包含一些额外的东西:一个div元素和在里边的两个隐藏的input元素。这个div很重要,因为如果没有它form不能够成功的被提交。第一个input元素有name utf8会强制浏览器合适的遵循你的form’s的字符编码并且无论是它创建的所有forms还是它们的actionsGET” or “POST”)。第二个input元素使用name authenticity_token它是一个Rails的安全特性调用cross-site request forgery protection(站间请求保护),并且form helpers为每个non-GET form (确保这个安全特性是有效的) 创建它。你可以阅读关于他的更多信息在SecurityGuide中。

Throughout this guide, the div with the hidden input elements will be excluded排除from code samples for brevity短暂.通过这个教程,div里面的两个隐藏的input元素将会从samples code暂时排除。

1.1 A Generic Search Form通用搜索表单

One of the most basic forms you see on the web is a search form. This form contains:

搜索表单是你在web上最常见的基本表单之一。这个表单包含:

  1. a form element with “GET” method,
  2. a label for the input,
  3. a text input element, and
  4. a submit element.

To create this form you will use form_tag, label_tag, text_field_tag, and submit_tag, respectively. Like this:

要创建这个表单你将会分别使用form_tag,label_tag,text_field_tag,and submit_tag

<%= form_tag(“/search”, :method => “get”) do %>

<%= label_tag(:q, “Search for:”) %>

<%= text_field_tag(:q) %>

<%= submit_tag(“Search”) %>

<% end %>

This will generate the following HTML:这将会生成随后的HTML

<form accept-charset=“UTF-8” action=“/search” method=“get”>

<label for=“q”>Search for:</label>

<input id=“q” name=“q” type=“text” />#label inputforid的值的统一是为了将它们关联起来选中labelinput则被激活

<input name=“commit” type=“submit” value=“Search” />

</form>

For every form input, an ID attribute is generated from its name (“q” in the example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.

对于每个form inputID属性被从它的name生成(本例中是q)。这些IDsCSS styling中非常有用或者使用JavaScript操作form controls

Besides text_field_tag and submit_tag, there is a similar helper for every form control in HTML.下面的text_field_tag and submit_tag,它们是每个form controlHTML中的similar helper

Always use “GET” as the method for search forms. This allows users to bookmark a specific search and get back to it. More generally Rails encourages you to use the right HTTP verb for an action.

通常对search forms使用GET”方法。这允许用户将一个指定的搜索(的url添加)书签并(通过这个书签)取回查询结果。更常规的方法Rails鼓励你对于一个HTTP 动作使用正确的verb(动词)

1.2 Multiple Hashes in Form Helper Calls

The form_tag helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element’s class.

form_tag helper接收两个参数:path的值为动作的路径以及一个optionshash字典。

这个hash字典指定提交form的方法和(一些)HTML选项如form元素的class属性。

As with the link_to helper, the path argument doesn’t have to be given a string; it can be a hash of URL parameters recognizable by Rails’ routing mechanism, which will turn the hash into a valid URL. However, since both arguments to form_tag are hashes, you can easily run into a problem if you would like to specify both. For instance, let’s say you write this:

如同link_to helperpath参数不是必须是被提供给一个string;它可以是被Railsrouting mechanism组织的一个URL参数的hash字典,他将转接hash(键)到一个有效的URL。然而,

既然两个参数form_tag都是hash字典形式,你可以容易的陷入一个问题如果你想指定他们两个。下面的实例,让告诉你写下如下代码:

form_tag(:controller => “people”, :action => “search”, :method => “get”, :class => “nifty_form”)

=> ‘<form accept-charset=“UTF-8” action=“/people/search?method=get&class=nifty_form” method=“post”>’

Here, method and class are appended to the query string of the generated URL because you even though you mean to write two hashes, you really only specified one. So you need to tell Ruby which is which by delimiting the first hash (or both) with curly brackets. This will generate the HTML you expect:

这里,methodclass被添加到生成的UIRquery字符串中是因为即使你曾经意图写的两个hash字段,你实际上只指定了一个。因此你需要告诉Ruby其中的那一个被限制为第一个hash(或者两个都)使用大括号。这是生成的HTML你期望的:

form_tag({:controller => “people”, :action => “search”}, :method => “get”, :class => “nifty_form”)

=> ‘<form accept-charset=“UTF-8” action=“/people/search” method=“get”>’

1.3 Helpers for Generating Form Elements

Rails provides a series of helpers for generating form elements such as checkboxes, text fields, and radio buttons. These basic helpers, with names ending in “_tag” (such as text_field_tag and check_box_tag), generate just a single <input> element. The first parameter to these is always the name of the input. When the form is submitted, the name will be passed along with the form data, and will make its way to the params hash in the controller with the value entered by the user for that field. For example, if the form contains <%= text_field_tag(:query) %>, then you would be able to get the value of this field in the controller with params[:query].

Rails提供一系列的helpers来创建form元素例如checkboxestext fields以及radio buttons。这些基本的helpers,使用_tag”结尾(例如text_field_tag and check_box_tag),创建一个单独的<input>元素。其第一个参数通常是inputname。当这个form被提交的时候,名称也将会和form data一起(提交),并且将会以它的方式将它和用户输入到field中的值传递到controller中的params hash中。例如,如果form包含<%= text_field_tag(:query) %>,然后你可以在controller使用params[:query]获取这个field中的值。

When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in params. You can read more about them in chapter7ofthisguide. For details on the precise usage of these helpers, please refer to the APIdocumentation.

当命名了inputsRails使用某些公约使得提交parametersnon-scalar(非标量)的值例如arrays或者hashes成为了可能,其也可以通过使用params来访问。你可以阅读chapter7ofthisguide来了解更多的信息。要知道这些helpers的精确用法的描述,请参阅APIdocumentation

1.3.1 Checkboxes复选框

Checkboxes are form controls that give the user a set of options they can enable or disable:

Checkboxesform控件它提供给用户一个设置选项这个选项可以选中和取消选中:

<%= check_box_tag(:pet_dog) %>

<%= label_tag(:pet_dog, “I own a dog”) %>

<%= check_box_tag(:pet_cat) %>

<%= label_tag(:pet_cat, “I own a cat”) %>

This generates the following:这将生成随后的代码:

<input id=“pet_dog” name=“pet_dog” type=“checkbox” value=“1” />

<label for=“pet_dog”>I own a dog</label>

<input id=“pet_cat” name=“pet_cat” type=“checkbox” value=“1” />

<label for=“pet_cat”>I own a cat</label>

The first parameter to check_box_tag, of course, is the name of the input. The second parameter, naturally, is the value of the input. This value will be included in the form data (and be present in params) when the checkbox is checked.

check_box_tag的第一个参数,当然是inputname。第二个参数,自然地,是input输入的值。这个value将会包含在form data中(并且使用params表示)当checkbox被选中。

1.3.2 Radio Buttons单选按钮

Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (i.e., the user can only pick one):

单选框,它和复选框很类似,它是一种控件指定一些设置选项这些选项之间是相互排斥的(i.e., 用户仅仅只能选择一个):

<%= radio_button_tag(:age, “child”) %>

<%= label_tag(:age_child, “I am younger than 21”) %>

<%= radio_button_tag(:age, “adult”) %>

<%= label_tag(:age_adult, “I’m over 21”) %>

Output:输出:

<input id=“age_child” name=“age” type=“radio” value=“child” />

<label for=“age_child”>I am younger than 21</label>

<input id=“age_adult” name=“age” type=“radio” value=“adult” />

<label for=“age_adult”>I’m over 21</label>

As with check_box_tag, the second parameter to radio_button_tag is the value of the input. Because these two radio buttons share the same name (age) the user will only be able to select one, and params[:age] will contain either “child” or “adult”.

正如check_box_tagradio_button_tag的第二个参数是inputvalue。因为这两个radio buttons分享相同的nameage)用户仅仅能够选择一个,并且params[:age]将会包含或者child或者adult

Always use labels for checkbox and radio buttons. They associate text with a specific option and make it easier for users to click the inputs by expanding扩大the clickable region.

经常把checkboxradio buttonlabels标签一起使用。他们将文字text和一个特定的选项管理起来并且通过扩大可点击区域使得用户能够更加容易点击。

1.4 Other Helpers of Interest其他有趣的Helpers

Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, URL fields and email fields:

其他值得一提的form控件是textareaspasswordfieldshidden fieldssearch fieldstelephone fieldsURL fieldsemail fields

other helpers of interest<br />

<%= label_tag(:message, “content”) %>

<%= text_area_tag(:message, “Hi, nice site”, :size => “24x6”) %><br />

<%= label_tag(:password, “password”) %>

<%= password_field_tag(:password) %><br />

 

<%= hidden_field_tag(:parent_id, “5”) %><br />

<%= label_tag(:user_name, “search:”) %>

<%= search_field(:user, :name) %><br />

<%= label_tag(:user_phone, “phone”) %>

<%= telephone_field(:user, :phone) %><br />

<%= label_tag(:user_homepage, “homepage”) %>

<%= url_field(:user, :homepage) %><br />

<%= label_tag(:user_address, “address”) %>

<%= email_field(:user, :address) %><br />

Output:输出:

<label for=“message”>content</label>

<textarea cols=“24” id=“message” name=“message” rows=“6”>Hi, nice site</textarea><br />

<label for=“password”>password</label>

<input id=“password” name=“password” type=“password” /><br />

 

<input id=“parent_id” name=“parent_id” type=“hidden” value=“5” /><br />

<label for=“user_name”>search:</label>

<input id=“user_name” name=“user[name]” size=“30” type=“search” /><br />

<label for=“user_phone”>phone</label>

<input id=“user_phone” name=“user[phone]” size=“30” type=“tel” /><br />

<label for=“user_homepage”>homepage</label>

<input id=“user_homepage” name=“user[homepage]” size=“30” type=“url” /><br />

<label for=“user_address”>address</label>

<input id=“user_address” name=“user[address]” size=“30” type=“email” /><br /

Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript.

Hidden inputs不显示给用户但是它可以hold任何文本形式数据的输入。Hidden里面的值可以用JavaScript改变。

The search, telephone, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely noshortageofsolutionsforthis, although a couple of popular tools at the moment are Modernizr and yepnope, which provide a simple way to add functionality based on the presence of detected HTML5 features.

search, telephone, URL, email inputsHTML5控件。如果你需要你的app在旧的浏览器上面拥有兼容的体验,你将需要一个HTML5 polyfillprovided by CSS and/or JavaScript)。这里肯定noshortageofsolutionsforthis(没有针对此类问题一致的解决方法),即使现在有了一组广受欢迎的工具是Modernizr and yepnope,他们提供了一个简单的方法来添加检测HTML5特性功能是否存在。

 

If you’re using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the SecurityGuide.

如果你正在使用password input fields(无论何种原因),你可能想配置你的应用程序以防止记录这些参数。你可以从SecurityGuide中了解这些信息。

2 Dealing with Model Objects

2.1 Model Object Helpers

A particularly common task for a form is editing or creating a model object. While the *tag helpers can certainly be used for this task they are somewhat verbose as for each tag you would have to ensure the correct parameter name is used and set the default value of the input appropriately. Rails provides helpers tailored to this task. These helpers lack the tag suffix, for example text_field, text_area.

form的一个格外普遍的任务是编辑或创建一个model对象。然而,无疑*tag helpers能够在这个任务中使用,他们有点繁琐,为每一个标签,你就必须确保使用中的参数名称的正确使用,并设置适当的输入的默认值。Rails为这个任务量身定制了helpers。这些helpers缺少tag 后缀,例如text_field,text_area.

For these helpers the first argument is the name of an instance variable and the second is the name of a method (usually an attribute) to call on that object. Rails will set the value of the input control to the return value of that method for the object and set an appropriate input name. If your controller has defined @person and that person’s name is Henry then a form containing:

对于这些helpers第一个参数是实例变量的name,第二个是方法的名称(通常是一个属性)来调用那个对象。Rails将(传输)input 控件的值给该对象的方法的返回值,并设置一个适当的输入名称。如果你的控件name已经被定义为@person并且persons name is Henry那么这个form包含:

<%= text_field(:person, :name) %>

will produce output similar to

<input id=“person_name” name=“person[name]” type=“text” value=“Henry”/>

Upon form submission the value entered by the user will be stored in params[:person][:name]. The params[:person] hash is suitable for passing to Person.new or, if @person is an instance of Person, @person.update_attributes. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a name and a name= method Rails will be happy.

在用户输入后,并由form提交的值将会被存储在params[:person][:name]中。params[:person]相当于Person.new,如果@person是一个Person的实例,则相当于@person.update_attributes。虽然这些helpers的属性名称最常见的是第二个参数,但这不是强制性的。在例子中,只要Person对象有一个名称和一个name=method那么Rails会很乐意。

You must pass the name of an instance variable, i.e. :person or “person”, not an actual instance of your model object.你必须提供一个实例变量的名称,i.e. :person or “person”,不是一个你model对象中实际的实例。

Rails provides helpers for displaying the validation errors associated with a model object. These are covered in detail by the ActiveRecordValidationsandCallbacks guide.

2.2 Binding a Form to an ObjectformObject绑定

While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what form_for does.

虽然添加的这些功能让我们(在使用中)感到舒适但离完美远远不够。如果Person拥有多个属性需要编辑然后我们将会重复被编辑objectname很多次。我们想要(Rails)做的是以某种方式将model objectform绑定,这也恰好是form_for要做的。

Assume we have a controller for dealing with articles app/controllers/articles_controller.rb:

假设我们有一个controller(app/controllers/articles_controller.rb)来处理articles:

def new

@article = Article.new

end

The corresponding view app/views/articles/new.html.erb using form_for looks like this:

<%= form_for @article, :url => { :action => “create” }, :html => {:class => “nifty_form”} do |f| %>

<%= f.text_field :title %>

<%= f.text_area :body, :size => “60x12” %>

<%= f.submit “Create” %>

<% end %>

There are a few things to note here:这里对其做的事情备注如下:

  1. @article is the actual object being edited. @article是实际上被编辑的object
  2. There is a single hash of options. Routing options are passed in the :url hash, HTML options are passed in the :html hash. 这里是一个单个hash字典的选项。Routing选项通过:url hash键对提供,HTML选项通过:html hash键对提供
  3. The form_for method yields a form builder object (the f variable).
  4. Methods to create form controls are called on the form builder object f创建form控件的方法是通过调用object fform builder

The resulting HTML is:

<form accept-charset=“UTF-8” action=“/articles/create” method=“post” class=“nifty_form”>

<input id=“article_title” name=“article[title]” size=“30” type=“text” />

<textarea id=“article_body” name=“article[body]” cols=“60” rows=“12”></textarea>

<input name=“commit” type=“submit” value=“Create” />

</form>

The name passed to form_for controls the key used in params to access the form’s values. Here the name is article and so all the inputs have names of the form article[attribute_name]. Accordingly, in the create action params[:article] will be a hash with keys :title and :body. You can read more about the significance of input names in the parameter_names section.

通过form_for控件的name以及parmas使用的关键字来接收form的值。这里的namearticle并且form所有的inputsname都是article[attribute_name]。因此,在create actionparams[:article]将会有关键字:title and :bodyhash键值。你可以阅读更多的input name的含义在parameter_names章节。

The helper methods called on the form builder are identical相同一致to the model object helpers except that it is not necessary to specify which object is being edited since this is already managed by the form builder.

这个调用form builderhelper方法除了不需要指定那个object被编辑既然这已经交给form builder管理,其他部分与model object helpers一致。

You can create a similar binding without actually creating <form> tags with the fields_for helper. This is useful for editing additional model objects with the same form. For example if you had a Person model with an associated ContactDetail model you could create a form for creating both like so:

你可以创建一个类似的tags组合却没有真正的使用fields_for helper创建<form> tags。这在使用相同的form编辑额外的对象的时候非常有用。例如如果你有一个Person model以及相关联的ContactDetail model你可以创建一个form来创建他们两个如下:

<%= form_for @person, :url => { :action => “create” } do |person_form| %>

<%= person_form.text_field :name %>

<%= fields_for @person.contact_detail do |contact_details_form| %>

<%= contact_details_form.text_field :phone_number %>

<% end %>

<% end %>

which produces the following output:

<form accept-charset=“UTF-8” action=“/people/create” id=“new_person” method=“post”>

<input id=“person_name” name=“person[name]” size=“30” type=“text” />

<input id=“contact_detail_phone_number” name=“contact_detail[phone_number]” size=“30” type=“text” />

</form>

The object yielded by fields_for is a form builder like the one yielded by form_for (in fact form_for calls fields_for internally(在内部调用fields_for).

2.3 Relying on Record Identification依托于记录的识别

The Article model is directly available to users of the application, so — following the best practices for developing with Rails — you should declare it a resource:

Article模型是应用程序用户直接可用的,因此——遵循Rails开发最好的实践——你应该声明它为一个resource:

resources :articles

Declaring a resource has a number of side-effects(官网上是side-affects错别字?). See RailsRoutingFromtheOutsideIn for more information on setting up and using resources.

声明一个resource会有一系列的副作用。查看RailsRoutingFromtheOutsideIn来获取在设定和使用resource的更多信息。

When dealing with RESTful resources, calls to form_for can get significantly easier if you rely on record identification. In short, you can just pass the model instance and have Rails figure out model name and the rest:

当在处理RESTful resources的时候,调用form_for会明显简单的多如果你rely on record identification。简而言之,你可以仅仅通过model实例和Railsfigure out(谋取)model name或者重设它:

## Creating a new article

# long-style:

form_for(@article, :url => articles_path)

# same thing, short-style (record identification gets used):

form_for(@article)

 

## Editing an existing article

# long-style:

form_for(@article, :url => article_path(@article), :html => { :method => “put” })

# short-style:

form_for(@article)

Notice how the short-style form_for invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking record.new_record?. It also selects the correct path to submit to and the name based on the class of the object.

注意short-styleform_for的调用相对如此的方便,无论这个记录是刚开始还是已经存在了。Record identification能够智能识别如果记录是否是new通过询问record.new_record?。它也选择正确的提交路径并且它的name基于objectclass

Rails will also automatically set the class and id of the form appropriately: a form creating an article would have id and class new_article. If you were editing the article with id 23, the class would be set to edit_article and the id to edit_article_23. These attributes will be omitted省略for brevity in the rest of this guide.

Rails也会适当的自动的设置form(元素)的classid:一个(用于)创建一个articlefom将会拥有idclassnew_article。如果你在正编辑id23article,它的class属性将会被设置为edit_article并且idedit_article_23。为了教程的简洁,这些属性将会被部分忽略。

When you’re using STI (single-table inheritance) with your models, you can’t rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, :url, and :method explicitly.

当你正在你的model中使用STI(single-table inheritance),你不能在一个子类中使用 rely on record identification除非他的父类被声明为一个resource。(那么)你将不得不准确的指定model name,:url,:method。

2.3.1 Dealing with Namespaces使用名称空间处理(业务)

If you have created namespaced routes, form_for has a nifty shorthand for that too. If your application has an admin namespace then

如果你已经创建一个namespaced routesform_for拥有一个可爱的快捷操作来做这些。如果你的应用程序拥有一个admin namespace然后

form_for [:admin, @article]

will create a form that submits to the articles controller inside the admin namespace (submitting to admin_article_path(@article) in the case of an update). If you have several levels of namespacing then the syntax is similar:

将会创建一个form它会提交(信息)到在admin namespace(比如在update情况下会提交到admin_article_path(@article))中的articles controller。如果你有一系列等级的name spacing然后将会有语法智能(处理他们):

form_for [:admin, :management, @article]

For more information on Railsrouting system and the associated conventions, please see the routingguide.

得到更多的关于Railsrouting和关联约定,请阅读routingguide

2.4 How do forms with PUT or DELETE methods work?表单的PUT或者DELETE方法是如何工作的

The Rails framework encourages RESTful design of your applications, which means you’ll be making a lot of “PUT” and “DELETE” requests (besides “GET” and “POST”). However, most browsers dont support methods other than “GET” and “POST” when it comes to submitting forms.

Rails框架鼓励在你的应用程序中使用RESTful风格设计,意思是你将会产生许多PUT”DELETE”请求(在GET”AND“POST”之后)。然而,大多数的浏览器在提交表单的时候并不支持超过GET”AND”POST”之外的方法。

Rails works around this issue by emulating模拟other methods over POST with a hidden input named “_method”, which is set to reflect the desired method:

Rails围绕这个问题通过在POST之外添加一个隐藏的(命名为“_method”input标签来模拟其他的方法,这个input标签被用来呈现所需的方法:

form_tag(search_path, :method => “put”)

output:

<form accept-charset=“UTF-8” action=“/search” method=“post”>

<div style=“margin:0;padding:0”>

<input name=“_method” type=“hidden” value=“put” />

<input name=“utf8” type=“hidden” value=“&#x2713;” />

<input name=“authenticity_token” type=“hidden” value=“f755bb0ed134b76c432144748a6d4b7a7ddf2b71” />

</div>

When parsing POSTed data, Rails will take into account the special _method parameter and acts as if the HTTP method was the one specified inside it (“PUT” in this example).

当解析POSTed数据的时候,Rails将会出进入指定的_method参数并且如果有一个HTTP方法在里面被指定了(本例中是PUT)。

3 Making Select Boxes with Ease使用容易(的方式)来生成Select Boxes

Select boxes in HTML require a significant(明显)amount of markup (one OPTION element for each option to choose from), therefore it makes the most sense for them to be dynamically generated.

Select boxesHTML需要标明markup的数量(一个OPTION元素对应需要选择的每一个选项),因此对于他们的动态生成这是有意义的。

Here is what the markup might look like:这里是markup可能的样子:

<select name=“city_id” id=“city_id”>

<option value=“1”>Lisbon</option>

<option value=“2”>Madrid</option>

<option value=“12”>Berlin</option>

</select>

Here you have a list of cities whose names are presented to the user. Internally the application only wants to handle their IDs so they are used as the options’ value attribute. Let’s see how Rails can help out here.

这里你有一个城市的列表这个列表的名字被呈现给用户。在应用程序的内部只希望处理它们的IDs因此它们通过option‘svalue属性来使用。让我们看看Rails是如何帮助输出这里的代码的。

3.1 The Select and Option Tags SelectOption标签

The most generic helper is select_tag, which — as the name implies — simply generates the SELECT tag that encapsulates an options string:

最常见的生成器helperselect_tag,它——就像名字的意思——简单的生成SELECT标签在其中封装一个选项字符串:

<%= select_tag(:city_id, ‘<option value=“1”>Lisbon</option>…’) %>

This is a start, but it doesn’t dynamically create the option tags. You can generate option tags with the options_for_select helper:

这是开始,但是它不会动态创建option标签。你可以在options_for_select helper创建option标签:

<%= options_for_select([[‘Lisbon’, 1], [‘Madrid’, 2], …]) %>

 

output:

 

<option value=“1”>Lisbon</option>

<option value=“2”>Madrid</option>

The first argument to options_for_select is a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will be submitted to your controller. Often this will be the id of a corresponding database object but this does not have to be the case.

options_for_select的第一个参数是一个嵌套数组它的的每一个元素有两个元素:option textcity name)和option valuecity id)。这里的option value将会被提交到你的controller中。通常这里会有id和其相应的数据库对象但是并不完全是这样。

Knowing this, you can combine select_tag and options_for_select to achieve the desired, complete markup:

知道这些,你可结合select_tagoptions_for_select到达预期的(目标),完成markup

<%= select_tag(:city_id, options_for_select(…)) %>

options_for_select allows you to pre-select an option by passing its value.

options_for_select允许你预选择一个选项通过它的值。

<%= options_for_select([[‘Lisbon’, 1], [‘Madrid’, 2], …], 2) %>

 

output:

 

<option value=“1”>Lisbon</option>

<option value=“2” selected=“selected”>Madrid</option>

bellow is my code in templates

<!—it not work—>

 

<%= select_tag(:city_id, ‘<option value=“1”>Lisbon</option><option value=“2”>Madrid</option>’) %>

 

<%= options_for_select([[‘Lisbon’, 1], [‘Madrid’, 2] ], 2)%>

 

 

 

combine select_tag and options_for_select combine select_tag and options_for_select:<br />

 

<%= select_tag(:city_id, options_for_select([[‘Lisbon’, 1], [‘Madrid’, 2] ], 2)) %>

 

Whenever Rails sees that the internal value of an option being generated matches this value, it will add the selected attribute to that option.

无论何时Rails发现开始创建的option tag的值匹配option内部的值(设定的默认选中的值),

,将会添加selected属性到这个选项。

The second argument to options_for_select must be exactly equal to the desired internal value. In particular if the value is the integer 2 you cannot pass “2” to options_for_select — you must pass 2. Be aware of values extracted from the params hash as they are all strings.

options_for_select的第二个参数必须准确等于预期的内部的值。特别是如果这个值是整数2你不能传送2options_for_select——你必须传送2。在这个值来自params hash的时候你必须保持清醒因为它们全都是strings

3.2 Select Boxes for Dealing with Models使用Select Boxes来处理Models#外键的select boxes

In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the _tag suffix from select_tag:

在大多数情况中form controls将会被捆绑到一个指定的数据库model并且正如你可能期望Rails为捆绑的这个models提供量身定制的helpers。与其他form helpers一致的,当在处理models的时候你从select_tag 抛弃_tag后缀。

controller:

@person = Person.new(:city_id => 2)

view:

<%= select(:person, :city_id, [[‘Lisbon’, 1], [‘Madrid’, 2], …]) %>

Notice that the third parameter, the options array, is the same kind of argument you pass to options_for_select. One advantage here is that you don’t have to worry about pre-selecting the correct city if the user already has one — Rails will do this for you by reading from the @person.city_id attribute.

注意上面的第三个参数,这个选项数组,是相同的类型的参数你提供给options_for_select(注意其中的省略号,它是省略了一些选项没有写出但是实际中不能这样写哦有就全部给出)。这里的一个优势就是你不用必须担心预选择正确的城市如果用户已经有一个——Rails将会通过读取@person.city_id属性替你这些事情。

As with other helpers, if you were to use the select helper on a form builder scoped to the @person object, the syntax would be:

如同其他helpers,如果你打算在一个作用域为@person对象的form builder使用select helper,语句将会是:

select on a form builder

<%= f.select(:city_id, …) %>

If you are using select (or similar helpers such as collection_select, select_tag) to set a belongs_to association you must pass the name of the foreignkey (in the example above city_id), not the name of association itself. If you specify city instead of city_id Active Record will raise an error along the lines of ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750) when you pass the params hash to Person.new or update_attributes. Another way of looking at this is that form helpers only edit attributes. You should also be aware of the potential潜在security ramifications后果of allowing users to edit foreign keys directly. You may wish to consider the use of attr_protected and attr_accessible. For further details on this, see the RubyOnRailsSecurityGuide.

you must pass the name of the foreignkey (in the example above city_id), not the name of association itself. 你可以考虑使用attr_protected and attr_accessible.

3.3 Option Tags from a Collection of Arbitrary Objects 任意集合对象的Option 标签

Generating options tags with options_for_select requires that you create an array containing the text and value for each option. But what if you had a City model (perhaps an Active Record one) and you wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:

通常情况下options_for_selectoption标签需要你创建一个包含text和每个选项的的值的数组。但是如果你有一个City model(可能是Active Record中的一个)同时你想生成这些对象集合的选项?一个解决办法是产生一个迭代所有对象的嵌套数组:

<% cities_array = City.all.map { |city| [city.name, city.id] } %>

<%= options_for_select(cities_array) %>

This is a perfectly Pairs passed to options_for_select should have the name first and the id second, however with options_from_collection_for_select the first argument is the value method and the second the text method.valid solution, but Rails provides a less verbose alternative: options_from_collection_for_select. This helper expects a collection of arbitrary objects and two additional arguments: the names of the methods to read the option value and text from, respectively分别:

这是一个完美有效的解决方法,但是Rail提供一个更少累赘替代上面的方法:options_from_collection_for_select. 这个helper预订一个任意对象的集合和两个额外的参数:方法的name来分别读取optionvaluetext

<%= options_from_collection_for_select(City.all, :id, :name) %>

As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with select_tag, just as you would with options_for_select. When working with model objects, just as select combines select_tag and options_for_select, collection_select combines select_tag with options_from_collection_for_select.

正如其名,它仅仅生成option tags。要生成一个能够工作的select box你还需将其关联到select_tag,就像你处理options_for_select一样的。当在model对象中工作的时候,就像select联合select_tagoptions_for_select的情行,collection_select联合select_tagoptions_from_collection_for_select.#这里好像说的是外键的select boxes

<%= collection_select(:person, :city_id, City.all, :id, :name) %>

To recap, options_from_collection_for_select is to collection_select what options_for_select is to select.

概括一下,options_from_collection_for_select对于collection_select正如options_for_select对于select

Pairs passed to options_for_select should have the name first and the id second, however with options_from_collection_for_select the first argument is the value method and the second the text method.

3.4 Time Zone and Country Select时区和国家选择

To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using collection_select, but you can simply use the time_zone_select helper that already wraps this:

Rails利用时区支持,你必须询问你的用户他们处在什么时区。要这样做将在使用collection_select的时候require来自预先定义列表中的一个TimeZome 对象的创建select选项,但是你可以简单的使用time_zone_select helper它已经包含了这些:

<%= time_zone_select(:person, :time_zone) %>

There is also time_zone_options_for_select helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.

这里同样也有also time_zone_options_for_select helper来手动的做这些(因此更具定制可能)。阅读API文档来了解这两个方法可能的参数。

Rails used to have a country_select helper for choosing countries, but this has been extracted to the country_selectplugin. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).

Rails通常使用一个country_select来选择国家,但是这是从country_selectplugin提取的。当使用这些,要知道来自list中排除或者列入的一些names可能有些争议(并且这也是这个功能从Rails中提取出来的原因)。

4 Using Date and Time Form Helpers

The date and time helpers differ from all the other form helpers in two important respects:

date and time helpers与其他所有的form helpers都不相同(着重)表现在两个重要的方面:

  1. Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your params hash with your date or time.

Dates and times不仅仅表现为单个input element。对于每个组件中的一个(year,month,day ect)取而代之你都有一些(element)。因此在你的params hash字典中没有你的date or time的单个值。

  1. Other helpers use the _tag suffix to indicate说明whether a helper is a barebones helper or one that operates on model objects. With dates and times, select_date, select_time and select_datetime are the barebones helpers, date_select, time_select and datetime_select are the equivalent model object helpers. #交换了一下顺序

其他helpers使用_tag后缀来说明一个helper是单纯的helper还是一个model对象的那样的操作。

Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc.).

这些helper的组员将会创建一系列的对应于不同的组件的select boxesyear, month, day etc.)。

4.1 Barebones Helpers

The select_* family of helpers take as their first argument an instance of Date, Time or DateTime that is used as the currently selected value. You may omit this parameter, in which case the current date is used. For example

譬如select_* helpers成员获取Date实例作为它们的第一个变量,Time or DateTime使用的是当前选择的值。你可能忽略这个参数,在这种情况中current date会被使用。例如

<%= select_date Date.today, :prefix => :start_date %>

outputs (with actual option values omitted for brevity)为了简洁省略了实际的选项值#省略号应该是相应的实际的日期和时间

<select id=“start_date_year” name=“start_date[year]”> … </select>

<select id=“start_date_month” name=“start_date[month]”> … </select>

<select id=“start_date_day” name=“start_date[day]”> … </select>

The above inputs would result in params[:start_date] being a hash with keys :year, :month, :day. To get an actual Time or Date object you would have to extract these values and pass them to the appropriate constructor, for example

完整的输入结果将会在params[:start_date]他会是一个关键字是:year, :month, :dayhash字典。要得到一个实际的Time or Date对象你将不得不提取这些值并且转换他们为适合的结构,例如:

Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)

The :prefix option is the key used to retrieve the hash of date components from the params hash. Here it was set to start_date, if omitted it will default to date.

:prefix选项是用来接收来自params hashhash形式的date组件的关键。这里它被设置为start_date,如果省略形式的话默认是date

4.2 Model Object Helpers

select_date does not work well with forms that update or create Active Record objects as Active Record expects预计each element of the params hash to correspond to one attribute. The model object helpers for dates and times submit parameters with special names, when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example:

select_date(这个是单纯的helper)在update or create Active Record对象的时候预置每个params hash字典的element对应到一个属性不会很好的工作。Dates and times model object helpers提交参数通过指定的names,当Active Record发现这些参数时,它知道它们必须结合其他参数以及每一列(属性year, month, day etc.)被给予一个合适的结构。例如:

<%= date_select :person, :birth_date %>

outputs (with actual option values omitted for brevity)将实际的optionvalues忽略以精简

<select id=“person_birth_date_1i” name=“person[birth_date(1i)]”> … </select>

<select id=“person_birth_date_2i” name=“person[birth_date(2i)]”> … </select>

<select id=“person_birth_date_3i” name=“person[birth_date(3i)]”> … </select>

which results in a params hash like

{:person => {‘birth_date(1i)’ => ‘2008’, ‘birth_date(2i)’ => ‘11’, ‘birth_date(3i)’ => ‘22’}}

When this is passed to Person.new (or update_attributes), Active Record spots that these parameters should all be used to construct the birth_date attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as Date.civil.

当这些信息提交给Person.newupdate_attributes),Active Record指出所有的这些参数应该使用于birth_date attribute 结构并且使用后缀声明使得它能够传递这些参数给功能(方法)例如Date.civil

4.3 Common Options

Both families of helpers use the same core set of functions to generate the individual select tags and so both accept largely the same options. In particular, by default Rails will generate year options 5 years either side of the current year. If this is not an appropriate range, the :start_year and :end_year options override this. For an exhaustive list of the available options, refer to the APIdocumentation.

helpers的所有成员都使用相同的core set of functions来构建个体的select 标签因此同样能够接收大量相同的options。以其一为例,默认情况下year optionsRails将会创建在当前年份的上和下5年。如果这不是一个合适的范围,:start_year and :end_year选项将会覆盖默认的范围。要得到可用选项的全面的列表,参考APIdocumentation

As a rule of thumb you should be using date_select when working with model objects and select_date in other cases, such as a search form which filters results by date.

一个规则指引你在处理model objects的时候使用date_select在其他情况的时候使用select_date,比如一个search form它过滤日期作获取结果。

In many cases the built-in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.

在许多情况下,内置的日期选择器是笨拙的,因为他们对计算出日期和工作日关系的用户没有援助。

4.4 Individual Components个体组件

Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component select_year, select_month, select_day, select_hour, select_minute, select_second. These helpers are fairly straightforward. By default they will generate an input field named after the time component (for example “year” for select_year, “month” for select_month etc.) although this can be overridden with the :field_name option. The :prefix option works in the same way that it does for select_date and select_time and has the same default value.

偶然情况下你需要仅仅显示单个日期组件如year or monthRails提供一些helpers来做这些,给每一个组件select_year, select_month, select_day, select_hour, select_minute, select_second。这些helpers相当的简单。默认情况下他们将会创建一个input field并以时间组件命名(例如year” for select_year, “month” for select_month etc.)即使这会覆盖:field_name选项。:prefix以一样的方式工作,对于select_date and select_time会设置同样的默认值。

<%= select_year(2011) %>

<%= select_year(Time.now) %>

will produce the same output if the current year is 2011 and the value chosen by the user can be retrieved by params[:date][:year].

5 Uploading Files

A common task is uploading some sort of file, whether it’s a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the rendered form’s encoding MUST be set to “multipart/form-data”. If you use form_for, this is done automatically. If you use form_tag, you must set it yourself, as per the following example.

The following two forms both upload a file.

一个常用的任务是上传一些种类的文件,无论在上传进程中的是一个个人图片还是一个包含数据CSV文件。文件上传中重点要注意的事情是rendered 表单的编码必须被设置成multipart/form-data”. 如果你使用的form_for,这将被自动完成。如果你使用form_tag,你必须自己设置它,在随后的每个例子中。后面的两个表单都是上传一个文件。

 

<%= form_tag({:action => :upload}, :multipart => true) do %>

<%= file_field_tag ‘picture’ %>

<% end %>

 

<%= form_for @person do |f| %>

<%= f.file_field :picture %>

<% end %>

Since Rails 3.1, forms rendered using form_for have their encoding set to multipart/form-data automatically once a file_field is used inside the block. Previous versions required you to set this explicitly.

Rails3.1,form使用form_forrender一旦在block中有一个file_field被使用编码自动设置为multipart/form-data。以前的版本需要你准确的设置为这样的编码。

Rails provides the usual pair of helpers: the barebones file_field_tag and the model oriented file_field. The only difference with other helpers is that you cannot set a default value for file inputs as this would have no meaning. As you would expect in the first case the uploaded file is in params[:picture] and in the second case in params[:person][:picture].

R爱丽丝提供通常配对的helpers:单个的file_field_tag和面向modelfile_field

这与其他的helpers的不同是你不能为file inputs设置一个不同的值因为这样没有意义。正如你所期望的第一种情况上传的文件在params[:picture],第二种情况在params[:person][:picture]中。

5.1 What Gets Uploaded

The object in the params hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an original_filename attribute containing the name the file had on the user’s computer and a content_type attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in #{Rails.root}/public/uploads under the same name as the original file (assuming the form was the one in the previous example).

params hash中的对象是IO子类的一个实例。取决于上传文件的大小它可能实际上是一个StringIO或者临时文件的备份文件。在这两个情况中对象将会有一个original_filename属性包含文件在用户电脑上的名字以及一个content_type属性包含上传文件的MIME形式。随后的阶段保存上传的内容在#{Rails.root}/public/uploads并以与原始名字相同(假设form是一个预先的例子则下面是其控制函数)

def upload

uploaded_io = params[:person][:picture]

File.open(Rails.root.join(‘public’, ‘uploads’, uploaded_io.original_filename), ‘w’) do |file|

file.write(uploaded_io.read)

end

end

Once a file has been uploaded, there are a multitude of potential潜在tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several libraries designed to assist with these. Two of the better known ones are CarrierWave and Paperclip.

一旦一个文件被上传,这里有多个潜在的任务,响应在哪里存放文件(on disk, Amazon S3, etc) 并且关联它们至models为重设大小的图片文件同时创建缩略图。这里的复杂远超这个guide的范围,但是这里有一些库被设计来分派这些。有两个较好的库ones are CarrierWave and Paperclip

Iftheuserhasnotselectedafilethecorrespondingparameterwillbeanemptystring.

5.2 Dealing with Ajax处理Ajax

Unlike other forms making an asynchronous file upload form is not as simple as providing form_for with :remote => true. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.

与其他forms不同制作一个异步文件上传表单不能简单的提供form_for with :remote => true.一个Ajax表单这个序列被在浏览器内部运行的JavaScript完成同时因为JavaScript不能从你的硬盘中读取文件这个文件就不能上传。最常见的解决方法是使用一种无形的iframe服务于目标forms的提交。

6 Customizing Form Builders

As mentioned previously the object yielded by form_for and fields_for is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way you can also subclass FormBuilder and add the helpers there. For example

正如开始提到的object yielded by form_for以及是FormBuilder的实例(或者其中的子类的实例)。Form builders封装表单元素的显示notion为一个单独的对象。然而你当然你也可以为你的form编写helpers通常的方式你可以通过(编写)FormBuilder的子类并且在这里添加helpers。例如:

<%= form_for @person do |f| %>

<%= text_field_with_label f, :first_name %>

<% end %>

可以替换为:

<%= form_for @person, :builder => LabellingFormBuilder do |f| %>

<%= f.text_field :first_name %>

<% end %>

by defining a LabellingFormBuilder class similar to the following:

通过定义一个LabellingFormBuilder类与接下来的类似:

class LabellingFormBuilder < ActionView::Helpers::FormBuilder

def text_field(attribute, options={})

label(attribute) + super

end

end

If you reuse this frequently you could define a labeled_form_for helper that automatically applies the :builder => LabellingFormBuilder option.

如果你拒绝这样频繁的操作你可以定义一个labeled_form_for它会自动的应用:builder => LabellingFormBuilder选项。

The form builder used also determines what happens when you do

form builer的使用也决定了在你操作的时候将会发生什么

<%= render :partial => f %>

If f is an instance of FormBuilder then this will render the form partial, setting the partial’s object to the form builder. If the form builder is of class LabellingFormBuilder then the labelling_form partial would be rendered instead.

如果fFormBuilder的一个实例那么这里将会局部的render表单,为form buider设置partial’s对象。如果form builderLabellingFormBuilder那么在renderlabelling_form将会替代f

7 Understanding Parameter Naming Conventions明白参数公约

As you’ve seen in the previous sections, values from forms can be at the top level of the params hash or nested in another hash. For example in a standard create action for a Person model, params[:model] would usually be a hash of all the attributes for the person to create. The params hash can also contain arrays, arrays of hashes and so on.

正如你在(本节guide)前面部分看到的,来至forms的值将会在顶级的parmas hash字典内或者是嵌套在另一个hash子典中。例如在Person model的一个标准的create action中,params[:model]通常将会有一个含有创建的person的所有属性的字典。params字典也可以包含数组,由hash字典组成的数组等等。

Fundamentally HTML forms don’t know about any sort of structured data, all they generate is name–value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.

归根结底HTML forms并不知道关于数据结构的任何类型,它们生成的所有仅是name-value对,这些(name-value)仅仅是纯string。你在应用程序中看到的数组和hash字典是一些在Rails使用的参数命名公约的结果。

You may find you can try out examples in this section faster by using the console to directly invoke Rails’ parameter parser. For example,

你会发现你可以快速的尝试这部分的例子通过使用console来直接的调用Rails’s parameter 解析器。例如,

ActionController::UrlEncodedPairParser.parse_query_parameters “name=fred&phone=0123456789”

=> {“name”=>“fred”, “phone”=>“0123456789”}

7.1 Basic Structures基础的strcutures

The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in params. For example if a form contains

两个根本的结构是arrayshash字典。Hash字典镜像这种语法用来访问params的值。例如如果一个form中包含

<input id=“person_name” name=“person[name]” type=“text” value=“Henry”/>

the params hash will contain

{‘person’ => {‘name’ => ‘Henry’}}

and params[:person][:name] will retrieve the submitted value in the controller.

同时params[:person][:name]将会在controller中接收提交的value

Hashes can be nested as many levels as required, for example

如果需要hash字典可以嵌套多层,例如

<input id=“person_address_city” name=“person[address][city]” type=“text” value=“New York”/>

will result in the params hash being将会在params hash字典中返回

{‘person’ => {‘address’ => {‘city’ => ‘New York’}}}

Normally Rails ignores duplicate parameter names. If the parameter name contains an empty set of square brackets [] then they will be accumulated in an array. If you wanted people to be able to input multiple phone numbers, you could place this in the form:

正常情况下Rails忽略重复参数名称。如果参数名包含一个空的[]那么它们会在一个数组中累积。如果你想people能够输入多个电话号码,你可以将这里放入form中:

<input name=“person[phone_number][]” type=“text”/>

<input name=“person[phone_number][]” type=“text”/>

<input name=“person[phone_number][]” type=“text”/>

This would result in params[:person][:phone_number] being an array.

7.2 Combining Them联合它们

We can mix and match these two concepts. For example, one element of a hash might be an array as in the previous example, or you can have an array of hashes. For example a form might let you create any number of addresses by repeating the following form fragment

我们可以总结出两个概念。例如,一个elementhash键值可能是作为一个先前例子的hash键值的数组,或者你也可以有一个hash字典的数组。例如一个form可以让你创建任何数量的地址通过重复随后的form 片段

<input name=“addresses[][line1]” type=“text”/>

<input name=“addresses[][line2]” type=“text”/>

<input name=“addresses[][city]” type=“text”/>

This would result in params[:addresses] being an array of hashes with keys line1, line2 and city. Rails decides to start accumulating values in a new hash whenever it encounters an input name that already exists in the current hash.

这里返回的结果在params[:addresses]中,是一个hash字典组成的数组其中keysline1,line2cityRails决定开始在一个新的hash中积累值不管它是不是遇到一个已经在当前的hash已经存在的input name

There’s a restriction, however, while hashes can be nested arbitrarily, only one level of “arrayness” is allowed. Arrays can be usually replaced by hashes, for example instead of having an array of model objects one can have a hash of model objects keyed by their id, an array index or some other parameter.

这是一个限制,然而,即使hashes能够嵌套任意层,只有一级的arrayness”是被允许的。数组通常可以替换hashes字典,例如作为替代,一个数组的model对象可以键入它们的id,一个array index或者一些其他参数。

Array parameters do not play well with the check_box helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The check_box helper fakes this by creating an auxiliary hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new array element. It is preferable to either use check_box_tag or to use hashes instead of arrays.

数组参数在check_box helper中不能得到很好的效果。根据HTML规范的unchecked checkboxes提交空值。无论如何一个checkbox提交一个值通常非常方便。check_box helper 假想这种情况来创建具有相同name一个辅助的隐藏input。如果checkboxunchecked那么只有hidden input被提交如果它是check那么两个都会提交但是checkbox优先提交value.当(Rails)工作到有这两个重复提交的参数数组rails将会产生混淆,因为重复 input name会开始一个新的数组元素。这里使用 check_box_tag或者使用hash替代array都是可取的。

7.3 Using Form Helpers

The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as text_field_tag Rails also provides higher level support. The two tools at your disposal here are the name parameter to form_for and fields_for and the :index option that helpers take.

前面的部分根本没有使用Rails form helpers。然而你可以自己加工input name并且直接传输他们给helpers例如 text_field_tag Rails也提供更高级别的支持。有两个工具供你差遣它们是获取:nameform_for和获取:index选项的fields_for helpers

You might want to render a form with a set of edit fields for each of a person’s addresses. For example:

你可能想render一个person’s addresses form并且对person’s each addresses使用一样的edit fields

<%= form_for @person do |person_form| %>

<%= person_form.text_field :name %>

<% @person.addresses.each do |address| %>

<%= person_form.fields_for address, :index => address do |address_form|%>

<%= address_form.text_field :city %>

<% end %>

<% end %>

<% end %>

Assuming the person had two addresses, with ids 23 and 45 this would create output similar to this:

假设person有两个addressid2345将会创建与下面类似的HTML代码:

<form accept-charset=“UTF-8” action=“/people/1” id=“edit_person_1” method=“post”>

<input id=“person_name” name=“person[name]” size=“30” type=“text” />

<input id=“person_address_23_city” name=“person[address][23][city]” size=“30” type=“text” />

<input id=“person_address_45_city” name=“person[address][45][city]” size=“30” type=“text” />

</form>

This will result in a params hash that looks like

{‘person’ => {‘name’ => ‘Bob’, ‘address’ => {‘23’ => {‘city’ => ‘Paris’}, ‘45’ => {‘city’ => ‘London’}}}}

 

Rails knows that all these inputs should be part of the person hash because you called fields_for on the first form builder. By specifying an :index option you’re telling Rails that instead of naming the inputs person[address][city] it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call to_param on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even nil (which will result in an array parameter being created).

Rails知道这里所有的inputs应该是person hash的一部分因为你在form builder的开始调用了 fields_for。通过指定:index选项你告诉Rails替代person[address][city] input中的名字它将会在address和此帖有之间插入被[]包围的index。如果你通过的是一个Active Record object正如我们知道的Rails将会调用_param,它会默认返回数据库的(city的)id。这在当Addrss记录需要修改,的时候非常有用它能易于定位。你可以传递给其他意义的数字,字符串或者空(它将返回在创建的参数数组中)。

To create more intricate复杂 nestings, you can specify the first part of the input name (person[address] in the previous example) explicitly, for example

<%= fields_for ‘person[address][primary]’, address, :index => address do |address_form| %>

<%= address_form.text_field :city %>

<% end %>

will create inputs like

As a general rule the final input name is the concatenation of the name given to fields_for/form_for, the index value and the name of the attribute. You can also pass an :index option directly to helpers such as text_field, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.

As a shortcut you can append [] to the name and omit忽略 the :index option. This is the same as specifying :index => address so

<%= fields_for ‘person[address][primary][]’, address do |address_form| %>

<%= address_form.text_field :city %>

<% end %>

produces exactly the same output as the previous example.

8 Forms to external resources

If you need to post some data to an external外部 resource it is still great to build your from using rails form helpers. But sometimes you need to set an authenticity_token for this resource. You can do it by passing an :authenticity_token => ‘your_external_token’ parameter to the form_tag options:

<%= form_tag ‘http://farfar.away/form’, :authenticity_token => ‘external_token’) do %>

Form contents

<% end %>

Sometimes when you submit data to an external resource, like payment gateway, fields you can use in your form are limited by an external API. So you may want not to generate an authenticity_token hidden field at all. For doing this just pass false to the :authenticity_token option:

<%= form_tag ‘http://farfar.away/form’, :authenticity_token => false) do %>

Form contents

<% end %>

The same technique is available for the form_for too:

<%= form_for @invoice, :url => external_url, :authenticity_token => ‘external_token’ do |f|

Form contents

<% end %>

Or if you don’t want to render an authenticity_token field:

<%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|

Form contents

<% end %>

9 Building Complex Forms

Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include:

 

File Uploading Using Rails

File Uploading using Rails

You may have a requirement in which you want your site visitors to upload a file on your server. Rails makes it very easy to handle this requirement. Now we will proceed with a simple and small Rails project.

As usual, let’s start off with a new Rails application called upload. So let’s create basic structure of the application by using simple rails command.

C:\ruby> rails upload

Now let’s decide where you would like to save your uploaded files. Assume this is data directory inside your public section. So create this directory and check the permissions.

C:\ruby> cd upload
C:\ruby> mkdir upload\public\data

Our next step will be as usual, to create controller and models, so let’s do that:

Creating Model:

Because this is not a database based application so we can keep name whatever is comfortable to us. Assume we have to create a DataFile model.

C:\ruby> ruby script/generate model DataFile
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/data_file.rb
      create  test/unit/data_file_test.rb
      create  test/fixtures/data_files.yml
      create  db/migrate
      create  db/migrate/001_create_data_files.rb

Now we will create a method called save in data_file.rb model file. This method will be called by the application controller.

class DataFile < ActiveRecord::Base
  def self.save(upload)
    name =  upload['datafile'].original_filename
    directory = "public/data"
    # create the file path
    path = File.join(directory, name)
    # write the file
    File.open(path, "wb") { |f| f.write(upload['datafile'].read) }
  end
end

The above function will take CGI object upload and will extract uploaded file name using helper function original_filename and finally it will store uploaded file into “public/data” directory. You can call helper function content_type to know media type of the uploaded file.

Here File is a ruby object and join is a helper function will concatenate directory name alongwith file name and will return full file path.

Next, to open a file in write mode we are using open helper function provided by File object. Further we are reading data from the passed data file and writing into output file.

Creating Controller:

Now let’s create a controller for our upload project:

C:\ruby> ruby script/generate controller Upload
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/upload
      exists  test/functional/
      create  app/controllers/upload_controller.rb
      create  test/functional/upload_controller_test.rb
      create  app/helpers/upload_helper.rb

Now we will create two controller functions first function index will call a view file to take user input and second function uploadFile takes file information from the user and passes it to the ‘DataFile’ model. We set the upload directory to the ‘uploads’ directory we created earlier “directory = ‘data’”.

class UploadController < ApplicationController
  def index
     render :file => 'app\views\upload\uploadfile.rhtml'
  end
  def uploadFile
    post = DataFile.save(params[:upload])
    render :text => "File has been uploaded successfully"
  end
end

Here we are calling function defined in model file. The render function is being used to redirect to view file as well as to display a message.

Creating View:

Finally we will create a view file uploadfile.rhtml which we have mentioned in controller. Populate this file with the following code:

<h1>File Upload</h1>
<%= start_form_tag ({:action => 'uploadFile'}, 
                        :multipart => true) %>
<p><label for="upload_file">Select File</label> : 
<%= file_field 'upload', 'datafile' %></p>
<%= submit_tag "Upload" %>
<%= end_form_tag %>

Here everything is same what we have explained in earlier chapters. Only new tag is file_field which will create a button to select a file from user’s computer.

By setting the multipart parameter to true, you ensure that your action properly passes along the binary data from the file.

Here, an important point to note is that we have assigned “uploadFile” as the method name in :action, which will be called when you click the Upload button.

This will show you a screen as follows:

Upload File

Now you select a file and upload it, this file will be uploaded into app/public/data directory with the actual file name and a message will be displayed to you saying that “File has been uploaded successfully”.

NOTE: If a file with the same name already exists in your output directory then it will be over-written.

Files uploaded from Internet Explorer:

Internet Explorer includes the entire path of a file in the filename sent, so the original_filename routine will return something like:

C:\Documents and Files\user_name\Pictures\My File.jpg

instead of just:

My File.jpg

This is easily handled by File.basename, which strips out everything before the filename.

def sanitize_filename(file_name)
  # get only the filename, not the whole path (from IE)
  just_filename = File.basename(file_name) 
  # replace all none alphanumeric, underscore or perioids
  # with underscore
  just_filename.sub(/[^\w\.\-]/,'_') 
end

Deleting an existing File:

If you want to delete any existing file then its simple and need to write following code:

  def cleanup
    File.delete("#{RAILS_ROOT}/dirname/#{@filename}") 
            if File.exist?("#{RAILS_ROOT}/dirname/#{@filename}")
  end

For a complete detail on File object, you need to go through Ruby Reference Manual.

Python 的虛擬環境及多版本開發利器─Virtualenv 與 Pythonbrew

Python 的虛擬環境及多版本開發利器─Virtualenv 與 Pythonbrew

Virtualenv 和 Pythonbrew 都是可以創造虛擬(獨立)Python 環境的工具,只是虛擬(獨立)標的不同。

Virtualenv 可以隔離函數庫需求不同的專案,讓它們不會互相影響。在建立並啟動虛擬環境後,透過 pip 安裝的套件會被放在虛擬環境中,專案就可以擁有一個獨立的環境。

簡而言之,Virtualenv 可以幫你做到:

  • 在沒有權限的情況下安裝新套件
  • 不同專案可以使用不同版本的相同套件
  • 套件版本升級時不會影響其他專案

Pythonbrew 則可以在家目錄中安裝多個 Python,並迅速地切換版本;也可以在指定的 Python 版本下批次測試你的 Python 程式;另外更整合了 Virtualenv。

這篇文章會詳細介紹這兩個工具,讓你在多人開發及多版本開發的環境中更得心應手。

 

事前準備

Python 的 package 通常會上傳至 PyPI,有很多工具都可以從 PyPI 安裝 package。下面會使用 easy_install 這個工具(由 setuptools 提供)來安裝 Virtualenv 和 Pythonbrew。

01. Linux

如果不知道 easy_install 或還沒安裝 setuptools,在 Debian/Ubuntu 可以用下列指令安裝:

$ sudo apt-get install python-setuptools 

在 Fedora/CentOS/Redhat/openSUSE,則可以使用:

$ su - # yum install python-setuptools 

02. Windows

在 Windows 則可以從 setuptools 的頁面找到 *.exe 格式的安裝檔案。安裝完後,可以在 C:\PythonX.Y\Scripts</code>(X.Y 是 Python 的版本)下找到 easy_install.exe。記得把這個路徑放進 Windows 環境變數中的 PATH。

接著就可以輕鬆安裝任何在 PyPI 的 Python Package 囉。

Virtualenv - Virtual Python Environment builder

01. 安裝

Pythonbrew 已整合了 Virtualenv,如果不想額外安裝一個套件,也可以不要裝 Virtualenv。

如果需要安裝,請於命令列模式下輸入下列指令:

# easy_install virtualenv 

02. 使用方法

I. 建立虛擬環境

請於命令列模式下輸入下列指令:

$ virtualenv [指定虛擬環境的名稱] 

例如下列指令會建立名為 “ENV” 的虛擬環境:

$ virtualenv ENV 

預設在建立虛擬環境時,會依賴系統環境中的 site packages,如果想完全不依賴系統的 packages,可以加上參數 —no-site-packages 來建立虛擬環境:

$ virtualenv --no-site-packages [指定虛擬環境的名稱] 

II. 啟動虛擬環境

請先切換當前目錄至建立的虛擬環境中。前例中,建立名稱為 “ENV”,則:
$ cd ENV 
接著,啟動虛擬環境:
$ source bin/activate 
在 Windows 環境中則改用:
> \path\to\env\Scripts\activate.bat 
然後就可以注意到,在 shell 提示字元的最前面多了虛擬環境的名稱提示:
(ENV) ...$ 
III. 退出虛擬環境
請於命令列模式下輸入下列指令:
$ deactivate 
就可以回到系統原先的 Python 環境。
IV. 在虛擬環境安裝新的 Python 套件
Virtualenv 在安裝時會附帶 pip 這個 Python 的套件安裝工具,當虛擬環境被啟動時,由它安裝的 package 會出現在虛擬環境的資料夾中,用法是:
(ENV)...$ pip install [套件名稱] 
如果系統也有安裝 pip,請特別注意是否已經啟動虛擬環境,否則套件會被安裝到系統中,而非虛擬環境裡。 如果想要避免 pip 在沒有進入虛擬環境時被使用,可以在 ~/.bashrc 加上:
export PIP_REQUIRE_VIRTUALENV=true 
要求 pip 一定要在虛擬環境中執行。 也可以用下面的設定,讓系統的 pip 自動使用啟動中的虛擬環境。
export PIP_RESPECT_VIRTUALENV=true 
避免意外將套件安裝至系統環境。
V. 從程式中指定使用虛擬環境的函數庫
無法從 Shell 啟動虛擬環境的情況,像是使用 mod_pythonmod_wsgi,這時可以在 Python 的程式中加上:
activate_this = '/path/to/env/bin/activate_this.py' execfile(activate_this, dict(__file__=activate_this)) 
來使用安裝在虛擬環境中的 packages。

03. 延伸套件:Virtualenvwrapper

Virtualenvwrapper 是一個 Virtualenv 的 extension,可使虛擬環境的管理變得更容易。 詳細來說,Virtualenvwrapper 提供下述功能:
  1. 將所有的虛擬環境整合在一個目錄下。
  2. 管理(新增、移除、複製)所有的虛擬環境。
  3. 可以使用一個命令切換虛擬環境。
  4. Tab 補全虛擬環境的名字。
  5. 每個操作都提供允許使用者自訂的 hooks。
  6. 可撰寫容易分享的 extension plugin 系統。
I. 安裝
請於命令列模式下輸入下列指令:
# easy_install virtualenvwrapper 
II. 使用方法
$WORKON_HOME製作虛擬環境:
$ mkvirtualenv [-i package] [-r requirements_file] [virtualenv options] ENVNAME 
列出所有的虛擬環境:
$ lsvirtualenv [-b] [-l] [-h] 
-b 是簡短模式;-l 是詳細模式(預設);-h 是印出 help 資訊。 移除虛擬環境:
$ rmvirtualenv ENVNAME 
複製虛擬環境:
$ cpvirtualenv ENVNAME TARGETENVNAME 
啟動虛擬環境:
$ workon [environment_name] 
如果只輸入 workon,則會列出所有的虛擬環境。 離開虛擬環境一樣是使用 deactivate。 可以使用下面的設定來告訴 pip Virtualenv 的路徑。
export PIP_VIRTUALENV_BASE=$WORKON_HOME 
Virtualenvwrapper 的功能當然不只如此,更多功能可以參考 Virtualenvwrapper 的官方文件

Pythonbrew

01. 安裝

Pythonbrew 是個比較新的專案,雖然比較新,卻非常完整。它也有整合上面介紹的 Virtualenv。可以用類似 Virtualenvwrapper 的方式來操作 Virtualenv。 安裝方式與 Virtualenv 一樣,只要輸入下面的指令就可以了:
# easy_install $ pythonbrew 
Pythonbrew 官方有推薦的安裝方式,但這篇教學為求一致性,就不額外介紹了,可以參考 pythonbrew/README.rst。 對於 Windows 的使用者,很可惜地,Pythonbrew 暫時沒有支援 Windows 的計畫 (#6: Windows Support? - Issues - utahta/pythonbrew - GitHub)。所以 Windows 暫時還沒辦法使用 Pythonbrew 囉。 經過 easy_install 的安裝後,還需要在 shell 執行:
$ pythonbrew_install 
才會把初始的設定檔和資料夾配置進你的家目錄。接著要修改 ~/.bashrc 的配置:
$ echo "source ~/.pythonbrew/etc/bashrc" >> ~/.bashrc 
這樣就算安裝完全囉。 Pythonbrew 使用 curl 來抓取資料,如果你的系統沒有,請記得安裝。Ubuntu 上可以使用這行指令:
$ sudo apt-get install curl 

02. 編譯前準備

因為 Pythonbrew 採取下載 tarball,並編譯、安裝的方法,所以我們要先為系統準備好編譯 Python 所需的套件。 也因為許多 Linux 發行版都已打包 Python,所以我們可以偷懶一點,用已經打包好的套件來解決編譯所需的相依性。在 Ubuntu/Debian 上,可以透過:
$ sudo apt-get build-dep python2.7 
來安裝所有編譯 Python 2.7 所需的套件。雖然已經能夠安裝得相當完整,但還是缺少了 gdbm 這個 module,如果需要的話,可以透過:
$ sudo apt-get build-dep python-gdbm 
來安裝編譯 gdbm 所需的套件。 註:bsddb185linuxaudiodevossaudiodevsunaudiodev 等是按以上方式安裝後,仍會缺少的 module。其中 ossaudiodev(Open Sound System)在隨 Ubuntu 發布的 Python 中有提供,列出來讓大家參考。 Fedora/CentOS/Redhat/openSUSE 則可以使用 yum-builddep 這個指令。

03. 使用方法

Pythonbrew 的操作不外乎安裝、移除、列出及使用新的 Python 版本,下面是依照初次使用時所需的指令順序來介紹。
I. 列出可安裝的版本
首先我們用 list --know 列出可以安裝的 Python 版本:
$ pythonbrew list --know 
II. 安裝新的版本
接著利用 install VERSION 來下載並編譯 Python 到本機,除了接 Python 的版本編號以外,也可以接 Python 的 tarball 路徑或網址來安裝;也能調整編譯 Python 的選項。下面是一些例子:
$ pythonbrew install 2.7.2 $ pythonbrew install --verbose 2.7.2 $ pythonbrew install --force 2.7.2 $ pythonbrew install --no-test 2.7.2 $ pythonbrew install --configure="CC=gcc_4.1" 2.7.2 $ pythonbrew install --no-setuptools 2.7.2 $ pythonbrew install http://www.python.org/ftp/python/2.7/Python-2.7.2.tgz $ pythonbrew install /path/to/Python-2.7.2.tgz $ pythonbrew install /path/to/Python-2.7.2 $ pythonbrew install 2.7.2 3.2 
III. 清理安裝時產生的檔案
下載的 Python tarball 會放在 ~/.pythonbrew/dists/ 下;而編譯則會在 ~/.pythonbrew/build/ 下進行。如果想清理這兩個目錄,可以使用:
$ pythonbrew cleanup 
IV. 列出所有已安裝的版本
安裝好之後,可以使用 list 命令列出所有已安裝的 Python 版本:
$ pythonbrew list 
後面有打星號的,就是現在正在使用的 Python 版本。
V. 切換已安裝的版本
可以使用 switch 來切換預設的 Python 版本:
$ pythonbrew switch VERSION 
如果只想在當前的 shell 下切換,可以使用 use
$ pythonbrew use VERSION 
要切換回預設的環境時,使用 off
$ pythonbrew off 
就會返回系統環境的 Python 了。
VI. 批次在不同版本下測試
最重要的是,可以用系統內所有安裝過的 Python 版本,或指定的 Python 版本來測試自己的程式!
$ pythonbrew py test.py # 使用所有有安裝的版本 $ pythonbrew py -v test.py # 詳細輸出 $ pythonbrew py -p 2.7.2 -p 3.2 test.py # 指定特定的版本 
VII. 移除已安裝的版本
若想移除已經安裝的 Python,則可以使用 uninstall
$ pythonbrew uninstall 2.7.2 $ pythonbrew uninstall 2.7.2 3.2 
VIII. 與 Virtualenv 的整合
要注意 Pythonbrew 中所提供的 Virtualenv,是基於 Pythonbrew 中所安裝的 Python(置於 ~/.pythonbrew/venvs/Python-VERSION/ 下)。在不使用 Pythonbrew 的情況下,無法使用附屬於 Pythonbrew 的 venv 功能。 Pythonbrew 提供了和 Virtualenvwrapper 類似的功能,只是沒有像 Virtualenvwrapper 那麼完整的 plugin 系統。所有在 Pythonbrew 中的 Virtualenv 指令都以 venv 作為第一個副命令。
$ pythonbrew venv create [指定虛擬環境的名稱] $ pythonbrew venv list $ pythonbrew venv use [指定虛擬環境的名稱] $ pythonbrew venv delete [指定虛擬環境的名稱] 
離開虛擬環境一樣是使用 deactivate
IX. Buildout
如果有使用 Buildout 這個工具,也可以透過 Pythonbrew 來執行:
$ pythonbrew buildout $ pythonbrew buildout -p 2.6.6 # 指定版本 
X. 自我更新
最後,Pythonbrew 有內建更新自己的指令:
$ pythonbrew update $ pythonbrew update --master $ pythonbrew update --develop 
Virtualenv 和 Pythonbrew 就介紹到這邊,如果想獲得更多資訊,可以多多參考它們的官網哦。祝大家玩得愉快。

參考資料

Django开发必知必会

Django开发必知必会

Django开发必知必会

版权说明

由于最近发现很多转载笔者的文章而没有说明作者和出处,所以特别在些声明. 本博客所有内容采用 Creative Commons Licenses 许可使用. 引用本内容时,请保留 朱涛出处 ,并且 非商业 .

摘要

Django 作为 python 社区中最流利的web框架,它所强调的就是 快速开发复用可读. 本文主要介绍使用 Django 来开发所需要了解的知识,其中也包含了一些指导性的原则以及笔者的一些经验之谈.
Contents
  • 版权说明
  • 摘要
  • 基本知识
    • 熟悉python和django
    • 其它的相关技术
  • 可用的资源
  • 高级知识
    • 使用virtualenv+pip来构建独立的开发环境
    • 使用fabric进行部署
  • 推荐的开发流程
  • 结论
  • 参考资料

基本知识

熟悉python和django

Django 是基于 python 的,所以在开发前需要熟悉相关的技术. python 推荐几本比较好的书,包括:
  1. learning python
  2. python cookbook
熟读 Django 的 官方文档 和相关的书籍,这些都是必须。比较好的书籍有:
  1. Django Book
  2. Pro Django
  3. Practical Django Projects
更多参考 django相关资源.

其它的相关技术

作为web开发的程序员,还需要了解其它的相关技术,包括:
  • html
  • css
  • javascript ( jquery 等)
  • ajax
  • 网络知识和标准,如 http 协议, TCP/IP网络架构等.

可用的资源

掌握了上面提到的基本知识外,我们对 python, Django 及web开发的相关技术有了一定的熟悉,也对 python 和 Django 的 哲学也有一定的了解,如 Don’t Repeat Yourself,Keep It Simple, StupidDon’t Reinvent Wheels 等. 所以,在开始一个项目之前我们需要了解已有哪些可用的资源. 而在可用的资源中,最需要跟踪的一个项目就是 pinax ,它提供了站点所必须的一些常用功能,而 所采用的方式便是提供一些 可复用的app, 使得我们很容易集成到我们的环境中, 或者基于 pinax 二次开发。 除此之外,还需要了解一些特定领域的相关资源,如:
  • 商店 satchmo
  • 地理 GeoDjango
  • OpenId django-openid
  • 等等
在获得特定领域的相关资源后,会大大地提高开发的效率,从而降低成本。

高级知识

掌握上面提到的技术,这时就基本上是一个合格的 Django 程序员了,但是除此之外,还需要了解一些高级的知识.

使用virtualenv+pip来构建独立的开发环境

virtualenv 用 于建立一个独立的(与其它module)的虚拟环境,从而使得不同的虚拟环境下的 程序可以依赖于不同版本的module,例如某个django project是基于0.96的,而另外的project是基于 1.1的,所以就必须有不同的虚拟环境。 了解更多参考: pip and virtualenv 使用 pip 来 复制 已有的依赖。 pip 是用来取代 easy_install 的,除此而外, 我们还可以很方便地使用 pip 来 复制 我们当前的工作环境。例如,我们建立一个requirements.txt 文件 来记录我们当前工作环境中的相关依赖程序,然后输入 pip install -r /path/to/requirements.txt, 那么相应的 依赖软件会自动安装。前提是你的系统已经安装了 pip. 除此而外,当你需要部署一个django项目时,可以使用 wsgi 很方便地进行部署,具体参考 virtualenv and wsgi.

使用fabric进行部署

参考 Deploying Python Web Applications 另外,我们还可以使用 python 的强大功能,来实现例如配置文件的路径自适应, 利用调试状态来设定特定的属性等.

推荐的开发流程

可使用下面的开发流程来进行 Django 的开发(假设是从新开始一个项目的):
  1. 使用 virtualenv 建立相应的独立环境
  2. 使用 easy_install 和 pip 安装相应的依赖模块
  3. 多人开发时可将当前的所有模块 复制 给同事( pip 有 feeze 命令来输出当前环境下的所有依赖模块及版本,以保证所有的开发人员使用相同的代码库)
  4. 为服务器也建立相应的独立环境,并使用 fabric 进行自动化部署.
  5. 最后,使用 wsgi 和web服务器来发布产品

结论

利用 Django 的 复用快速开发 等特性来进行Web开发, 其实是有一定的规律可循的, 除了上面介绍的一些开发知识(技巧)外,如果在开发中你发现某个过程很繁琐,你这时候就要考虑去调研 是否已经存在相关的解决方案.而不要一味地,耗时地去完成.

参考资料

  1. Notes on using pip and virtualenv with Django
  2. 啄木鸟社区
  3. django相关资源

Layouts and Rendering in Rails

Layouts and Rendering in Rails

Layouts and Rendering in Rails

This guide covers the basic layout features of Action Controller and Action View. By referring to this guide, you will be able to:

这个教程涵盖了Action Controller and Action View的基础布局。通过这个教程,你可以了解到如下内容:

  • Use the various rendering methods built into Rails 使用Rails内建的各种渲染方法
  • Create layouts with multiple content sections 使用多个content sections创建layouts
  • Use partials to DRY up your views 在你的view里面使用partials
  • Use nested layouts (sub-templates) 使用嵌套layoutssub-templates

1 Overview: How the Pieces Fit Together概述:如何完整的组合在一起

This guide focuses on the interaction between Controller and View in the Model-View-Controller triangle. As you know, the Controller is responsible for orchestrating the whole process of handling a request in Rails, though it normally hands off any heavy code to the Model. But then, when it’s time to send a response back to the user, the Controller hands things off to the View. It’s that handoff that is the subject of this guide.

这个教程主要着重于在model-view-controller三角(关系)中的controllerview之间的交互。众所周知,controller是负责编排在Rails处理请求的全过程,通过这样它一般可以让model从沉重的代码中解放出来。但是接下来,什么时候是发回一个响应给用户呢,controller把事情交给view.这就是handoff这也是这个教程的主题。

In broad strokes, this involves deciding what should be sent as the response and calling an appropriate method to create that response. If the response is a full-blown view, Rails also does some extra work to wrap包裹the view in a layout and possibly to pull in partial views. You’ll see all of those paths later in this guide.

在广泛的划分情况下,这涉及到决定应该发送什么作为响应或者调用一个适当的方法来创建一个响应。如果这个响应是一个full-blownviewRails同样做一些额外的工作来在layout中包装视图或者有可能的话调入其他部分view。在后部分你将会看到完整的教程。

2 Creating Responses创建一个回应

From the controller’s point of view, there are three ways to create an HTTP response:

基于controllerview观点,这里有三种方法来创建一个HTTP response

  • Call render to create a full response to send back to the browser 调用一个render来创建以个完全回应full response回传给浏览器。
  • Call redirect_to to send an HTTP redirect status code to the browser 调用一个redirect_to来发送一个HTTP重定向状态码给浏览器
  • Call head to create a response consisting solely of HTTP headers to send back to the browser 调用head创建一个响应,只包括HTTP头并将其发送回浏览器

I’ll cover each of these methods in turn. But first, a few words about the very easiest thing that the controller can do to create a response: nothing at all.

我将会依次(介绍)每种方法。但在此之前,用少量的文字说明最简单的事情——controller创建一个回应:什么都没有。

2.1 Rendering by Default: Convention Over Configuration in Action以默认方式渲染:Action约定优于配置

You’ve heard that Rails promotes “convention over configuration”. Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to valid routes. For example, if you have this code in your BooksController class:

你已经听过了Rails的原则约定优于配置。在这里默认的渲染是一个优秀的例子。默认情况中,Rails中的controllers自动渲染视图通过在routes中验证对应的视图名字。例如,如果你的(视图代码)在你的BooksController中:

rails generate scaffold Book title:string content:text

 

class BooksController < ApplicationController

end

And the following in your routes file:并且将下面的代码放入你的routes文件中:

resources :books

And you have a view file app/views/books/index.html.erb:

Rails will automatically render app/views/books/index.html.erb when you navigate to /books and you will see “Books are coming soon!” on your screen.

Rails将会自动渲染render app/views/books/index.html.erb当你导航到/books并且你将会看到在你的屏幕上会出现Books are coming soon!

However a coming soon screen is only minimally useful, so you will soon create your Book model and add the index action to BooksController:

无论如何a coming soon screen只是一个很小的用途,因此接着很快你将会创建你的Book模型并且添加index actionBooksController

class BooksController < ApplicationController

def index

@books = Book.all

end

end

Note that we dont have explicit render at the end of the index action in 按照accordance withconvention over configurationprinciple. The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controllers view path and render it. So in this case, Rails will render the app/views/books/index.html.erb file.

注意按照约定优于配置原则,在index action结束的时候,我们没有明确的渲染。这里的规则是如果你在使用一个controller action结束的时候没有明确的render一些事物,Rails将会自动的在controller的视图目录中找到action_name.html.erb文件并渲染它。因此在这个例子中,Rails将会渲染app/views/books/index.html.erb文件。

If we want to display the properties of all the books in our view, we can do so with an ERB template like this:

如果我们想在我们的视图中显示所有books的属性,我们可以这样做使用一个如下的ERB模板:

<h1>Listing Books</h1>

 

<table>

<tr>

<th>Title</th>

<th>Summary</th>

<th></th>

<th></th>

<th></th>

</tr>

 

<% @books.each do |book| %>

<tr>

<td><%= book.title %></td>

<td><%= book.content %></td>

<td><%= link_to ‘Show’, book %></td>

<td><%= link_to ‘Edit’, edit_book_path(book) %></td>

<td><%= link_to ‘Remove’, book, :confirm => ‘Are you sure?’, :method => :delete %></td>

</tr>

<% end %>

</table>

 

<br />

 

<%= link_to ‘New book’, new_book_path %>

 

The actual rendering is done by subclasses of ActionView::TemplateHandlers. This guide does not dig into that process, but it’s important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are .erb for ERB (HTML with embedded Ruby), and .builder for Builder (XML generator).

actual渲染是通过ActionView::TemplateHandlers的子类完成的。这个教程不会(深入)挖掘那个过程,但是知道一些文件存在在你的view controls,被选择用来做template handler这很重要。在Rails2ERBHTML with embedded Ruby)标准的扩展名是.erb,并且.builderBuilderXML生成器)的扩展名。

2.2 Using render

In most cases, the ActionController::Base#render method does the heavy lifting of rendering your application’s content for use by a browser. There are a variety of ways to customize the behaviour of render. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.

在大多数例子中,ActionController::Base#render方法通过浏览器将你的应用程序的内容生动的rendering给用户。这里有多种方法来定制render的习惯。你可以render一个Rails template为默认的视图,或者指定一个template,或者一个文件,或者一个内联代码,或者什么都没有。你可以render textJSON,或者XML。你可以指定上下文类型或者HTTP回应rendered的状态。

If you want to see the exact results of a call to render without needing to inspect检查it in a browser, you can call render_to_string. This method takes exactly the same options as render, but it returns a string instead of sending a response back to the browser.

如果你想查看调用一个render在浏览器中不需要检查的那些额外的结果。你可以调用render_to_string。这个方法获取和render完全的options,但是它返回的一个字符串而不是发送一个response回浏览器。

2.2.1 Rendering Nothing

Perhaps the simplest thing you can do with render is to render nothing at all:

render :nothing => true

If you look at the response for this using cURL, you will see the following:

如果你使用cURL查看回应,你将会看到如下内容:

curl -I 127.0.0.1:3000/books#此命令就是访问HTTP status

$ curl -i 127.0.0.1:3000/books

HTTP/1.1 200 OK

Connection: close

Date: Sun, 24 Jan 2010 09:25:18 GMT

Transfer-Encoding: chunked

Content-Type: /; charset=utf-8

X-Runtime: 0.014297

Set-Cookie: _blog_session=…snip…; path=/; HttpOnly

Cache-Control: max-age=0, private, must-revalidate

We see there is an empty response (no data after the Cache-Control line), but the request was successful because Rails has set the response to 200 OK. You can set the :status option on render to change this response. Rendering nothing can be useful for AJAX requests where all you want to send back to the browser is an acknowledgment that the request was completed.

def index

 

render :nothing => true,:status=>500

 

end

jhjguxin@jhjguxin-virtual-machine:~/blog$ curl -I 127.0.0.1:3000/books

 

HTTP/1.1 300 Multiple Choices

HTTP/1.1 500 Internal Server Error

 

You should probably be using the head method, discussed later in this guide, instead of render :nothing. This provides additional额外flexibility灵活的and makes it explicit that youre only generating HTTP headers.(使用render :nothing提供了额外的灵活性它能够准确的只创建HTTP headers

2.2.2 Rendering an Action’s View渲染一个动作的视图

If you want to render the view that corresponds to a different action within the same template, you can use render with the name of the view:

如果你想render的视图对应着不同的action在一个相同的template中,你可以使用render加上视图的名字。

def update

@book = Book.find(params[:id])

if @book.update_attributes(params[:book])

redirect_to(@book)

else

render “edit”

end

end

If the call to update_attributes fails, calling the update action in this controller will render the edit.html.erb template belonging to the same controller.

If you prefer如果你喜欢, you can use a symbol instead of a string to specify the action to render:

def update

@book = Book.find(params[:id])

if @book.update_attributes(params[:book])

redirect_to(@book)

else

render :edit

end

end

To be explicit, you can use render with the :action option (though this is no longer necessary in Rails 3.0):

def update

@book = Book.find(params[:id])

if @book.update_attributes(params[:book])

redirect_to(@book)

else

render :action => “edit”

end

end

Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.

使用render:action是来(处理)来自Rails的新访客的频繁混乱的(请求)。指定的action是用来决定渲染哪个视图,但是Rails并没有在controller中为那个action运行任何代码。不管在视图中的你请求的何种实例必须在调用render之前被set up到当前action中。

2.2.3 Rendering an Action’s Template from Another Controller从另一个controller渲染一个actionstemplate

What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with render, which accepts the full path (relative to app/views) of the template to render. For example, if you’re running code in an AdminProductsController that lives in app/controllers/admin, you can render the results of an action to a template in app/views/products this way:

如果你想render一个template它的action来自完全不同的controller?你也可以使用render来(达到目的),它接收template的全路径(相对于app/views#项目路径为根目录)来render

例如,你正在运行的代码在AdminProductsController存放在app/controllers/admin中你可以render一个action的结果到app/views/products 的一个template中用这样的方法: render ‘products/show’

Rails knows that this view belongs to a different controller because of the embedded slash character in the string. If you want to be explicit如果你想准确的表达, you can use the :template option (which was required on Rails 2.2 and earlier):

render :template => ‘products/show’

2.2.4 Rendering an Arbitrary File渲染任意文件

The render method can also use a view that’s entirely outside of your application (perhaps you’re sharing views between two Rails applications):

render方法也可以使用一个完全脱离你的application的视图(可能你在两个Rails之间共享视图)

render “/u/apps/warehouse_app/current/app/views/products/show”

Rails determines that this is a file render because of the leading slash character斜线字符. To be explicit, you can use the :file option (which was required on Rails 2.2 and earlier):

Rails认为这个是一个文件render是因为开头的斜线字符。更准确的(表示),你可以使用:file选项(它至少需要Rails2.2或者更高)。

render :file =>

“/u/apps/warehouse_app/current/app/views/products/show”

The :file option takes an absolute file-system path. Of course, you need to have rights to the view that you’re using to render the content.

:file选项获取一个完全文件系统路径。当然,你需要有权限(查看)你正在渲染的视图。

By default, the file is rendered without using the current layout. If you want Rails to put the file into the current layout, you need to add the :layout => true option.

默认情况下,这个文件没有使用当前的layoutrender。如果你想Rails把这个文件渲染到当前layout,你需要添加:layout=>true选项。

 

If you’re running Rails on Microsoft Windows, you should use the :file option to render a file, because Windows filenames do not have the same format as Unix filenames.如果你正在Microsoft Windows中运行Rails,你应该使用:file选项来render一个文件,因为Windows文件名称与Unix文件名称并不相同(这里的文件名称应该包含了路径)。

2.2.5 Wrapping it up包装起来

The above three ways of rendering (rendering another template within the controller, rendering a template within another controller and rendering an arbitrary file on the file system) are actually variants of the same action.

这里总共有三种rendering的方法(在controllerrendering另一个template,使用另一个controller render一个template或者渲染一个文件系统上的任意类型的文件)实际上它们是相同action的变种。

In fact, in the BooksController class, inside of the update action where we want to render the edit template if the book does not update successfully, all of the following render calls would all render the edit.html.erb template in the views/books directory:

事实上,在BooksController类中,在update action中如果book没有更新成功的话我们希望render edit template,所有的后续render调用将会全部renderviews/books目录中的edit.html.erb(edit的各种render方式)

render :edit

render :action => :edit

render ‘edit’

render ‘edit.html.erb’

render :action => ‘edit’

render :action => ‘edit.html.erb’

render ‘books/edit’

render ‘books/edit.html.erb’

render :template => ‘books/edit’

render :template => ‘books/edit.html.erb’

render ‘/path/to/rails/app/views/books/edit’

render ‘/path/to/rails/app/views/books/edit.html.erb’

render :file => ‘/path/to/rails/app/views/books/edit’

render :file => ‘/path/to/rails/app/views/books/edit.html.erb’

Which one you use is really a matter of style and convention, but the rule of thumb is to use the simplest one that makes sense for the code you are writing.

你实际使用的格式与风格和公约有关,但一般规则是使用最简单的一个,使你正在编写的代码的意义。

2.2.6 Using render with :inline(使用):inlinerender

The render method can do without a view completely, if you’re willing to use the :inline option to supply ERB as part of the method call. This is perfectly valid:

render方法可以完全没有视图而(单独)使用,如果你使用:inline选项来提供ERBERB标签)作为方法调用(的参数)。这完全合法的:

render :inline =>

“<% products.each do |p| %><p><%= p.name %></p><% end %>”

 

There is seldom很少any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate单独erb view instead.

(很少有好的理由来使用这个选项)。在你的controllers中复杂的ERB使得你的Rails(应用程序)在MVC方面很失败,并且使得其他开发人员很难跟随你项目的逻辑。(最好)使用单个erb视图替代。

 

By default, inline rendering uses ERB. You can force it to use Builder instead with the :type option:

默认情况下,inline rendering使用ERB。你可以使用创建器强制替代它通过:type选项:

render :inline =>

“xml.p {‘Horrid coding practice!’}”, :type => :builder

2.2.7 Rendering Text渲染文本

You can send plain text – with no markup at all – back to the browser by using the :text option to render:

你可以发送纯文本——没有做任何标记——返回到浏览器通过在render中使用:text选项:

render :text => “OK”

Rendering pure text is most useful when you’re responding to AJAX or web service requests that are expecting something other than proper HTML.

Rendering纯文本在你回应到AJAZ或者web service请求的时候非常有用(因为)此时用户期待适当的HTML以外的东西。

 

By default, if you use the :text option, the text is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the :layout => true option.

作为默认的,如果你是用:text选项,text并没有被render到当前layout。如果你希望Rails放置text到当前的layout,你需要添加:layout=>true选项。

2.2.8 Rendering JSON渲染JSON

JSON is a JavaScript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:

JSONJavaScript数据格式它被很多AJAX库使用。Rails内建支持转换对象成JSON并且rendering这些JSON反馈给浏览器:

render :json => @product

You don’t need to call to_json on the object that you want to render. If you use the :json option, render will automatically call to_json for you.

你不必对你renderobject调用to_json。如果你使用:json选项,render将会自动为你调用to_json

2.2.9 Rendering XML

Rails also has built-in support for converting objects to XML and rendering that XML back to the caller:

render :xml => @product

You don’t need to call to_xml on the object that you want to render. If you use the :xml option, render will automatically call to_xml for you.

2.2.10 Rendering Vanilla JavaScript渲染香草味的的JavaScript

Rails can render vanilla JavaScript:

render :js => “alert(‘Hello Rails’);”

This will send the supplied string to the browser with a MIME type of text/javascript.

2.2.11 Options for render

Calls to the render method generally accept four options:

  • :content_type
  • :layout
  • :status
  • :location
2.2.11.1 The :content_type Option

By default, Rails will serve the results of a rendering operation with the MIME content-type of text/html (or application/json if you use the :json option, or application/xml for the :xml option.). There are times when you might like to change this, and you can do so by setting the :content_type option:

默认情况下,Rails将会帮助渲染操作通过使用text/htmlMIME content-type(或者如果你使用:json选项则是application/json,或者如果是:xml选项application/xml)。有时你可以改成这样,你可以通过设置:content_type选项达成:

render :file => filename, :content_type => ‘application/rss’

2.2.11.2 The :layout Option

With most of the options to render, the rendered content is displayed as part of the current layout. You’ll learn more about layouts and how to use them later in this guide.

它是render的常见的选项,通过这个选项被rendered的内容被显示为当前layout的一部分。在本教程的随后部分你将了解更多的layouts的知识以及如何使用它们。

You can use the :layout option to tell Rails to use a specific file as the layout for the current action:

render :layout => ‘special_layout’

You can also tell Rails to render with no layout at all:

render :layout => false

2.2.11.3 The :status Option

Rails will automatically generate a response with the correct HTTP status code (in most cases, this is 200 OK). You can use the :status option to change this:

Rails将会自动生成(包含)正确的HTTP状态码的响应(在大多数情况,200 OK)。你可以使用:status选项来更改这些:

render :status => 500

or

render :status => :forbidden

Rails understands both numeric status codes and symbols for status codes.

2.2.11.4 The :location Option

You can use the :location option to set the HTTP Location header:

render :xml => photo, :location => photo_url(photo)

2.2.12 Finding Layouts

To find the current layout, Rails first looks for a file in app/views/layouts with the same base name as the controller. For example, rendering actions from the PhotosController class will use app/views/layouts/photos.html.erb (or app/views/layouts/photos.builder). If there is no such controller-specific layout, Rails will use app/views/layouts/application.html.erb or app/views/layouts/application.builder. If there is no .erb layout, Rails will use a .builder layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.

要查找正确的layoutRails首先查找app/views/layouts中与controller base name相同的文件。For example, rendering actions from the PhotosController class will use app/views/layouts/photos.html.erb (or app/views/layouts/photos.builder). If there is no such controller-specific layout, Rails will use app/views/layouts/application.html.erb or app/views/layouts/application.builder. 如果没有找到.reblayoutRails将会使用一个.builder如果存在。Rails也提供一些方法来恰当的分派assign指定的layouts到个别的controllersactions

2.2.12.1 Specifying Layouts on a per-Controller Basis

You can override the automatic layout conventions in your controllers by using the layout declaration in the controller. For example:

你可以通过在controller(类)中使用layout决定(指定的)layout来覆盖自动layout公约。例如:

class ProductsController < ApplicationController

layout “inventory”

end

With this declaration, all methods within ProductsController will use app/views/layouts/inventory.html.erb for their layout.

通过这个声明,在ProductsController中所有的方法都将使用app/views/layouts/inventory.html.erb作为他们的layout

To assign a specific layout for the entire application, use a declaration in your ApplicationController class:

要分配一个指定的layout给整个application,在你的ApplicationController类(app/controllers/application_controller.rb使用一个声明:

class ApplicationController < ActionController::Base

layout “main”

end

With this declaration, all views in the entire application will use app/views/layouts/main.html.erb for their layout.通过这个声明,整个应用程序中的view将会使用app/views/layouts/main.html.erb作为他们的layout

2.2.12.2 Choosing Layouts at Runtime在运行的时候选择layout

You can use a symbol to defer the choice of layout until a request is processed:

你可以使用一个symbol来推迟选择的layout直到一个请求被处理:

class ProductsController < ApplicationController

layout :products_layout

 

def show

@product = Product.find(params[:id])

end

 

private

def products_layout

@current_user.special? ? “special” : “products”

end

 

end

Now, if the current user is a special user, they’ll get a special layout when viewing a product. You can even use an inline method to determine the layout:

现在,如果当前用户是一个特殊用户,他们将得到一个特殊的layout当其正在访问一个product。你甚至可以使用一个内联方法来确定layout

You can also decide the layout by passing a Proc object, the block you give the Proc will be given the controller instance, so you can make decisions based on the current request. For example:

你也可以通过一个Proc object来决定layout,你给出的Proc块将会被传递给controller实例,因此你可以在当前的请求的基础上作出(指定layout的)决定。

class ProductsController < ApplicationController

layout Proc.new { |controller| controller.request.xhr? ? ‘popup’ : ‘application’ }

end

块里面的内容是什么意思呢?

2.2.12.3 Conditional Layouts条件layouts

Layouts specified at the controller level support :only and :except options that take either a method name or an array of method names which correspond to method names within the controller:

controller级别的layouts指定支持:only and :except选项它们获取一个方法名或者一个方法名组成的数组它们对应于在controller中的方法名。

class ProductsController < ApplicationController

layout “product”, :except => [:index, :rss]

end

With this declaration, the product layout would be used for everything but the rss and index methods.

通过这个声明,product将会使用任何layout但是除了rssindex方法。

2.2.12.4 Layout Inheritance layout继承

Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example:

  • application_controller.rb

class ApplicationController < ActionController::Base

layout “main”

end

  • posts_controller.rb

class PostsController < ApplicationController

end

  • special_posts_controller.rb

class SpecialPostsController < PostsController

layout “special”

end

  • old_posts_controller.rb

class OldPostsController < SpecialPostsController

layout nil

 

def show

@post = Post.find(params[:id])

end

 

def index

@old_posts = Post.older

render :layout => “old”

end

end

In this application:

  • In general, views will be rendered in the main layout
  • PostsController#index will use the main layout
  • SpecialPostsController#index will use the special layout
  • OldPostsController#show will use no layout at all
  • OldPostsController#index will use the old layout
2.2.13 Avoiding Double Render Errors避免两次render错误

Sooner or later, most Rails developers will see the error message “Can only render or redirect once per action”. While this is annoying, it’s relatively easy to fix. Usually it happens because of a fundamental misunderstanding of the way that render works.

迟早,多大数Rails开发人员将会看到Can only render or redirect once per action”.当这恼人的(消息出现的时候),它相当容易修复。它发生的通常情况是因为对render工作方式的根本误解。

For example, here’s some code that will trigger this error:

例如,这些代码将会触发这个错误:

def show

@book = Book.find(params[:id])

if @book.special?

render :action => “special_show”

end

render :action => “regular_show”

end

If @book.special? evaluates to true, Rails will start the rendering process to dump转储the @book variable into the special_show view. But this will not stop the rest of the code in the show action from running, and when Rails hits the end of the action, it will start to render the regular_show viewand throw an error. The solution is simple: make sure that you have only one call to render or redirect in a single code path. One thing that can help is and return. Here’s a patched version of the method:

def show

@book = Book.find(params[:id])

if @book.special?

render :action => “special_show” and return

end

render :action => “regular_show”

end

Make sure you use and return and not && return because while the former will work, the latter will not due to operator precedence in the Ruby Language.

确保你使用的是and return而不是&& return因为即使former将会工作,然而随后的却不会那是因为ruby语言中的运算优先操作。

irb(main):019:0> 1+3 and p 3+4

7

=> nil

irb(main):020:0> 1+3 && p 3+4

SyntaxError: compile error

(irb):20: syntax error, unexpected tINTEGER, expecting kDO or ‘{’ or ‘(’

1+3 && p 3+4

^

from (irb):20

from :0

irb(main):021:0> 1+3 &&(p 3+4)

7

=> nil

irb(main):022:0> false and 3+4

=> false

2.3 Using redirect_to使用重定向

Another way to handle returning responses to an HTTP request is with redirect_to. As you’ve seen, render tells Rails which view (or other asset) to use in constructing a response. The redirect_to method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:

另一种方法是使用redirect_to来处理HTTP请求返回响应。正如你看到的,render通知Rails——视图(或者其他asset)使用其他(layout)结构来响应。redirect_to方法做一些完全不同的事情:它告诉浏览器发送一个新的请求给不同的URL。例如,无论你在你代码的哪里你可以重定向到photosindex,在你的应用程序中使用这个掉用:

redirect_to photos_path

You can use redirect_to with any arguments that you could use with link_to or url_for. In addition, theres a special redirect that sends the user back to the page they just came from:这里是特殊的重定向发送(命令)给用户(的浏览器)回到原来的页面:

redirect_to :back

2.3.1 Getting a Different Redirect Status Code

Rails uses HTTP status code 302 (temporary redirect) when you call redirect_to. If you’d like to use a different status code (perhaps 301, permanent redirect), you can do so by using the :status option:

Rails使用HTTP状态代码301(临时重定向)当你调用redirect_to。如果你想使用一个不同的状态代码(可能是301,永久重定向),你可以通过使用:status选项:

redirect_to photos_path, :status => 301

Just like the :status option for render, :status for redirect_to accepts both numeric and symbolic header designations名称.

2.3.2 The Difference Between render and redirect_to

Sometimes inexperienced developers conceive of redirect_to as a sort of goto command, moving execution from one place to another in your Rails code. This is not correct. Your code stops running and waits for a new request for the browser. It just happens that you’ve told the browser what request it should make next, by sending back an HTTP 302 status code.

有时缺乏经验的开发人员心中构想的redirect_to就像一种goto命令,从你的Rails代码的一个地方移动到另一个地方执行。

Consider these actions to see the difference:

def index

@books = Book.all

end

 

def show

@book = Book.find_by_id(params[:id])

if @book.nil?

render :action => “index”

end

end

With the code in this form, there will likely be a problem if the @book variable is nil. Remember, a render :action doesnt run any code in the target action, so nothing will set up the @books variable that the index view is presumably可能depending on(实际验证结果也就是说@books为空). One way to fix this is to redirect instead of rendering:

def index

@books = Book.all

end

 

def show

@book = Book.find_by_id(params[:id])

if @book.nil?

redirect_to :action => :index

end

end

With this code, the browser will make a fresh request for the index page, the code in the index method will run, and all will be well.

The only downside to this code, is that it requires a round tripto the browser, the browser requested the show action with /books/1 and the controller finds that there are no books, so the controller sends out a 302 redirect response to the browser telling it to go to /books/, the browser complies依从and sends a new request back to the controller asking now for the index action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen.

这段代码仅有的缺点,是它需要浏览器兜一个圈,浏览器通过向服务器发送/books/1请求show action同时controller发现数据库没有这本书,因此controller发出一个302重定向响应给浏览器告诉它导航到/books/,浏览器依从(响应)并且发回一个新的请求来申请现在(进行)index actioncontroller随后从数据库中获取所有的books并且rendersindex template,发送它至浏览器然后在你的screen得到显示。

While in a small app, this added latency might not be a problem, it is something to think about when speed of response is of the essence本质. One way to handle this double request (though a contrived example) could be:

然而在一个小的app,加载延迟不是问题,有时候是基于响应速度为主的考虑。一种方式来处理这个两次请求(通过一个人为的例子)可以像这样:

def index

@books = Book.all

end

 

def show

@book = Book.find_by_id(params[:id])

if @book.nil?

@books = Book.all

render “index”, :alert => ‘Your book was not found!’

end

end

Which would detect检测that there are no books, populate填充the @books instance variable with all the books in the database and then directly render the index.html.erb template returning it to the browser with a flash alert message telling the user what happened.

2.4 Using head To Build Header-Only Responses使用head来创建一个Header-Only响应

The head method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling render :nothing.它提供一个render :nothing明显的替代。The head method takes one parameter, which is interpreted解释执行as a hash of header names and values. For example, you can return only an error header:

head :bad_request

Which would produce the following header:

HTTP/1.1 400 Bad Request

Connection: close

Date: Sun, 24 Jan 2010 12:15:53 GMT

Transfer-Encoding: chunked

Content-Type: text/html; charset=utf-8

X-Runtime: 0.013483

Set-Cookie: _blog_session=…snip…; path=/; HttpOnly

Cache-Control: no-cache

Or you can use other HTTP headers to convey additional information:

或者你可以使用其他的HTTP headers来传递额外信息:

head :created, :location => photo_path(@photo)

Which would produce:这将会生成:

HTTP/1.1 201 Created

Connection: close

Date: Sun, 24 Jan 2010 12:16:44 GMT

Transfer-Encoding: chunked

Location: /photos/1

Content-Type: text/html; charset=utf-8

X-Runtime: 0.083496

Set-Cookie: _blog_session=…snip…; path=/; HttpOnly

Cache-Control: no-cache

3 Structuring Layouts结构布局

When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response:

Rails渲染一个视图作为响应,它这样做了通过联合视图和当前的layout(使用规则来查找当前的layout,它在这个教程的早期被介绍了)。在一个layout中,你访问三种工具来整合不同的输出块,以形成整体的回应:

  • Asset tags
  • yield and content_for
  • Partials

3.1 Asset Tags标签资源

Asset tags provide methods for generating HTML that links views to feeds, JavaScript, stylesheets, images, videos and audios. These are the six asset tags available in Rails:

标签资产提供方法来创建HTML链接视图到feedsJavaScriptstylesheetsimagesvideosaudiosRails中有六个asset tags可用。

  • auto_discovery_link_tag
  • javascript_include_tag
  • stylesheet_link_tag
  • image_tag
  • video_tag
  • audio_tag

You can use these tags in layouts or other views, although the tags other than image_tag are most commonly used in the <head> section of a layout.

你可以使用这些tagslayout或者其他的views,即使tags image_tag超过通常使用的在layout<head>部分使用。

The asset tags do not verify the existence of the assets at the specified locations; they simply assume假设that you know what you’re doing and generate the link.

Asset tags并不验证asstes存在的性在specified locations;他们假设你知道他们正在做的什么并且生成链接。

3.1.1 Linking to Feeds with auto_discovery_link_tag

The auto_discovery_link_tag helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (:rss or :atom), a hash of options that are passed through to url_for, and a hash of options for the tag:

对于auto_discovery_link_tag helper创建HTML,那些浏览器和新读者能够检测存在的RSS或者ATOM。它带有link类型的参数为(:rss or :atom),一个hash字典的选项可以通过它来指定url_for以及taghash选项。

<%= auto_discovery_link_tag(:rss, {:action => “feed”},

{:title => “RSS Feed”}) %>

There are three tag options available for auto_discovery_link_tag:

  • :rel specifies the rel value in the link (defaults to “alternate”)
  • :type specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically.
  • :title specifies the title of the link

MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

3.1.2 Linking to JavaScript Files with javascript_include_tag

The javascript_include_tag helper returns an HTML script tag for each source provided. Rails looks in public/javascripts for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include public/javascripts/main.js:

javascript_include_tag helper返回一个HTML script tag给每个提供的源。Rails默认在public/javascripts查找这些文件,但是你可以指定一个全路径关联到文档目录,或者一个URL,如果你喜欢。例如,要包含public/javascripts/main.js:

<%= javascript_include_tag “main” %>

To include public/javascripts/main.js and public/javascripts/columns.js:

<%= javascript_include_tag “main”, “columns” %>

To include public/javascripts/main.js and public/photos/columns.js:

<%= javascript_include_tag “main”, “/photos/columns” %>

To include http://example.com/main.js:

<%= javascript_include_tag “http://example.com/main.js” %>

If the application does not use the asset pipeline, the :defaults option loads jQuery by default:

如果应用程序没有使用asset管道,:defautls选项默认导入jQuery

<%= javascript_include_tag :defaults %>

And you can in any case override the expansion in config/application.rb:

并且你可以随意覆盖这个扩展在config/application.rb

config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)

When using :defaults, if an application.js file exists in public/javascripts it will be included as well at then end.

如果使用:defaults,如果一个application.js文件存在于public/javascripts他将会同样被导入在随后。

Also, the :all option loads every JavaScript file in public/javascripts:(不包含子目录)

<%= javascript_include_tag :all %>

Note that your defaults of choice will be included first, so they will be available to all subsequently随后included files.

注意你默认的选择将会首先被inclueded,因此他们将于随后导入所有的文件。

You can supply the :recursive option to load files in subfolders of public/javascripts as well:

你也可以提供:recursive(递归)选项来导入在public/javascripts 子文件夹中的文件:

<%= javascript_include_tag :all, :recursive => true %>

If you’re loading multiple JavaScript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify :cache => true in your javascript_include_tag:

如果你正在导入多个的JavaScript文件,你可以创建一个更好的用户体验通过整合多个文件到一个单独的下载(all.js)。要得到这样的效果在产品中,在你的javascript_include_tag指定:cache => true

<%= javascript_include_tag “main”, “columns”, :cache => true %>

You can even use dynamic paths such as cache/#{current_site}/main/display.

你可以使用多元路径例如cache/#{current_site}/main/display

3.1.3 Linking to CSS Files with stylesheet_link_tag

The stylesheet_link_tag helper returns an HTML <link> tag for each source provided. Rails looks in public/stylesheets for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include public/stylesheets/main.css:

stylesheet_link_tag helper返回一个HTML <link>标签给每一个提供的源。Rails默认在public/stylesheets查找这些文件,但是你可以指定一个全路径关联到文档目录,或者一个URL,如果你喜欢。例如,要导入public/stylesheets/main.css:

<%= stylesheet_link_tag “main” %>

To include public/stylesheets/main.css and public/stylesheets/columns.css:

<%= stylesheet_link_tag “main”, “columns” %>

To include public/stylesheets/main.css and public/photos/columns.css:

<%= stylesheet_link_tag “main”, “/photos/columns” %>

To include http://example.com/main.css:

<%= stylesheet_link_tag “http://example.com/main.css” %>

By default, stylesheet_link_tag creates links with media=“screen” rel=“stylesheet” type=“text/css”. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):

默认情况,stylesheet_link_tag创建links使用media=“screen” rel=“stylesheet” type=“text/css”。你可以覆盖这些默认设置的任何一个通过指定一个适当的选项(:media, :rel, or :type):

<%= stylesheet_link_tag “main_print”, :media => “print” %>

The all option links every CSS file in public/stylesheets:(不包含子目录)

<%= stylesheet_link_tag :all %>

You can supply the :recursive option to link files in subfolders of public/stylesheets as well:(递归导入public/stylesheets所有的样式

<%= stylesheet_link_tag :all, :recursive => true %>

If you’re loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify :cache => true in your stylesheet_link_tag:(使用cache来改善用户体验)

<%= stylesheet_link_tag “main”, “columns”, :cache => true %>

By default, the combined file will be delivered as stylesheets/all.css. You can specify a location for the cached asset file instead:

默认情况下组合文件会交付为stylesheets/all.css。你可以指定一个lacationcached asset文件替代:

<%= stylesheet_link_tag “main”, “columns”,

:cache => ‘cache/main/display’ %>

You can even use dynamic paths such as cache/#{current_site}/main/display.

3.1.4 Linking to Images with image_tag

The image_tag helper builds an HTML <img /> tag to the specified file. By default, files are loaded from public/images.

image_tag helper对于指定的文件创建一个HTML <img />标签。默认情况,文件会从public/images被导入。

Notethatyoumustspecifytheextensionoftheimage.PreviousversionsofRailswouldallowyoutojustusetheimagenameandwouldappend.pngifnoextensionwasgivenbutRails3.0doesnot.

注意你必须指定图像文件的扩展名。以前版本的Rails能够允许你仅仅使用图像文件名称就会被添加.png即使没有扩增名被给出。但是Rails3.0不这样了

<%= image_tag “header.png” %>

You can supply a path to the image if you like:

你可以给image提供一个路径如果你喜欢:

<%= image_tag “icons/delete.gif” %>

You can supply a hash of additional HTML options:

<%= image_tag “icons/delete.gif”, {:height => 45} %>

You can also supply an alternate image to show on mouseover:你可以提供一个在鼠标经过的备用图像。

<%= image_tag “home.gif”, :onmouseover => “menu/home_highlight.gif” %>

You can supply alternate text for the image which will be used if the user has images turned off in their browser. If you do not specify an alt text explicitly, it defaults to the file name of the file, capitalized and with no extension. For example, these two image tags would return the same code:

你可以提供一个备用文字给image它们将在用户的浏览器关闭图像(显示)的时候使用。如果你不准确的指定一个alt文本,它默认是文件的文件名,大写(首字母)并且没有扩展名。例如,下面的两个image标签将会返回同样的代码:

<%= image_tag “home.gif” %>

<%= image_tag “home.gif”, :alt => “Home” %>

You can also specify a special size tag, in the format “{width}x{height}”:

<%= image_tag “home.gif”, :size => “50x20” %>

In addition to the above special tags, you can supply a final hash of standard HTML options, such as :class, :id or :name:

<%= image_tag “home.gif”, :alt => “Go Home”,

:id => “HomeImage”,

:class => ‘nav_bar’ %>

3.1.5 Linking to Videos with video_tag

The video_tag helper builds an HTML 5 <video> tag to the specified file. By default, files are loaded from public/videos.

video_tag helper给指定文件创建一个HTML 5 <video>标签。默认的,文件会从public/videos被导入。

<%= video_tag “movie.ogg” %>

Produces生成

<video src=“/videos/movie.ogg” />

Like an image_tag you can supply a path, either absolute, or relative to the public/videos directory. Additionally you can specify the :size => “#{width}x#{height}” option just like an image_tag. Video tags can also have any of the HTML options specified at the end (id, class et al).

The video tag also supports all of the <video> HTML options through the HTML options hash, including:

video标签同样支持所有的<video> HTML选项通过HTML选项的hash字典,包含:

  • :poster => ‘image_name.png’, provides an image to put in place of the video before it starts playing.
  • :autoplay => true, starts playing the video on page load.
  • :loop => true, loops the video once it gets to the end.
  • :controls => true, provides browser supplied controls for the user to interact with the video.
  • :autobuffer => true, the video will pre load the file for the user on page load.

You can also specify multiple videos to play by passing an array of videos to the video_tag:

<%= video_tag [“trailer.ogg”, “movie.ogg”] %>

This will produce:

<video><source src=“trailer.ogg” /><source src=“movie.ogg” /></video>

3.1.6 Linking to Audio files with audio_tag

The audio_tag helper builds an HTML 5 <audio> tag to the specified file. By default, files are loaded from public/audios.

<%= audio_tag “music.mp3” %>

You can supply a path to the audio file if you like:

<%= audio_tag “music/first_song.mp3” %>

You can also supply a hash of additional options, such as :id, :class etc.

Like the video_tag, the audio_tag has special options:

  • :autoplay => true, starts playing the audio on page load
  • :controls => true, provides browser supplied controls for the user to interact with the audio.
  • :autobuffer => true, the audio will pre load the file for the user on page load.

3.2 Understanding yield未知的yield

Within the context of a layout, yield identifies a section where content from the view should be inserted. The simplest way to use this is to have a single yield, into which the entire contents of the view currently being rendered is inserted:

layoutcontext(上下文)中,yield表示一节来自view的内容将会被插入。最简单的方法来使用这个是有一个yield,视图的整个内容将会被rendered 插入。

<html>

<head>

</head>

<body>

<%= yield %>

</body>

</html>

You can also create a layout with multiple yielding regions:你也可以创建一个layout有多个yield区域。

<html>

<head>

<%= yield :head %>

</head>

<body>

<%= yield %>

</body>

</html>

The main body of the view will always render into the unnamed yield. To render content into a named yield, you use the content_for method.

视图的body将会总是render一个unnamed yield。要render内容给一个知名的yield,使用content_for方法。

3.3 Using content_for

The content_for method allows you to insert content into a named yield block in your layout. For example, this view would work with the layout that you just saw:

content_for允许你插入内容到知名的yield块到你的layout

<% content_for :head do %>

<title>A simple page</title>

<% end %>

 

<p>Hello, Rails!</p>

The result of rendering this page into the supplied layout would be this HTML:

<html>

<head>

<title>A simple page</title>

</head>

<body>

<p>Hello, Rails!</p>

</body>

</html>

The content_for method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It’s also useful for inserting tags that load page-specific JavaScript or css files into the header of an otherwise generic layout.

3.4 Using Partials

Partial templates – usually just called “partials” – are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.

Partial templates-通常被称为partials”-是另一个设备(单元)分解渲染过程到易于管理的多个块中。通过一个partial,你可以渲染了一个特定块的文件,通过respense这个文件来响应特定的代码。

3.4.1 Naming Partials命名Partials

To render a partial as part of a view, you use the render method within the view:

<%= render “menu” %>

This will render a file named _menu.html.erb at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore下划线to distinguish区分them from regular views, even though they are referred to without the underscore. This holds true even when you’re pulling in a partial from another folder:

这将会render一个名为menu.html.erb的文件在渲染视图的Partials插入点的时候。注意起始的下划线字符:partials被命名为以下划线()开始的规则来区分它们和视图,即使他们(的调用没有下划线)。这在你从除当前视图所在文件夹之外调入也一样成立:

<%= render “shared/menu” %>

That code will pull in the partial from app/views/shared/_menu.html.erb.

3.4.2 Using Partials to Simplify Views

One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what’s going on more easily. For example, you might have a view that looked like this:

<%= render “shared/ad_banner” %>

 

<h1>Products</h1>

 

<p>Here are a few of our fine products:</p>

 

<%= render “shared/footer” %>

Here, the ad_banner.html.erb and footer.html.erb partials could contain content that is shared among many pages in your application. You don’t need to see the details of these sections when you’re concentrating on a particular page.

For content that is shared among all pages in your application, you can use partials directly from layouts.

For content that is shared among all pages in your application, you can use partials directly from layouts.

3.4.3 Partial Layouts

A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this:

一个partial可以使用它们自己的layout文件,就像一个视图可以使用一个layout。例如你可以这样调用一个partial

<%= render :partial => “link_area”, :layout => “graybar” %>

This would look for a partial named link_area.html.erb and render it using the layout graybar.html.erb. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master layouts folder).

这将会查找一个名叫link_area.html.erbpartial并且在graybar.html.erbrender和使用它。

Also note that explicitly specifying :partial is required when passing additional options such as :layout.

3.4.4 Passing Local Variables

You can also pass local variables into partials, making them even more powerful and flexible. For example, you can use this technique to reduce duplication between new and edit pages, while still keeping a bit of distinct content:

  • new.html.erb

<h1>New zone</h1>

<%= error_messages_for :zone %>

<%= render :partial => “form”, :locals => { :zone => @zone } %>

  • edit.html.erb

<h1>Editing zone</h1>

<%= error_messages_for :zone %>

<%= render :partial => “form”, :locals => { :zone => @zone } %>

  • _form.html.erb

<%= form_for(zone) do |f| %>

<p>

<b>Zone name</b><br />

<%= f.text_field :name %>

</p>

<p>

<%= f.submit %>

</p>

<% end %>

Although the same partial will be rendered into both views, Action View’s submit helper will return “Create Zone” for the new action and “Update Zone” for the edit action.

Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the :object option:

<%= render :partial => “customer”, :object => @new_customer %>

Within the customer partial, the customer variable will refer to引用@new_customer from the parent view.

In previous versions of Rails, the default默认情况local variable would look for an instance variable with the same name as the partial in the parent. This behavior was deprecated增加in 2.3 and has been removed in Rails 3.0.(可以自己添加如上)

If you have an instance of a model to render into a partial, you can use a shorthand syntax:

<%= render @customer %>

Assuming假设that the @customer instance variable contains an instance of the Customer model, this will use _customer.html.erb to render it and will pass the local variable customer into the partial which will refer to the @customer instance variable in the parent view.

假设@customer实例变量包含在一个Customer的实例中,这将会使用_customer.html.erbrender它并且传递本地变量customerpartial引用@customer实例变量的父视图。

3.4.5 Rendering Collections

Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection:

  • index.html.erb

<h1>Products</h1>

<%= render :partial => “product”, :collection => @products %>

  • _product.html.erb

<p>Product Name: <%= product.name %></p>

When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is product, and within the product partial, you can refer to product to get the instance that is being rendered.

当一个partial被通过一个多元的collection调用,然后partial 的个别的实例会访问collection的成员并开始被rendered通过一个在partial后面的变量名。在本例中the partial is product,并且在product partial中,你可以引用product来得到被开始渲染的实例。

In Rails 3.0, there is also a shorthand for this. Assuming @products is a collection of product instances, you can simply write this in the index.html.erb to produce the same result:

Rails 3.0中有一个快捷操作。假设@products是一个product实例的集合,你可以简单的在index.html.erb中写入下面的代码来产生相同的结果:

<h1>Products</h1>

<%= render @products %>

Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous合成collection and render it this way, and Rails will choose the proper partial for each member of the collection:

  • index.html.erb

<h1>Contacts</h1>

<%= render [customer1, employee1, customer2, employee2] %>

  • customers/_customer.html.erb

<p>Customer: <%= customer.name %></p>

  • employees/_employee.html.erb

<p>Employee: <%= employee.name %></p>

In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.

3.4.6 Local Variables

To use a custom local variable name within the partial, specify the :as option in the call to the partial:

<%= render :partial => “product”, :collection => @products, :as => :item %>

With this change, you can access an instance of the @products collection as the item local variable within the partial.你可以访问item本地变量来访问@products collection的实例。

 

You can also pass in arbitrary local variables to any partial you are rendering with the :locals => {} option:

<%= render :partial => ‘products’, :collection => @products,

:as => :item, :locals => {:title => “Products Page”} %>

Would render a partial _products.html.erb once for each instance of product in the @products instance variable passing the instance to the partial as a local variable called item and to each partial, make the local variable title available with the value Products Page.

Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by _counter. For example, if youre rendering @products, within the partial you can refer to product_counter to tell you how many times the partial has been rendered. This does not work in conjunction结合with the :as => :value option.

You can also specify a second partial to be rendered between instances of the main partial by using the :spacer_template option:

3.4.7 Spacer Templates间隔templates

<%= render @products, :spacer_template => “product_ruler” %>

Rails will render the product_ruler partial (with no data passed in to it) between each pair of product partials.

3.5 Using Nested Layouts使用嵌套layouts

You may find that your application requires a layout that differs slightly略有不同from your regular application layout to support one particular controller. Rather than repeating the main layout and editing it, you can accomplish完成this by using nested layouts (sometimes called sub-templates). Here’s an example:

Suppose假设you have the following ApplicationController layout:

  • app/views/layouts/application.html.erb

<html>

<head>

<title><%= @page_title or ‘Page Title’ %></title>

<%= stylesheet_link_tag ‘layout’ %>

<style type=“text/css”><%= yield :stylesheets %></style>

</head>

<body>

<div id=“top_menu”>Top menu items here</div>

<div id=“menu”>Menu items here</div>

<div id=“content”><%= content_for?(:content) ? yield(:content) : yield %></div>

</body>

</html>

On pages generated by NewsController, you want to hide the top menu and add a right menu:

  • app/views/layouts/news.html.erb

<% content_for :stylesheets do %>

top_menu {display: none}

right_menu {float: right; background-color: yellow; color: black}

<% end %>

<% content_for :content do %>

<div id=“right_menu”>Right menu items here</div>

<%= content_for?(:news_content) ? yield(:news_content) : yield %>

<% end %>

<%= render :template => ‘layouts/application’ %>

<表达式1>?<表达式2>:<表达式3>; “?”运算符的含义是: 先求表达式1的值, 如果为真, 则执行表达式2,并返回表达式2的结果; 如果表达式1的值为假, 则执行表达式3 ,并返回表达式3的结果.

That’s it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the “content” div.

There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the ActionView::render method via render :template => ‘layouts/news’ to base a new layout on the News layout. If you are sure you will not subtemplate the News layout, you can replace the content_for?(:news_content) ? yield(:news_content) : yield with simply yield.

 

Python中的编码问题

python中的编码问题

这里简单的说一下。(下面内容基本上时从《Python.Core.Programming.2ed》上摘的) Unicode是计算机可以支持这个星球上的多种语言的秘密武器,在Unicode之前,用的都是ASCII,ASCII吗非常简单,每个英文字符都用7位二进制数的方式存储在计算机内,其范围是32到126.它的实现原理这里也不说了。 但是ASCII码只能表示95个可打印的字符,后来把ASCII扩展到了8位,这样就能表示223个字符了,虽然这个来表示欧美字母语言已经足够了,但是对于像中文等语系来说就太少了。于是Unicode码诞生了。 Unicode通过使用一个或者多个字节来表示一个字符,这样就突破了ASCII的限制,这样,Unicode可以表示超过90000个字符了。   Python 与Unicode   为了让Unicode和ASCII码值的字符串看起来尽可能的相像,Python的字符串从原来的简单数据类型改变成了真正的对象,ASCII字符串成了StringType,而Unicode字符串成了UnicodeType类型,他们的行为非常相近。String模块里面都有相应的处理函数。String模块已经停止了更新,只保留了对ASXII码的支持,string模块已经不推荐使用,在任何要跟Unicode兼容的代码里都不要再用该模块,Python保留该模块仅仅为了向后兼容。 Python里面默认所有字面上的字符串都用ASCII编码,可以通过在字符串前面加一个‘u’前缀的方式声明Unicode字符串,这个‘u’前缀告诉Python后面的字符串要编成Unicode字符串。   >>> “Hello World” #ASCII string ‘Hello World’ >>> u”Hello World” #Unicode string u’Hello World’   内建的str()函数和chr()函数不能处理Unicode,它们只能处理常规ASCII编码的字符串,如果一个Unicode字符串作为参数传给了str()函数,它会首先被转换成ASCII码字符串然后交给str()函数。   Codecs   Codec是把Coder/DECoder得首字母组合,它定义了文本跟二进制的转换方式,跟ASCII那种用一个字节把字符转换成数字的方式不同,Unicode用的是多字节,这导致了Unicode支持多种不同的编码方式,比如说codec支持的四种耳熟能详的编码方式是:ASCII,ISO8859—1/Latin-1,UTF-8,和UTF-16 最著名的是UTF-8编码,它也用一个字节来编码ASCII字符,这让那些必须同时处理ASCII码和Unicode码文本的程序员的工作变得非常轻松,因为ASCII字符的UTF-8编码和ASCII编码完全相同。 UTF-8编码可以用1到4个字节来表示其他语言的字符,这给那些需要直接处理Unicode数据的程序员带来了麻烦,因为他们没有办法按照固定长度逐一读出各个字符,幸运的是我们不需要掌握直接读取Unicode数据的方法,Python已经替我们完成了相关细节,我们无需为处理多字节字符的复杂问题而担心。 UTF-16也是一种变长编码,但是它不常用。   编码解码   Unicode支持多种编码格式,这为程序员带来了额外的负担,每当你向一个文件写入字符串的时候,你必须定义一个编码用于把对应的Unicode内容转换成你定义的格式,Python通过Unicode字符串的encode()函数解决了这个问题,该函数接受字符串中的字符为参数,输出你指定的编码格式的内容。 所以,每次我们写一个Unicode字符串到磁盘上我们都要用指定的编码器给他“编码“一下,相应地,当我们从这个文件读取数据时,我们必须”解码”该文件,使之成为Unicode字符串对象。 简单的例子:

#!/usr/bin/env python # -*- coding: UTF-8 -*- import chardet #chardet.detect(u'啊啊算法') def u(s, encoding): if isinstance(s, unicode):#用来判断是否为unicode return s else: try: return unicode(s, encoding)# 不是的话就转成unicode except IOError: print('error\n')#s=r’\u6df1\u5733\u71c3\u6c14\u4e2d\u7b7e\u7387’ #zh = reduce(lambda x,y: x + unichr(int(y, 16)), s.split(r”\u”)) #print zh def jiema(getstring): fstr=r”” list_str=getstring.split(r’\u’) for single in list_str: if single != ”: fstr+=unichr(int(single,16)) print fstrif __name__==’__main__’: coding=[‘utf’,’ascii’,’gb2312’,’gbk’] a=’\u8bf7\u91cd\u65b0\u767b\u5f55’#这里没有加u得到的 就是ascii码 #if chardet.detect(u’啊啊算法’) if chardet.detect(a)[‘encoding’]==’ascii’: print “beging to call jiema ….” jiema(a) else: print “beging to zhuanma” for code in coding: print code print u(a,code) print “”“Python里面默认所有字面上的字符串都用ASCII编码,可以通过在字符串前面加一个‘u’前缀的方式声明Unicode字符串,这个‘u’前缀告诉Python后面的字符串要编成Unicode字符串。”“” print “”“ASCII 是一种字符集,包括大小写的英文字母、数字、控制字符等,它用一个字节表示,范围是 0-127Unicode分为UTF-8和UTF-16。UTF-8变长度的,最多 6 个字节,小于 127 的字符用一个字节表示,与 ASCII 字符集的结果一样,ASCII 编码下的英语文本不需要修改就可以当作 UTF-8 编码进行处理。”“”
标签: python

MAC OS快捷键

MAC OS快捷键

全局 Cmd-C 复制文件 Cmd-V 粘贴文件 Option-拖动复制文件到新地址 Cmd-拖动移动并自动对齐 Cmd-Delete 删除 Cmd-Option-拖动做替身(快捷方式) Cmd-Shift-Delete 清空垃圾桶 Cmd-Shift-Option-Delete 强制清空垃圾桶 Tab 选定下一个项目 Shift-Tab 选定上一个项目 Return 执行默认动作 Escape 关闭对话框 Page Up 向上翻页 向上箭头选取上一个文件 Page Down 向下翻页 向下箭头选取下一个文件 Cmd-Shift-G 打开’前往文件夹’对话框 Cmd-句号[.] 关闭对话框 Exposé 和系统的快捷 F8 切换Space Shift-F8 慢速切换Space F9(默认设置)使用Exposé 显示所有打开的窗口 F10(默认设置)使用Exposé 在某个应用程序中显示所有打开的窗口 F11(默认设置)使用Exposé 隐藏所有打开的窗口并显示桌面 Cmd-H 隐藏程序 Cmd-Option-H 隐藏其他程序 Cmd-Q 退出程序 Cmd-Shift-Q 退出所有程序并且注销用户 Cmd-Option-Shift-Q 强制注销用户 Cmd-Tab 切换到下一个应用程序 Cmd-Shift-Tab 切换到上一个应用程序 Cmd-拖动整理菜单栏 按下Option 键并点按一个窗口切换窗口并隐藏当前窗口 按住Option 并点按Dock 图标切换到另一个应用程序并隐藏当前应用程序 按下Control 键并点按该项查看某个项的快捷(上下文)菜单 将光标移到该词上面,然后按Cmd-Control-D 使用Dictionary 查看对字词在应用程序中的定义 停止响应 Cmd-句号[.] 停止进程 Cmd-Option-Escape 打开’强制退出’ 电源键关机 Cmd-Option-Shift-电源键强制关机或重新启动(在某些电脑上) Cmd-Control-电源键强制重启 Finder Cmd-点击标题查看当前窗口的路径 Cmd-双击(文件夹上) 新窗口中打开文件夹 Option-双击(文件夹上) 新窗口中打开文件夹并关闭当前窗口 Cmd-1 用图标浏览 Cmd-2 用列表浏览 Cmd-Option-向右箭头列表模式下显示包含的目录 向左箭头列表模式下关闭选定目录 Cmd-向下箭头在图标或列表模式下打开选定目录 Cmd-Option-向下箭头在图标或列表模式下在新窗口打开选定目录并关闭当前窗口 Cmd-Shift-Option-向下箭头(慢速)在图标或列表模式下在新窗口打开选定目录并关闭当前窗口 Cmd-向上箭头打开上一级目录 Cmd-Option-向上箭头打开上一级目录并关闭当前目录 Cmd-3 用分栏浏览 Cmd-4 用cover flow浏览 Cmd-Y 打开快速查看 Cmd-Option-Y 用幻灯片显示 Cmd-Shift-H 打开用户文件夹 Cmd-Option-Shift-向上箭头聚焦桌面 Cmd-Shift-I 打开iDisk Cmd-Shift-D 打开桌面 Cmd-Shift-C 打开’电脑’ Cmd-Shift-K 打开网络 Cmd-Shift-A 打开应用程序 双击标题最小化窗口 Cmd-M 最小化窗口 Option-点击按钮应用到所有激活的窗口 按下并按住滚动条快速浏览长文稿 按住Option 键并点按滚动条迅速在“滚动到当前位置”和“滚动到页面”之间切换 Cmd-波浪符号(~) 激活当前应用程序中的上一个或下一个窗口 Dock 拖动分割线自定义Dock大小 Option-拖动分割线调整Dock到合适大小 Control-点击显示Dock快捷菜单 Control-点击图标显示项目的快捷菜单 Cmd-点击打开图标所在文件夹 Option-点击切换并隐藏当前程序 Cmd-Option-点击切换并隐藏所有程序 Cmd-Option-拖动强制程序打开文件 Cmd-Option-D 显示/隐藏Dock 启动 *快捷键只能在启动时使用 当您看到进程指示器(看起来像旋转的齿轮)时,请按住左边的Shift 键。防止自动登录 听到启动音之后立即按住Shift 键,然后当您看到进程指示器(看起来像旋转的齿轮)时释放该键。以安全模式启动(只 有必要的Mac OS X 项被启动,一些功能和应用程序可能无法正常工作。) 在登录屏幕上点按“登录”按钮之后,请按住Shift 键。防止登录时打开“登录项”和Finder 窗口 C 从光盘启动 N 从默认的NetBoot 磁盘映像启动 T 以目标磁盘模式启动 Option 选择启动磁盘(在某些电脑上) Cmd-X 使用Mac OS X 而不是Mac OS 9 来进行启动(如果两者均位于同一宗卷上) 按住鼠标键推出可去掉的光盘 Cmd-Option-P-R 还原参数RAM Cmd-V 显示详细的状态信息(详细模式) Cmd-S 以单一用户模式启动 Safari Cmd-Option-F google搜索栏 Option-向上箭头向上翻页 Option-向下箭头向下翻页 Cmd-点击链接在后台用新标签打开 Cmd-Shift-点击链接打开并激活新标签 Cmd-Option-点击链接打开新窗口 Option-点击Close 按钮关闭其他标签 Cmd-Shift-] 选取下一个标签 Cmd-Shift-[ 选取上一个标签 Cmd-Shift-H 打开主页 Cmd-Shift-K 切换’禁止弹出窗口’ Cmd-Option-E 清空缓存 Cmd-Option-R 不用缓存并刷新页面 Cmd-F 查找 Cmd-M 最小化窗口 Shift-点击按钮慢动作动画效果 Cmd-加号[+] 增大字体 Cmd-减号[-] 减小字体 Cmd-0 默认字体 Dashboard 使用这些快捷来处理Dashboard 和Dashboard widget。 F12(默认设置)显示或隐藏Dashboard Cmd-R 重新载入当前widget Cmd-等号(=) 显示或隐藏widget 栏 Cmd-向左箭头键,Cmd-向右箭头键滚动widget 栏 注:要更改Dashboard 的快捷,请选取“文件”>“系统偏好设置”,点按“Exposé & Spaces”,然后点按“Exposé”。 Front Row 您可以使用键盘来控制Front Row 而无需使用Apple Remote 遥控器。 Cmd-Esc (Escape) 打开Front Row Cmd-Esc 或Esc 从打开的菜单中关闭Front Row 向上箭头键,向下箭头键浏览菜单和列表 Cmd-Esc 或Esc 返回上一级菜单 空格键或Return 选择菜单或列表中的项 空格键或Return 播放和暂停音频或视频 向上箭头键,向下箭头键更改音量 向右箭头键,向左箭头键前往下一个或上一个歌曲或照片 向右箭头键,向左箭头键前往所播放DVD 的下一章或上一章 右箭头键,左箭头键(按住按钮)快进或倒回歌曲、视频或DVD 在某些Apple 键盘和便携式电脑上,您或许也可以使用特定按键来更改音量和控制回放。 键盘导航 Control-F1 打开/关闭全键盘控制 Control-F2 聚焦菜单栏 Control-F3 聚焦Dock Control-F4 聚焦活跃窗口或下一个窗口 Control-F5 聚焦窗口工具栏 Control-F6 聚焦浮动窗口 Control-F7 在控制或文本框与列表之间移动 Control-F8 聚焦菜单栏中的状态菜单 Cmd-Accent [`] 聚焦活跃应用程序的下一个窗口 Cmd-Shift-Accent [`] 聚焦活跃应用程序的上一个窗口 Cmd-Option-Accent [`] 聚焦窗口抽屉 Cmd-Option-T 显示或隐藏字符调板
标签: apple mac shortcuts

Active Record-Query Interface-CN

Active Record-Query Interface-CN

Active Record

Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。Active Record Row Gateway (行记录入口)十分相似,但前者是领域模型,后者是一种数据源模式。关系型数据库往往通过外键来表述实体关系,Active Record 在数据源层面上也将这种关系映射为对象的关联和聚集。

  Active Record 适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合Data Mapper (数据映射器)使用。

  Active Record 驱动框架一般兼有ORM 框架的功能,但ActivActive Recorde Record 不是简单的ORM,正如和Row Gateway 的区别。著名的例子是全栈(Full StackWeb 开发框架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:migrateActive 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 框架来处理数据迁移,这里笔者把Active Record 框架放在一个地方学习了,如需了解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 框架来处理验证和回调,这里笔者把Active Record 框架放在一个地方学习了,如需了解Migration部分需要直接阅读Validations and Callbacks 部分。

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 Recordmodels中声明(它们的)关系
  • Understand the various types of Active Record associations 明白各种类型的Active Record关系
  • Use the methods added to your models by creating associations 通过添加方法到models(的形式)来创建关系

Active Record Query Interface(基于)活动记录的查询接口

This guide covers different ways to retrieve data from the database using Active Record. By referring to this guide, you will be able to:

这个教程涵盖了使用基于Active Record 的不同方式从数据库检索数据。同过参考这个教程,你可以掌握:

  • Find records using a variety of methods and conditions 使用各种方法和条件查找记录
  • Specify the order, retrieved attributes, grouping, and other properties of the found records对查找的记录指定顺序,检索属性,组,和其他属性
  • Use eager急于loading to reduce the number of database queries needed for data retrieval 使用预先加载,以减少数据检索所需的数据库查询数量
  • Use dynamic finders methods 使用多元搜索方法
  • Check for the existence of particular records 在特定的记录部分检查存在的记录
  • Perform various calculations on Active Record models Active Record 模型中执行各种计算

ThisGuideisbasedonRails3.0.SomeofthecodeshownherewillnotworkinotherversionsofRails.

If you’re used to using raw SQL to find database records then, generally, you will find that there are better ways to carry out进行the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.如果你使用过原生的SQL(语句)来查询数据库,那么,一般情况下,你将会发现(对数据库)进行同样的操作在Rails中会有这更好的方法。Active Record在大多数情况下会让你远离你(以前)需要使用的SQL查询语句。

Code examples throughout this guide will refer to one or more of the following models:贯穿这个教材代码示例将会参考一个或多个下面的模型:

All of the following models use id as the primary key, unless specified otherwise.所有的模型都会使用id作为主键,除非指定了其他主键。

class Client < ActiveRecord::Base

has_one :address

has_many :orders

has_and_belongs_to_many :roles

end

 

class Address < ActiveRecord::Base

belongs_to :client

end

 

class Order < ActiveRecord::Base

belongs_to :client, :counter_cache => true

end

 

class Role < ActiveRecord::Base

has_and_belongs_to_many :clients

end

Active Record will perform queries on the database for you and is compatible兼容with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you’re using, the Active Record method format will always be the same.

Active Record将会为你在数据库中执行查询并且它兼容大多数的数据库系统(MySQL, PostgreSQL and SQLite这里仅仅列举这些)。不管你使用的是何种数据库系统,Active Record的方法格式通常是相同的。

1 Retrieving Objects from the Database在数据库中检索对象

To retrieve objects from the database, Active Record provides several finder methods. Each finder method allows you to pass arguments into it to perform certain queries on your database without writing raw SQL.

为了从数据库中检索对象,Active Record提供了一些查询方法。每个查询方法都运行你带入参数并在数据库中执行查询而不用写SQL自身的语句。

The methods are:

  • where
  • select
  • group
  • order
  • reorder
  • reverse_order 逆向排序
  • limit
  • offset 偏移
  • joins
  • includes
  • lock
  • readonly
  • from
  • having

All of the above methods return an instance of ActiveRecord::Relation.所有以上方法会返回一个ActiveRecord::Relation的实例。

Primary operation of Model.find(options) can be summarized as:

Model.find(options)主要的操作可以被概括为:

  • Convert the supplied options to an equivalent SQL query. 转换提供的(查询)选项为等同的SQL查询
  • Fire the SQL query and retrieve the corresponding results from the database. 开始SQL查询并且检索从数据库相应的结果
  • Instantiate the equivalent Ruby object of the appropriate model for every resulting row. 把每个(数据库中原生的)结果实例化等同的Ruby对象
  • Run after_find callbacks if any. 运行after_find进行回调如果需要

1.1 Retrieving a Single Object检索单个对象

Active Record lets you retrieve a single object using five different ways.

Active Record让你可以使用五种不同的方法检索单个对象。

1.1.1 Using a Primary Key使用主键查询

Using Model.find(primary_key), you can retrieve the object corresponding to the supplied primary key and matching the supplied options (if any). For example:

使用Model.find(primary_key),你可以检索对象通过提供相应的主键或者匹配提供的其他选项(如果存在)。例如:

# Find the client with primary key (id) 1.

rails console

Loading development environment (Rails 3.1.1)

irb(main):001:0> p=Post.find(1)

Post Load (9.0ms) SELECT “posts”.* FROM “posts” WHERE “posts”.“id” = ? LIMIT 1 [[“id”, 1]]

=> #<Post id: 1, name: “阿飞姐姐”, title: “接口姐姐”, content: “12342424”, created_at: “2011-11-05 15:10:41”, updated_at: “2011-11-05 15:10:41”>

SQL equivalent of the above is:

SELECT * FROM posts WHERE (posts.id = 1)

Model.find(primary_key) will raise an ActiveRecord::RecordNotFound exception if no matching record is found.

Model.find(primary_key)如果没有记录匹配则会抛出一个ActiveRecord::RecordNotFound异常。

1.1.2 first

Model.first finds the first record matched by the supplied options. For example:

Model.first找到与提供的选项匹配的第一条记录。例如:

client = Client.first

=> #<Client id: 1, first_name: “Lifo”>

irb(main):018:0> Post.first

Post Load (0.4ms) SELECT “posts”.* FROM “posts” LIMIT 1

=> #<Post id: 2, name: “jhjguxin”, title: “test console”, content: “A new post to test console”, created_at: “2011-11-05 15:55:17”, updated_at: “2011-11-05 15:55:17”

SQL equivalent of the above is:

SELECT * FROM clients LIMIT 1

Model.first returns nil if no matching record is found. No exception will be raised.

如果没有记录匹配Model.first会返回nil。不会抛出异常。

1.1.3 last

Model.last finds the last record matched by the supplied options. For example:

client = Client.last

=> #<Client id: 221, first_name: “Russel”>

SQL equivalent of the above is:上面等同的SQL语句是:

SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1

##SELECT “posts”.* FROM “posts” ORDER BY “posts”.“id” DESC LIMIT 1

Model.last returns nil if no matching record is found. No exception will be raised.

如果没有记录匹配Model.last会返回nil。不会抛出异常。

1.2 Retrieving Multiple Objects检索多个目标

1.2.1 Using Multiple Primary Keys使用多个主键

Model.find(array_of_primary_key) also accepts an array of primary keys. An array of all the matching records for the supplied primary keys is returned. For example:

Model.find(array_of_primary_key)也接受一个主键数组。将会返回一个由所有与提供的主键匹配的记录组成的数组。例如:

# Find the clients with primary keys 1 and 10.

client = Client.find(1, 10) # Or even Client.find([1, 10])

=> [#<Client id: 1, first_name: => “Lifo”>, #<Client id: 10, first_name: => “Ryan”>]

SQL equivalent of the above is:

SELECT * FROM clients WHERE (clients.id IN (1,10))

Model.find(array_of_primary_key)willraiseanActiveRecord::RecordNotFoundexceptionunlessamatchingrecordisfoundforallofthesuppliedprimarykeys.

Model.find(array_of_primary_key)只要有一条记录没有找到就会抛出ActiveRecord::RecordNotFound异常

ActiveRecord::RecordNotFound:Couldn’tfindallPostswithIDs(2,4,5)(found2results,butwaslookingfor3)

1.3 Retrieving Multiple Objects in Batches分批次的检索多个目标

Sometimes you need to iterate反复重复over a large set of records. For example to send a newsletter to all users, to export some data, etc.

有时候你需要遍历大量的记录,例如发送一条业务通讯给所有的用户,输出一些数据,等等。

The following may seem very straight forward at first:

首先(通过)以下内容看起来会非常直观

# Very inefficient when users table has thousands of rows.

User.all.each do |user|

NewsLetter.weekly_deliver(user)

end

But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain平原impossible.

但是如果(数据)表单的行有非常大的量,上面的方法在执行(的时候)不可能性能(还是那么)平稳。

This is because User.all.each makes Active Record fetch the entire table, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.

这是因为User.all.each使得Active Record获取整个表单,给每一行数据创建一个object model,并且保留整个数组在内存中。有时会有太多的对象并且需要太多的内存。

 

Configuring the batch size配置批次大小

Behind the scenes find_each fetches rows in batches of 1000 and yields them one by one. The size of the underlying batches is configurable via the :batch_size option.在使用find_each获取1000次记录行并且一个接一个的yield它们的情况中。(确定)下面(查找)批次的大小是通过配置:batch_size选项。

To fetch User records in batch size of 5000:

User.find_each(:batch_size => 5000) do |user|

NewsLetter.weekly_deliver(user)

end

 

Starting batch find from a specific primary key通过一个指定的主键开始批次查找

Records are fetched in ascending order on the primary key, which must be an integer. The :start option allows you to configure the first ID of the sequence if the lowest is not the one you need. This may be useful for example to be able to resume an interrupted batch process if it saves the last processed ID as a checkpoint.这会非常有用比如能够减少因为设置最后处理的ID作为checkpoint引起的中断。

(这里)是按照主键值的升序排列获取记录的,主键值必须是整数。:start选项允许你配置序列的开始ID如果排序最低的(记录)不是你需要的。

To send newsletters only to users with the primary key starting from 2000:

User.find_each(:batch_size => 5000, :start => 2000) do |user|

NewsLetter.weekly_deliver(user)

end

Additional options其他(附加)选项

find_each accepts the same options as the regular find method. However, :order and :limit are needed internally and hence not allowed to be passed explicitly.

find_each接受和正则find方法相同的选项。

1.3.2 find_in_batches

You can also work by chunks instead of row by row using find_in_batches. This method is analogous to find_each, but it yields arrays of models instead:

通过使用find_in_batches你也可以用chunks替代数据行。这个方法类似于 find_each,但是作为替代它(会输出)到一个数组区域:

# Works in chunks of 1000 invoices at a time.

Invoice.find_in_batches(:include => :invoice_lines) do |invoices|

export.add_invoices(invoices)

end

The above will yield the supplied block with 1000 invoices every time.上面的语句每次会提供给语句1000invoices

2 Conditions条件

The where method allows you to specify conditions to limit the records returned, representing the WHERE-part of the SQL statement. Conditions can either be specified as a string, array, or hash.

where方法允许你指定条件限制记录返回(的内容),表示SQL部分的WHERE部分。条件可以指定为一个字符串,数组,或者hash(字典)。

2.1 Pure String Conditions纯字符串条件

If you’d like to add conditions to your find, you could just specify them in there, just like Client.where(“orders_count = ‘2’”). This will find all clients where the orders_count fields value is 2.

Building your own conditions as pure strings can leave you vulnerable脆弱to SQL injection注入exploits漏洞. For example, Client.where(“first_name LIKE ‘%#{params[:first_name]}%’”) is not safe. See the next section for the preferred way to handle conditions using an array.

2.2 Array Conditions

Now what if that number could vary, say as an argument from somewhere? The find then becomes something like:

现在,如果这个数字可能会有所不同,(比如说)作为某个地方的一个参数?查找会变成如下:

Client.where(“orders_count = ?”, params[:orders])

Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element.

Active Record将会在第一个元素中表示条件(语句)并且其他元素取代第一个元素中的问号。

Or if you want to specify two conditions, you can do it like:

Client.where(“orders_count = ? AND locked = ?”, params[:orders], false)

In this example, the first question mark will be replaced with the value in params[:orders] and the second will be replaced with the SQL representation of false, which depends on the adapter.

The reason for doing code like:

Client.where(“orders_count = ?”, params[:orders])

instead of:

Client.where(“orders_count = #{params[:orders]}”)

is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database as-is. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.

(这里)是基于参数安全(考虑)。将变量直接放入条件字符串将会(原封不动的传送变量)到数据库。他的意思是这个参数有可能将会是以一个来自用户的恶意的非转义的变量。如果你这样做,你就把整个数据库放在了风险之中,因为一旦有用户找到他们或者它可以利用(漏洞)对你数据库做任何操作。永远不要直接把你的参数放在条件字符串中。

For more information on the dangers of SQL injection, see the RubyonRailsSecurityGuide.

2.2.1 Placeholder Conditions条件(参数)占位符

Similar to the (?) replacement style of params, you can also specify keys/values hash in your array conditions:最简单的是使用(?)替代参数的形式,你也可以指定keys/values hash在你的条件语句数组中:

Client.where(“created_at >= :start_date AND created_at <= :end_date”,

{:start_date => params[:start_date], :end_date => params[:end_date]})

This makes for clearer readability if you have a large number of variable conditions.当你有大量的条件变量时这样表示更加简洁和可读性更好。

2.2.2 Range Conditions范围条件

If you’re looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the IN SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:

如果你正在一个表中限定一个条件范围查找(例如,用户限定在一定的时间表中创建)你可以使用条件选项加上(这个参数的)IN SQL声明。如果有来自一个controller的两个日期,你可以做些事情查找一个范围:

Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date))

This query will generate something similar to the following SQL:

SELECT “clients”.* FROM “clients” WHERE (“clients”.“created_at” BETWEEN ‘2010-09-29’ AND ‘2010-11-30’)

2.3 Hash Conditions Hash字典条件

Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:

Active Record同样也允许你通过hash条件来增加你条件语句的可读性。在hash条件,以键作为你的条件化参数并且相应的值则是具体的条件限制。

Only equality, range and subset checking are possible with Hash conditions.

2.3.1 Equality Conditions等同条件

Client.where(:locked => true)

The field name can also be a string:

Client.where(‘locked’ => true)

2.3.2 Range Conditions

The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.

Client.where(:created_at => (Time.now.midnight – 1.day)..Time.now.midnight)

This will find all clients created yesterday by using a BETWEEN SQL statement:

这将会通过使用一个BETWEEN SQL声明查找client中昨天创建的记录。

SELECT * FROM clients WHERE (clients.created_at BETWEEN ‘2008-12-21 00:00:00’ AND ‘2008-12-22 00:00:00’)

This demonstrates a shorter syntax for the examples in ArrayConditions

2.3.3 Subset Conditions子集条件

If you want to find records using the IN expression you can pass an array to the conditions hash:

如果想使用IN表达式查找记录你可以在条件hash字典中加上一个数组:

Client.where(:orders_count => [1,3,5])

This code will generate SQL like this:

SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))

3 Ordering排序

To retrieve records from the database in a specific order, you can use the order method.

如果你想检索记录从数据库中并且以一种指定的方式排序,你可以使用order方法。

For example, if you’re getting a set of records and want to order them in ascending order by the created_at field in your table:

Client.order(“created_at”)

You could specify ASC or DESC as well:

Client.order(“created_at DESC”)

# OR

Client.order(“created_at ASC”)

Or ordering by multiple fields:或者按照多个fields排序:

Client.order(“orders_count ASC, created_at DESC”)

4 Selecting Specific Fields

By default, Model.find selects all the fields from the result set using select *.

默认情况中,Model.find会选择所有的fields作为结果并且(对数据库执行)select *

To select only a subset of fields from the result set, you can specify the subset via the select method.

要想只选中fields中的一个子集作为结果,你可以通过select方法指定子集。

Iftheselectmethodisused,allthereturningobjectswillbereadonly.

For example, to select only viewable_by and locked columns:

Client.select(“viewable_by, locked”)

The SQL query used by this find call will be somewhat like:

SELECT viewable_by, locked FROM clients

Be careful because this also means you’re initializing a model object with only the fields that you’ve selected. If you attempt to access a field that is not in the initialized record youll receive:

ActiveModel::MissingAttributeError: missing attribute: <attribute>

Where <attribute> is the attribute you asked for. The id method will not raise the ActiveRecord::MissingAttributeError, so just be careful when working with associations because they need the id method to function properly.

You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the DISTINCT function you can do it like this:

Client.select(“DISTINCT(name)”)

 

5 Limit and Offset

To apply LIMIT to the SQL fired by the Model.find, you can specify the LIMIT using limit and offset methods on the relation.

You can use limit to specify the number of records to be retrieved, and use offset to specify the number of records to skip before starting to return the records. For example

Client.limit(5)

will return a maximum of 5 clients and because it specifies no offset it will return the first 5 in the table. The SQL it executes looks like this:

SELECT * FROM clients LIMIT 5

Adding offset to that

Client.limit(5).offset(30)

will return instead a maximum of 5 clients beginning with the 31st. The SQL looks like:

将会返回最大5clients并且从第31个开始。这个SQL看起来是:

SELECT * FROM clients LIMIT 5 OFFSET 30

6 Group

To apply a GROUP BY clause to the SQL fired by the finder, you can specify the group method on the find.

For example, if you want to find a collection of the dates orders were created on:

Order.select(“date(created_at) as ordered_date, sum(price) as total_price”).group(“date(created_at)”)

And this will give you a single Order生产者object for each date where there are orders in the database.

The SQL that would be executed would be something like this:

SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at)

7 Having

SQL uses the HAVING clause短语to specify conditions on the GROUP BY fields. You can add the HAVING clause to the SQL fired by the Model.find by adding the :having option to the find.

For example:

Order.select(“date(created_at) as ordered_date, sum(price) as total_price”).group(“date(created_at)”).having(“sum(price) > ?”, 100)

The SQL that would be executed would be something like this:

SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100

This will return single order objects for each day, but only those that are ordered more than $100 in a day.

8 Overriding Conditions覆盖条件

8.1 except排除

You can specify certain conditions to be excepted by using the except method. For example:

Post.where(‘id > 10’).limit(20).order(‘id asc’).except(:order)#取消排序

The SQL that would be executed:

SELECT * FROM posts WHERE id > 10 LIMIT 20

irb(main):006:0> Post.where(‘id > 1’).limit(20).order(‘id desc’)

Post Load (0.4ms) SELECT “posts”.* FROM “posts” WHERE (id > 1) ORDER BY id desc LIMIT 20

=> [#<Post id: 4, name: “2134”, title: “2134”, content: “21343242134”, created_at: “2011-11-09 10:25:38”, updated_at: “2011-11-09 10:25:38”>, #<Post id: 2, name: “jhjguxin”, title: “test console”, content: “A new post to test console”, created_at: “2011-11-05 15:55:17”, updated_at: “2011-11-05 15:55:17”>]

irb(main):007:0> Post.where(‘id > 1’).limit(20).order(‘id desc’).except(:order)

Post Load (0.4ms) SELECT “posts”.* FROM “posts” WHERE (id > 1) LIMIT 20

=> [#<Post id: 2, name: “jhjguxin”, title: “test console”, content: “A new post to test console”, created_at: “2011-11-05 15:55:17”, updated_at: “2011-11-05 15:55:17”>, #<Post id: 4, name: “2134”, title: “2134”, content: “21343242134”, created_at: “2011-11-09 10:25:38”, updated_at: “2011-11-09 10:25:38”>]

8.2 only

You can also override conditions using the only method. For example:

Post.where(‘id > 10’).limit(20).order(‘id desc’).only(:order, :where)

The SQL that would be executed:

SELECT * FROM posts WHERE id > 10 ORDER BY id DESC

8.3 reorder

The reorder method overrides the default scope order. For example:

class Post < ActiveRecord::Base

..

..

has_many :comments, :order => ‘posted_at DESC’

end

 

Post.find(10).comments.reorder(‘name’)

The SQL that would be executed:

SELECT * FROM posts WHERE id = 10 ORDER BY name

In case the reorder clause is not used, the SQL executed would be:

SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC

If no ordering clause is specified in the query, the reverse_order orders by the primary key in reverse撤销order.

Client.where(“orders_count > 10”).reverse_order

The SQL that would be executed:

SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC

This method accepts no arguments.

9 Readonly Objects

Active Record provides readonly method on a relation to explicitly disallow modification or deletion of any of the returned object. Any attempt to alter or destroy a readonly record will not succeed, raising an ActiveRecord::ReadOnlyRecord exception.

client = Client.readonly.first

client.visits += 1

client.save

As client is explicitly set to be a readonly object, the above code will raise an ActiveRecord::ReadOnlyRecord exception when calling client.save with an updated value of visits.

10 Locking Records for Update

Locking is helpful for preventing race conditions when updating records in the database and ensuring atomic updates.

Active Record provides two locking mechanisms:

  • Optimistic乐观Locking
  • Pessimistic悲观Locking

10.1 Optimistic Locking

Optimistic locking allows multiple users to access the same record for edits, and assumes假定a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened. An ActiveRecord::StaleObjectError exception is thrown if that has occurred发生and the update is ignored.

Optimistic locking column

In order to use optimistic locking, the table needs to have a column called lock_version. Each time the record is updated, Active Record increments the lock_version column. If an update request is made with a lower value in the lock_version field than is currently in the lock_version column in the database, the update request will fail with an ActiveRecord::StaleObjectError. Example:

c1 = Client.find(1)

c2 = Client.find(1)

 

c1.first_name = “Michael”

c1.save

 

c2.name = “should fail”

c2.save # Raises a ActiveRecord::StaleObjectError

You’re then responsible有责任for dealing处理with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict.

Youmustensurethatyourdatabaseschemadefaultsthelock_versioncolumnto0.

This behavior can be turned off by setting ActiveRecord::Base.lock_optimistically = false.

To override the name of the lock_version column, ActiveRecord::Base provides a class method called set_locking_column:

class Client < ActiveRecord::Base

set_locking_column :lock_client_column

end

10.2 Pessimistic Locking

Pessimistic locking uses a locking mechanism provided by the underlying底层database. Using lock when building a relation obtains an exclusive lock on the selected rows. Relations using lock are usually wrapped inside a transaction for preventing deadlock conditions.

在语句块的一个流程中使用lock通常是为了防止死锁情况出现。

For example:

Item.transaction do

i = Item.lock.first

i.name = ‘Jones’

i.save

end

The above session produces the following SQL for a MySQL backend:

You can also pass raw SQL to the lock method for allowing different types of locks. For example, MySQL has an expression called LOCK IN SHARE MODE where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:

SQL (0.2ms) BEGIN

Item Load (0.3ms) SELECT * FROM items LIMIT 1 FOR UPDATE

Item Update (0.4ms) UPDATE items SET updated_at = ‘2009-02-07 18:05:56’, name = ‘Jones’ WHERE id = 1

SQL (0.8ms) COMMIT

You can also pass raw SQL to the lock method for allowing different types of locks. For example, MySQL has an expression called LOCK IN SHARE MODE where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:

Item.transaction do

i = Item.lock(“LOCK IN SHARE MODE”).find(1)

i.increment!(:views)

end

11 Joining Tables

Active Record provides a finder method called joins for specifying JOIN clauses短语on the resulting SQL. There are multiple ways to use the joins method.

11.1 Using a String SQL Fragment

You can just supply the raw SQL specifying the JOIN clause to joins:

Client.joins(‘LEFT OUTER JOIN addresses ON addresses.client_id = clients.id’)

This will result in the following SQL:

SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id

11.2 Using Array/Hash of Named Associations

ThismethodonlyworkswithINNERJOIN.

Active Record lets you use the names of the associations defined on the model as a shortcut for specifying JOIN clause for those associations when using the joins method.

For example, consider the following Category, Post, Comments and Guest models:

class Category < ActiveRecord::Base

has_many :posts

end

 

class Post < ActiveRecord::Base

belongs_to :category

has_many :comments

has_many :tags

end

 

class Comment < ActiveRecord::Base

belongs_to :post

has_one :guest

end

 

class Guest < ActiveRecord::Base

belongs_to :comment

end

 

class Tag < ActiveRecord::Base

belongs_to :post

end

Now all of the following will produce the expected join queries using INNER JOIN:

11.2.1 Joining a Single Association

Category.joins(:posts)

This produces:

SELECT categories.* FROM categories

INNER JOIN posts ON posts.category_id = categories.id

Or, in English: “return a Category object for all categories with posts”. Note that you will see duplicate categories if more than one post has the same category. If you want unique唯一的categories, you can use Category.joins(:post).select(“distinct(categories.id)”).

或者,用英语说:返回所有posts的所有categoriesCategory对象。请注意,如果超过一个post具有相同的categories,你会看到重复的categories。如果你想要唯一的categories,你可以使用Category.joins(:post).select(“distinct(categories.id)”)

11.2.2 Joining Multiple Associations

Post.joins(:category, :comments)#两个参数之间是与关系

This produces:

SELECT posts.* FROM posts

INNER JOIN categories ON posts.category_id = categories.id

INNER JOIN comments ON comments.post_id = posts.id

Or, in English: “return all posts that have a category and at least one comment”. Note again that posts with multiple comments will show up multiple times.

11.2.3 Joining Nested Associations (Single Level)

 

Post.joins(:comments => :guest)

This produces:

SELECT posts.* FROM posts

INNER JOIN comments ON comments.post_id = posts.id

INNER JOIN guests ON guests.comment_id = comments.id

Or, in English: “return all posts that have a comment made by a guest.”

11.2.4 Joining Nested Associations (Multiple Level)

Category.joins(:posts => [{:comments => :guest}, :tags])

This produces:

SELECT categories.* FROM categories

INNER JOIN posts ON posts.category_id = categories.id

INNER JOIN comments ON comments.post_id = posts.id

INNER JOIN guests ON guests.comment_id = comments.id

INNER JOIN tags ON tags.post_id = posts.id

返回postcategory,并且这个post至少有一个commitguest提交而且这个post至少有一个tag

11.3 Specifying Conditions on the Joined Tables

You can specify conditions on the joined tables using the regular Array and String conditions. Hashconditions provides a special syntax for specifying conditions for the joined tables:

你可以在joined tables 的时候通过使用正则数组和字符串条件(表达式)来指定条件。Hash条件提供了一个特殊的语法来指定joined tables的条件:

time_range = (Time.now.midnight – 1.day)..Time.now.midnight

Client.joins(:orders).where(‘orders.created_at’ => time_range)

An alternative and cleaner syntax is to nest the hash conditions:一个更简洁的替代语法是将条件嵌入Hash条件中。

time_range = (Time.now.midnight – 1.day)..Time.now.midnight

Client.joins(:orders).where(:orders => {:created_at => time_range})

This will find all clients who have orders that were created yesterday, again using a BETWEEN SQL expression.这将会查找所有的clients,它们都有orders并且,order在昨天创建,再一次使用BETWEEN SQL语句。

12 Eager Loading Associations

Eager loading快速导入is the mechanism for loading the associated records of the objects returned by Model.find using as few queries as possible.

Eager loading快速导入是一个导入记录的关系的机器,它通过Model.find返回对象,并使用尽可能少的查询。

N + 1 queries problem

Consider the following code, which finds 10 clients and prints their postcodes:

clients = Client.limit(10)

 

clients.each do |client|

puts client.address.postcode

end

This code looks fine at the first sight. But the problem lies within the total number of queries executed. The above code executes 1 ( to find 10 clients ) + 10 ( one per each client to load the address ) = 11 queries in total.上面的代码执行一次(找到10clients+10(每一个client导入地址)

Solution to N + 1 queries problem

Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the includes method of the Model.find call. With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.

Revisiting the above case, we could rewrite Client.all to use eager load addresses:

clients = Client.includes(:address).limit(10)

 

clients.each do |client|

puts client.address.postcode

end

The above code will execute just 2 queries, as opposed to 11 queries in the previous case:

上面的代码将执行2查询,而不是在以前的情况下,以11查询:

SELECT * FROM clients LIMIT 10

SELECT addresses.* FROM addresses

WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))

12.1 Eager Loading Multiple Associations

Active Record lets you eager load any number of associations with a single Model.find call by using an array, hash, or a nested hash of array/hash with the includes method.

Active Record可以通过includes方法加上数组,hash或者使用数组和hash嵌套的hash字典调用单个Model.find方法快速导入任何数目的关系。

12.1.1 Array of Multiple Associations

Post.includes(:category, :comments)

This loads all the posts and the associated category and comments for each post.

12.1.2 Nested Associations Hash

Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)

This will find the category with id 1 and eager load all of the associated posts以及快速导入分类为id1post, the associated posts’ tags and commentsposttagscomments, and every comment’s guest association并且每个comments是由guest创建的.

12.2 Specifying Conditions on Eager Loaded Associations

Even though Active Record lets you specify conditions on the eager loaded associations just like joins, the recommended way is to use joins instead.

However if you must do this, you may use where as you would normally.

Post.includes(:comments).where(“comments.visible”, true)

This would generate a query which contains a LEFT OUTER JOIN whereas the joins method would generate one using the INNER JOIN function instead.

SELECT “posts”.“id” AS t0_r0, … “comments”.“updated_at” AS t1_r5 FROM “posts” LEFT OUTER JOIN “comments” ON “comments”.“post_id” = “posts”.“id” WHERE (comments.visible = 1)

If there was no where condition, this would generate the normal set of two queries.

If, in the case of this includes query, there were no comments for any posts, all the posts would still be loaded. Byusingjoins (an INNER JOIN), the join conditions must match, otherwise no records will be returned.

13 Scopes作用域

Scoping allows you to specify commonly-used ARelArel is a relational algebra engine for Ruby

queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as where, joins and includes. All scope methods will return an ActiveRecord::Relation object which will allow for further methods (such as other scopes) to be called on it.

作用域允许你指定通常使用的ArelArel是一个用于Ruby的关系代数引擎)查询它可以在对象或模型调用方法的时候被引用。通过这些作用域,你可以使用比如wherejoinsincludes这些方法覆盖以前的(方法)。所有的scope方法将会返回一个 ActiveRecord::Relation对象它将允许一些以后的方法(例如其他的作用域)被其调用。

To define a simple scope, we use the scope method inside the class, passing the ARel query that we’d like run when this scope is called:

class Post < ActiveRecord::Base

scope :published, where(:published => true)

end

Just like before, these methods are also chainable可链式的:

class Post < ActiveRecord::Base

scope :published, where(:published => true).joins(:category)

end

Scopes are also chainable within scopes:作用域对作用域中也是可链式的:

class Post < ActiveRecord::Base

scope :published, where(:published => true)

scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))

end

To call this published scope we can call it on either the class:调用这个published作用域我们可以在类中调用:

Post.published => [published posts]

Or on an association consisting of Post objects:或者是在组成Post对象的关系(成员)中调用:

category = Category.first

category.posts.published => [published posts belonging to this category]

13.1 Working with times工作中的时间(字段)

If you’re working with dates or times within scopes, due to how they are evaluated, you will need to use a lambda so that the scope is evaluated every time.

如果你在工作中遇到时间(字段)或者包含有时间(字段)的作用域,由于他们的求值方式,你将需要使用一个匿名函数来使得每次调用域都会计算时间的值。

class Post < ActiveRecord::Base

scope :last_week, lambda { where(“created_at < ?”, Time.zone.now ) }

end

Without the lambda, this Time.zone.now will only be called once.

13.2 Passing in arguments

When a lambda is used for a scope, it can take arguments:

class Post < ActiveRecord::Base

scope :1_week_before, lambda { |time| where(“created_at < ?”, time)

end

This may then be called using this:

Post.1_week_before(Time.zone.now)

However, this is just duplicating复制 the functionality功能 that would be provided to you by a class method.然而,这样仅仅通过一个类方法提供给来复制(使用)这个功能。

class Post < ActiveRecord::Base

def self.1_week_before(time)

where(“created_at < ?”, time)

end

end

Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible访问 on the association objects:

使用一个类方法是一个完美的方法来让scopes获取参数。这个方法仍然访问objectsassociation

category.posts.1_week_before(time)

13.3 Working with scopes使用作用域来工作

Where a relational object is required, the scoped method may come in handy. This will return an ActiveRecord::Relation object which can have further scoping applied to it afterwards. A place where this may come in handy is on associations

client = Client.find_by_first_name(“Ryan”)

orders = client.orders.scoped

With this new orders object, we are able to ascertain that this object can have more scopes applied to it. For instance, if we wanted to return orders only in the last 30 days at a later point.

orders.where(“created_at > ?”, 30.days.ago)

13.4 Applying a default scope

If we wish for a scope to be applied across all queries所有查询 to the model we can use the default_scope method within the model itself.

class Client < ActiveRecord::Base

default_scope where(“removed_at IS NULL”)

end

When queries are executed on this model, the SQL query will now look something like this:

SELECT * FROM clients WHERE removed_at IS NULL

13.5 Removing all scoping

If we wish to remove scoping for any reason we can use the unscoped method. This is especially useful if a default_scope is specified in the model and should not be applied for this particular query.

Client.unscoped.all

This method removes all scoping and will do a normal query on the table.

14 Dynamic Finders动态查询

For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called first_name on your Client model for example, you get find_by_first_name and find_all_by_first_name for free from Active Record. If you have a locked field on the Client model, you also get find_by_locked and find_all_by_locked methods.

在你表中定义的每个field(通常被看着一个属性), Active Record都提供了一个finder方法。例如,如果你有一个field名叫first_name在你的Client模型中,你会免费的从Active Record 得到find_by_first_namefind_all_by_first_name方法。如果你在Client模型中有一个locked field,你也会获得find_by_lockedfind_all_by_locked方法。

You can also use find_last_by_* methods which will find the last record matching your argument.

你也可以使用find_last_by_*方法,它将会查找在机器中匹配你的参数的最后的记录。

You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an ActiveRecord::RecordNotFound error if they do not return any records, like Client.find_by_name!(“Ryan”)

你也可以在动态finders末尾指定一个感叹号来来的到查询结果或者如果他们没有返回任何参数则抛出一个 ActiveRecord::RecordNotFound错误。

If you want to find both by name and locked, you can chain these finders together by simply typing and between the fields. For example, Client.find_by_first_name_and_locked(“Ryan”, true).

Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say Client.find_by_name_and_locked(“Ryan”), the behavior is to pass nil as the missing argument.如果参数少于动态finderfields的数目,习惯上是对缺少的fields传递一个nilThis is unintentional and this behavior will be changed in Rails 3.2 to throw an ArgumentError.这样做是没有意义的并且这个约定将会在Rails 3.2中修改成抛出一个 ArgumentError错误。

There’s another set of dynamic finders that let you find or create/initialize objects if they aren’t found. These work in a similar fashion to the other finders and can be used like find_or_create_by_first_name(params[:first_name]). Using this will first perform a find and then create if the find returns nil. The SQL looks like this for Client.find_or_create_by_first_name(“Ryan”):

 

SELECT * FROM clients WHERE (clients.first_name = ‘Ryan’) LIMIT 1

BEGIN

INSERT INTO clients (first_name, updated_at, created_at, orders_count, locked)

VALUES(‘Ryan’, ‘2008-09-28 15:39:12’, ‘2008-09-28 15:39:12’, 0, ‘0’)

COMMIT

irb(main):134:0> p=Post.find_or_create_by_name_and_title_and_content(‘1111111’,‘111111111’,‘11111111’)

Post Load (0.4ms) SELECT “posts”.* FROM “posts” WHERE “posts”.“title” = ‘111111111’ AND “posts”.“content” = ‘11111111’ AND “posts”.“name” = ‘1111111’ LIMIT 1

SQL (429.0ms) INSERT INTO “posts” (“content”, “created_at”, “name”, “title”, “updated_at”) VALUES (?, ?, ?, ?, ?) [[“content”, “11111111”], [“created_at”, Fri, 02 Dec 2011 08:42:39 UTC +00:00], [“name”, “1111111”], [“title”, “111111111”], [“updated_at”, Fri, 02 Dec 2011 08:42:39 UTC +00:00]]

=> #<Post id: 5, name: “1111111”, title: “111111111”, content: “11111111”, created_at: “2011-12-02 08:42:39”, updated_at: “2011-12-02 08:42:39”>

 

find_or_create’s sibling, find_or_initialize, will find an object and if it does not exist will act similarly to calling new with the arguments you passed in. For example:

client = Client.find_or_initialize_by_first_name(‘Ryan’)

will either assign an existing client object with the name “Ryan” to the client local variable, or initialize a new object similar to calling Client.new(:first_name => ‘Ryan’). From here, you can modify other fields in client by calling the attribute setters on it: client.locked = true and when you want to write it to the database just call save on it.

15 Finding by SQL

If you’d like to use your own SQL to find records in a table you can use find_by_sql. The find_by_sql method will return an array of objects even if the underlying query returns just a single record. For example you could run this query:

Client.find_by_sql(“SELECT * FROM clients

INNER JOIN orders ON clients.id = orders.client_id

ORDER clients.created_at desc”)

find_by_sql provides you with a simple way of making custom calls to the database and retrieving instantiated objects.

16 select_all

find_by_sql has a close relative called connection#select_all. select_all will retrieve objects from the database using custom SQL just like find_by_sql but will not instantiate them. Instead, you will get an array of hashes where each hash indicates指示 a record.

Client.connection.select_all(“SELECT * FROM clients WHERE id = ‘1’”)

1516 综合比较

irb(main):174:0> p=Post.find_by_sql(“SELECT * FROM posts WHERE (posts.id = 1)”) Post Load (1.7ms) SELECT * FROM posts WHERE (posts.id = 1)

=> [#<Post id: 1, name: “name111111111111111”, title: “title111111111111111”, content: “content111111111111111”, created_at: “2011-12-02 08:56:33”, updated_at: “2011-12-02 08:56:33”>]

irb(main):175:0> puts p

#<Post:0xb6bf4d64>

=> nil

irb(main):176:0> p=Post.connection.select_all(“SELECT * FROM posts WHERE id = ‘1’”)

(0.6ms) SELECT * FROM posts WHERE id = ‘1’

=> [{“name”=>“name111111111111111”, “created_at”=>“2011-12-02 08:56:33.397313”, “title”=>“title111111111111111”, “updated_at”=>“2011-12-02 08:56:33.397313”, “id”=>1, “content”=>“content111111111111111”}]

irb(main):177:0> puts p

namename111111111111111created_at2011-12-02 08:56:33.397313titletitle111111111111111updated_at2011-12-02 08:56:33.397313id1contentcontent111111111111111

=> nil

17 Existence of Objects目标是否存在

If you simply want to check for the existence of the object there’s a method called exists?. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.

Client.exists?(1)

The exists? method also takes multiple ids, but the catch is that it will return true if any one of those records exists.

Client.exists?(1,2,3)

# or

Client.exists?([1,2,3])

It’s even possible to use exists? without any arguments on a model or a relation.

Client.where(:first_name => ‘Ryan’).exists?

The above returns true if there is at least one client with the first_name ‘Ryan’ and false otherwise.

Client.exists?

The above returns false if the clients table is empty and true otherwise.

You can also use any? and many? to check for existence on a model or relation.

# via a model

Post.any?

Post.many?

 

# via a named scope

Post.recent.any?

Post.recent.many?

 

# via a relation

Post.where(:published => true).any?

Post.where(:published => true).many?

 

# via an association

Post.first.categories.any?#第一个postcategory是否存在

Post.first.categories.many?

18 Calculations

This section uses count as an example method in this preamble, but the options described apply to all sub-sections.

All calculation methods work directly on a model:

Client.count

# SELECT count(*) AS count_all FROM clients

Or on a relation:

Client.where(:first_name => ‘Ryan’).count

# SELECT count(*) AS count_all FROM clients WHERE (first_name = ‘Ryan’)

Post.first.tags.count

You can also use various finder methods on a relation for performing complex calculations:

Client.includes(“orders”).where(:first_name => ‘Ryan’, :orders => {:status => ‘received’}).count

#Post.includes(‘tags’).where(:id=>“2”,:tags=>{:name=>‘123’}).count

Which will execute:

SELECT count(DISTINCT clients.id) AS count_all FROM clients

LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE

(clients.first_name = ‘Ryan’ AND orders.status = ‘received’)

18.1 Count

If you want to see how many records are in your model’s table you could call Client.count and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use Client.count(:age).

For options, please see the parent section, Calculations.

18.2 Average平均值

If you want to see the average of a certain number in one of your tables you can call the average method on the class that relates to the table. This method call will look something like this:

Client.average(“orders_count”)

irb(main):215:0> puts Post.average(‘created_at’)

(1.4ms) SELECT AVG(“posts”.“created_at”) AS avg_id FROM “posts”

2011.0

18.3 Minimum

If you want to find the minimum value of a field in your table you can call the minimum method on the class that relates to the table. This method call will look something like this:

Client.minimum(“age”)

For options, please see the parent section, Calculations.

18.4 Maximum

If you want to find the maximum value of a field in your table you can call the maximum method on the class that relates to the table. This method call will look something like this:

Client.maximum(“age”)

For options, please see the parent section, Calculations.

18.5 Sum

If you want to find the sum of a field for all records in your table you can call the sum method on the class that relates to the table. This method call will look something like this:

Client.sum(“orders_count”)