Ruby Forum Rails-ES > Formularios complejos y has_one

Posted by Mario Soto (masoto)
on 18.08.2008 10:38
Hola lista,
tengo un pequeño problema que no se darle solución, resulta que tengo un
modelo User que tiene una dirección:

class User < ActiveRecord::Base

  has_one :location, :as => :localizable, :dependent => :destroy


Cuando se crea un usuario por primera vez, se inicializa con una
dirección en blanco:

  def after_create
    l = Location.new
    l.localizable = self
    l.save!
  end

Luego cuando hago el edit del usuario, tengo:

<% fields_for "user[location]", @user.location do |location_form| %>
<table class="profile">
  <tr>
    <th>Address:</th>
    <td><%= location_form.text_field :address %></td>
  </tr>
  <tr>
    <th>City:</th>
    <td><%= location_form.text_field :city %></td>
  </tr>
        ....

Y en el modelo:

  after_update :save_location

  def save_location
    location.save(false)
  end

Pero no me guarda nada. ¿Alguna idea por favor? ¿Donde estoy metiendo la
pata?
Muchas gracias a todos.
Posted by Daniel Rodriguez Troitiño (Guest)
on 18.08.2008 11:22
(Received via mailing list)
2008/8/18 Mario Soto <ruby-forum-incoming@andreas-s.net>:
> dirección en blanco:
>
>  def after_create
>    l = Location.new
>    l.localizable = self
>    l.save!
>  end

Lo primero, ese after_create queda mucho más claro utilizando Rails:

def after_create
  create_location # Crea un Location vacio, se lo asigna a la
instancia actual y lo guarda (se le pueden pasar parámetros)
end

Como siempre, hay que conocer las asociaciones y los métodos que
crean: 
<http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_one>

>    <td><%= location_form.text_field :city %></td>
>  </tr>
>        ....

Si estás dentro de un "form_for @user" con "fields_for @user.location"
debería bastar.
<http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for>

> Muchas gracias a todos.
Eso no debería ser necesario, cuando el modelo "padre" es salvado el
modelo asociado es salvado.

Tengo la impresión que lo que falla es tu código de la vista, que
desconozco que HTML generará, y como Rails lo interpretará. Lo ideal
sería conocer los parámetros que recibe Rails después de hacer el
submit del formulario. Suponiendo que el controlador sea el estándar
de Rails y que los parámetros lleguen correctamente todo debería
funcionar.

Suerte.
Posted by Mario Soto (masoto)
on 18.08.2008 14:11
Hola, muchas gracias por tu respuesta,
te pego el código de la vista:

<form action="/users/1" class="edit_user" id="edit_user_1" 
method="post"><div style="margin:0;padding:0"><input name="_method" 
type="hidden" value="put" /></div>
  <tr>
    <th>Nombre:</th>
    <td><input id="user_first_name" name="user[first_name]" size="30" 
type="text" value="Vicent" /></td>
  </tr>
        ...

      <tr>
    <th>Calle:</th>
    <td><input id="location_address" name="location[address]" size="30" 
type="text" /></td>
  </tr>
  <tr>
    <th>Ciudad:</th>
    <td><input id="location_city" name="location[city]" size="30" 
type="text" /></td>
  </tr>
        ...
</form>

En los modelos:

  # user.rb
  has_one :location, :as => :localizable, :dependent => :destroy

  # location.rb
  belongs_to :localizable, :polymorphic => true

  def after_create
    # más limpio así, gracias =)
    create_location
  end

  after_update :save_location

  def save_location
    location.save(false)
  end

Y en el controlador de user_controller.rb:

def update
    respond_to do |format|
      if current_user.update_attributes(params[:user])
      ....

Pero al hacer el submit, me sigue son guardar la dirección.
Gracias.

Un saludo.



Posted by Daniel Rodriguez Troitiño (Guest)
on 18.08.2008 14:23
(Received via mailing list)
2008/8/18 Mario Soto <ruby-forum-incoming@andreas-s.net>:
>  </tr>
> type="text" /></td>
>  </tr>
>        ...
> </form>

Si miras atentamente los atributos name de los input verás el
problema: los pertenecientes directamente al usuario son
"user[first_name]", mientras que los de la localización son
"location[city]". Eso llegará al controlador en dos hashes en param,
param[:user] y param[:location], y tú haces
update_attributes(param[:user]), con lo que los cambios en
params[:location] no van a ser salvados.

Como te he escrito en el anterior correo deberías utilizar "fields_for
@user.location" en vez de lo que tú utilizas, y debería funcionarte.

>
>  after_update :save_location
>
>  def save_location
>    location.save(false)
>  end
>

De nuevo, esta parte no es necesaria, cuando modifiques la vista y
hagas el current_user.update_attributes(params[:user]) seguido de
current_user.save se salvará a su vez el modelo asociado (sin
necesidad de ese código).

Suerte.
Posted by Mario Soto (masoto)
on 18.08.2008 16:10
> 
> Como te he escrito en el anterior correo deberías utilizar "fields_for
> @user.location" en vez de lo que tú utilizas, y debería funcionarte.
> 

Hola Daniel,
estoy utilizando como me comentaste en tu primera respuesta "fields_for 
@user.location"

>>
>>  after_update :save_location
>>
>>  def save_location
>>    location.save(false)
>>  end
>>
> 

Esto lo dejé porque sigue sin funcionarme, no lo entiendo.
Posted by Daniel Rodriguez Troitiño (Guest)
on 18.08.2008 17:31
(Received via mailing list)
2008/8/18 Mario Soto <ruby-forum-incoming@andreas-s.net>:
>>
>> Como te he escrito en el anterior correo deberías utilizar "fields_for
>> @user.location" en vez de lo que tú utilizas, y debería funcionarte.
>>
>
> Hola Daniel,
> estoy utilizando como me comentaste en tu primera respuesta "fields_for
> @user.location"
>

Jo, lo siento, eso funciona en Edge, pero no en el 2.1 (eso me pasa
por hablar sin comprobar las cosas).

Ahora mismo a tu controlador llegan los dos hash que te he comentado
antes (params[:user] y params[:location]). Lo que tendrías que hacer
es un:

User.transaction do
current_user.update_attributes!(params[:user])
current_user.location.update_attributes!(params[:location])
end

Y recuerda hacer los rescue pertinentes.

Suerte.
Posted by Mario Soto (masoto)
on 18.08.2008 17:58
Muchísimas gracias Daniel, con esto a la primera
un saludo.
Posted by Mario Soto (masoto)
on 27.08.2008 10:25
Buenos días a todos,
por favor, siguiendo con este hilo tengo una pregunta, un poco tonta 
creo, pero ahora mismo no me sale:
Si un usuario tiene un 'location' como hemos visto arriba, ¿como podría 
buscar todos los usuarios de una misma ciudad?

Muchas gracias.
Posted by Mario Soto (masoto)
on 30.08.2008 22:41
Mario Soto wrote:
> Buenos días a todos,
> por favor, siguiendo con este hilo tengo una pregunta, un poco tonta 
> creo, pero ahora mismo no me sale:
> Si un usuario tiene un 'location' como hemos visto arriba, ¿como podría 
> buscar todos los usuarios de una misma ciudad?
> 
> Muchas gracias.

¿Alguna idea por favor?
Posted by Manuel González Noriega (Guest)
on 30.08.2008 23:00
(Received via mailing list)
2008/8/30 Mario Soto <ruby-forum-incoming@andreas-s.net>

>
>
¿Qué artículos y referencias sobre el tema has encontrado en estos tres
días? ¿No te han servido? ¿Qué código has probado? ¿No te funciona? ¿Por
qué?

Son las cosas que solemos preguntar en estos casos :)
Posted by Daniel Rodriguez Troitiño (Guest)
on 30.08.2008 23:45
(Received via mailing list)
2008/8/30 Mario Soto <ruby-forum-incoming@andreas-s.net>:
> Mario Soto wrote:
>> Buenos días a todos,
>> por favor, siguiendo con este hilo tengo una pregunta, un poco tonta
>> creo, pero ahora mismo no me sale:
>> Si un usuario tiene un 'location' como hemos visto arriba, ¿como podría
>> buscar todos los usuarios de una misma ciudad?
>>
>> Muchas gracias.
>
> ¿Alguna idea por favor?

Sin comprobar:

User.find(:all, :include => :location, :conditions => ["locations.city
LIKE ?", search_city])

Todo lo podrías haber encontrado en... atención... la documentación:
<http://apidock.com/rails/ActiveRecord/Base/find/class>