Criando relacionamentos one-to-one no Rails

Sempre que criamos um sistema, precisamos criar relacionamentos entre tabelas. Os relacionamentos podem ser de alguns tipos:

  • One to One
  • One to Many
  • Many to Many

Eu vou demostrar aqui como criar relacionamentos One to One. Para esse exemplo, vamos considerar que um usuário possui um perfil e um perfil só pode pertencer a um usuário.

Para começar, vamos criar nosso usuário.

rails g scaffold user name:string email:string

No nosso caso, não iremos precisar criar controller e views para o perfil. As informações contidas no perfil serão criadas no mesmo form que se cria as informações do usuário. Por isso, criaremos apenas um model para o perfil.

rails g model profile user_id:integer description:text

Neste caso, faremos o relacionamento adicionando o id do usuário na tabela do perfil. Vamos, agora, adicionar as migrações no banco.

rake db:migrate

Podemos começar a adicionar o código para criar nosso relacionamento. A primera coisa a se fazer é alterar os models para que eles saibam com quem estão se relacionando. No model do perfil, vamos dizer que ele pertence ao usuário.

#app/models/profile.rb
class Profile < ActiveRecord::Base
  belongs_to :user
end

O model do usuário vai ficar assim:

#app/models/user.rb
class User < ActiveRecord::Base
  has_one :profile
  accepts_nested_attributes_for :profile
end

Neste caso, precisamos adicionar a linha acceptsnestedattributesfor :profile_. O que ela faz é informar para o model que ele deve aceitar os atributos que são do perfil e salvá-los na tabela correspondente. Nested Attributes permitem que você salve os atributos de um filho através de seu pai. Exatamente o que estamos fazendo neste caso. No form de usuário, adicionaremos os campos do perfil e, no momento que submetermos, o perfil será salvo através do usuário. Ficará mais fácil de entender vendo como montaremos o form do usuário. Vamos a ele então.

#app/views/users/_form.html.erb
<%= form_for(@user) do |f| %>
  ...
    <div class="field">
        <%= f.fields_for :profile do |p| %>
            <%= p.label :description %><br/>
            <%= p.text_area :description %>
        <% end %>
    </div>
  ...
<% end %>

Eu reduzi um pouco o arquivo para ficar mais fácil de visualizar o código que estamos adicionando. O f.fieldsfor :profile_ serve para adicionarmos os campos do perfil dentro do form do usuário. Foi por isso que adicionamos a linha do nestedattributes no model de usuário. Na segunda parte, passamos um bloco onde adicionaremos os campos do perfil que, no nosso caso, é apenas _description.

Precisamos fazer com que a descrição apareça na tela depois de criada. Por isso, a adicionaremos no show do usuário.

#app/views/users/show.html.erb
...
<p>
  <b>Description:</b>
  <%= @user.profile.description %>
</p>

Neste momento, se você iniciar o servidor e for olhar sua view, notará que o campo description ainda não está aparecendo; isto é porque ele ainda não foi “criado”. Então, precisamos fazer essa “criação”. Vamos alterar nosso controller para isso.

#app/controllers/users_controller.rb
def new
  @user = User.new
  @user.build_profile
end

A linha @user.buildprofile_ está dizendo para “construir”(não consigo pensar uma expressão melhor no momento :D ) um perfil para esse usuário.

Tudo pronto! Agora, quando você for criar um usuário novo, verá o campo description, que salvará as informações no local certo e com o relacionamento certo.

Apenas como informação adicional: remova o arquivo public/index.html e adicione no config/routes.rb a linha

root :to => 'users#index'

Isso é apenas para vocês não precisarem acessar /users todas a vezes que entrarem no sistema.

Você pode baixar o código fonte do exemplo no repositório blog-posts, no Github.