Relacion muchos a muchos

hola lista
tengo una relacion muchos a muchos entre empresa y mercado y la tabla
join este es el modelo

class CreateEmpresasMercados < ActiveRecord::Migration
def self.up
create_table :empresas_mercados, :id => false do |t|
t.integer :empresa_id
t.integer :mercado_id
t.timestamps
end
end

despues de pasar por una serie de formularios tengo el id de la empresa
que marco el usuario y el id del mercado y quiero escribir ambos en la
tabla join pero no hay manera

ya me lei la api de rails la parte de los modelos, pero no saco nada en
claro.

muchas gracias a todos

Hola,esto [1] lo he pillado en la primera busqueda de google que he
hecho,
yo no tengo mucha idea, pero creo que esta aproximación al tema de las
join
tables es mucho mejor. Te daras cuenta de que la “join table” en el
ejemplo
que te pongo no se llama [articulos_autores]. El por qué lo explica en
el
blog mucho mejor de lo que yo te lo explicaría, pero basicamente es
establecer (al existir el modelo publicacion) una doble relacio has_many
entre los tres modelos. gracias a through => :publications. Lo que tu
intentas hacer no requiere que crees el modelo [articulos_autores]. solo
la
tabla articulos_autores en tu Base de datos. La principal ventaja de
esta
forma es que puedes añadir otros campos al modelo publicacion ademas de
author_id y article_id y que todos sean accesibles por todos.

class Author << ActiveRecord::Base
has_many :publications
has_many :articles, :through => :publications
end

class Publication << ActiveRecord::Base
belongs_to :author
belongs_to :article
end

class Article<< ActiveRecord::Base
has_many :authors
has_many :authors, :through => :publications
end

El ejemplo de uso que muestra el autor de este articulo [1] es el
siguiente:

autor = Author.find(:first)
lista_articulos = autor.articles

Como ves solo tienes que hacer un find para encontrar el autor que
quieres y
luego tendras en la variable lista_articulos un array con todos los
articulos de ese autor. Esto de rails es una pasada.

A la gente de la lista que sabe mucho (mucho*infinito) + que yo. Que se
miren mi respuesta y si estoy diciendo alguna tonteria que me corrijan
por
favor.

Un saludo

NOTA:
Si alguien me puede explicar cuales son las ventajas y deventajas de las
dos
formas de atacar el problema de las join tables que proponemos Maite y
yo,
se lo agradecería.

[1]

hola andres, gracias por responder.
por lo que yo se hay dos manera de hacer lo de tabla join.

una es la que digo yo que lleva la tabla join en medio de las dos tablas
con el has_and_belongs_to_many y la otra es la que dices tu a travez del
thought

eso lo pone en la documentacion cuando habla de many to many

el thought lo usas cuando interviene alguna otra tabla entre los dos
modelos que quieres relacionar, nose si me explico bien, cuando quieres
guardar algun dato
eso esta explicadoen algunos post de la lista

a mi me conviene usar el has_and_belongs_to_many pero no entiendo muy
bien la documentacions cuando dice de insertar en la tabla

en el libro de agil development with rails dice que se hace con push,
articles.push_with_attributes(article, :read_at => Time.now),pag 294,
pero despues te dice que ese metodo es igual al <<.

el la ducumentacion también habla de ese metodo <<, pero no pone ningun
ejemplo, que es lo que quiero ver para probarlo.

muchas gracias a todos

hola de nuevo a todos, me ha surgido una duda

he estado probando el push para escribir en la tabla join y no me sale,
tengo lo siguiente

controller mercado

def update
@empresa=Empresa.find(:all)
@mercado=params[:mercados][:id]
#insertar(@empresa, @mercado)
end

y en el modelo tengo

class Mercado < ActiveRecord::Base
has_and_belongs_to_many :empresas
before_update :insertar

private
def insertar(empresa, mercado)
empresas.push_with_attributes(:empresa_id => empresa,
:mercado_id=>merado)
end
end

pero no pasa por el metodo insertar porque no inserta nada.
aunque tampoco se, como sabe el que tiene que escribir a la tabla
join??? por el has_and_belongs_to_many

espero me puedan ayudar

Hay una manera más facil de actualizar los campos de una relación HBTM,
puedes utilizar checkboxes para marcar en una empresa a que mercados
pertenece, te paso enlaces a unos ejemplos, si lo haces así, a parte de
seguir la convención, no tendrás que hacer pushes ni nada. Con una
linea en
el form lo tienes solucionado.
http://wiki.rubyonrails.org/rails/pages/CheckboxHABTM

http://www.justinball.com/2008/07/03/checkbox-list-in-ruby-on-rails-using-habtm/

El 29 de julio de 2008 12:10, Francesc
Esplugas[email protected]
escribió:

2008/7/29 Maite P. [email protected]:

y en el modelo tengo
end
Primero de todo comentar que si ese es el codigo que estas
utilizando, es imposible que funciona porque hay un error.
“:mercado_id => merado” …

Por otra parte podrias utilizar lo siguiente para añadir empresas a la
join table.

@mercado.empresas << empresa

Esto …

def update
  empresa=Empresa.find(:all)
  @empresa=empresa.last.id
  @mercado=params[:mercados][:id]
  @mercado.empresas << @empresa
end

Deberia ser algo como esto …

def update
  @empresa=Empresa.find(:last)
  @mercado = Mercado.find(params[:mercados][:id])
  @mercado.empresas << @empresa
end

muchas gracias Francesc

no me estaba dando cuenta de que no estaba tomando bien el valor de
@mercado

muchas gracias de nuevo

Gracias Francesc, por responder pero no me funciona nose si serán mis
tablas…
corregi lo que me dijiste

class Mercado < ActiveRecord::Base
has_and_belongs_to_many :empresas
before_update :insertar
private
def insertar(empresa, mercado)
mercado.empresas << empresa
#empresas.push_with_attributes(:empresa_id => empresa,
:mercado_id=>mercado)
end
end

si uso el callbacks no me da error pero no me inserta nada

si lo hago directamente en el controlador si me falla
def update
empresa=Empresa.find(:all)
@empresa=empresa.last.id
@mercado=params[:mercados][:id]
@mercado.empresas << @empresa
end

me dice que empresas no es un metodo de mercado
undefined method `empresas’ for “1”:String

sabes porque, muchas gracias de nuevo

Hola, me he puesto a mirar uno de los links que ha dejado Emili [1] y lo
he
probado, pero no consigo actualizar los lenguajes que tiene chekeados
cada
usuario
lo que si puedo hacer, es asignar los lenguajes que quiera a los
usuarios
pero solo la primera vez que los edito, despues si los quiero editar
otra
vez me aparecen chekeados los lenguajes que escogi la primera vez, pero
si
estos los cambio, entro por tercera vez a editar y ya no me ha realizado
el
UPDATE. Dejo aqui un pasti [2] con mi codigo por si a alguien le apetece
echarle un vistazo

[1]
http://www.justinball.com/2008/07/03/checkbox-list-in-ruby-on-rails-using-habtm/
[2] http://pastie.org/243348

Un saludo
El 29 de julio de 2008 14:28, Maite
Piedra<[email protected]

escribió:

On Tue, Jul 29, 2008 at 19:04, Andrés gutiérrez [email protected]
wrote:

[1]
Checkbox list in Ruby on Rails using HABTM – Justin Ball
[2] http://pastie.org/243348

Un saludo

Desconozco porqué el código de la página no te funciona, pero para
esas tareas programé hace mucho tiempo (Rails 1.1) un plugin que
puedes encontrar en
http://ruido-blanco.net/blog/rails-plugin-multiple-select-espanol/.
Funciona sin problemas en los nuevos Rails (2.1 y 2.0, aunque no está
adaptado al form_for).

Básicamente con un helper en la vista tienes todo hecho. La
documentación me parece bastante completa, pero te he creado un pastie
http://pastie.org/243691 con el cambio que deberías hacer (no he
comprobado lo del form_for, pero supongo que funcionará).

Pruebaló y mira a ver si te arregla el problema.

Suerte.

Muchas gracias por contestar, lo del plugin me lo apunto, pero de
momento no
lo quiero usar porque me interesa aprender como funcionan los mecanismos
del
propio rails antes de meterme con plugins externos. Pero muchas gracias
por
haberme contestado, me encanta la forma tan eficiente en la que funciona
esta lista.

Un saludo

El 30 de julio de 2008 1:05, Daniel R.
Troitiño[email protected]
escribió:

2008/7/30 Andrés gutiérrez [email protected]:

Muchas gracias por contestar, lo del plugin me lo apunto, pero de momento no
lo quiero usar porque me interesa aprender como funcionan los mecanismos del
propio rails antes de meterme con plugins externos. Pero muchas gracias por
haberme contestado, me encanta la forma tan eficiente en la que funciona
esta lista.

Un saludo

Eso es bueno, nunca utilices un plugin que no puedas haber escrito tu
mismo.

¿Podrías pegar el código HTML del formulario generado?

También sería genial conocer la petición POST que realiza el navegador
al enviar el formulario y lo que interpreta Rails de ella (lo que
envia el navegador lo puedes conseguir de Firebug y lo que interpreta
Rails del log de desarrollo).

También serie genial saber con que versión de Rails trabajas.

Muchas gracias Daniel. Ahora estoy liado con el curro, pero en cuanto
tenga
un mlomento te contesto a eso para que me puedas ayudar :slight_smile:

la versión de rails: 2.0.2
Ruby 1.8.6

Un saludo

El 30 de julio de 2008 11:43, Daniel R.
Troitiño[email protected]
escribió:

Todo lo que voy a pegar a continuación es el codigo correspondiente a
cuando
entro en un usuario a la vista de edición, que es donde tengo el
problema
que describo más abajo [1]. Aquí dejo lo que me has pedido Daniel

¿Podrías pegar el código HTML del formulario generado?

Este es el formulario generado por la vista edit.erb.html del modelo
user.rb

Name

Login

<input id="user_login" name="user[login]" size="30" type="text"

value=“paco” />

English

French

Spanish

lo que interpreta Rails del log de desarrollo

Processing UsersController#update (for 127.0.0.1 at 2008-07-30 15:04:28)
[PUT]
Session ID:
BAh7BzoMY3NyZl9pZCIlZTFkMjNlYjBkOWQ2YmRmYmE3NzU0ZTYzYWM0NDYx%0AZjUiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh%0Ac2h7AAY6CkB1c2VkewA%3D–f5e6c74ebf98d2ca4614f4ffa6cd8d3dc605fbde
Parameters: {“user”=>{“name”=>“paco”, “language_ids”=>“1”,
“login”=>“paco”}, “commit”=>“Update”,
“authenticity_token”=>“35fcc5f7dc4f0604cbe1de71c0ff2f1179c9dc95”,
“_method”=>“put”, “action”=>“update”, “id”=>“3”, “controller”=>“users”}
User Columns (0.006105) SHOW FIELDS FROM users
User Load (0.006227) SELECT * FROM users WHERE (users.id = 3)
Language Columns (0.066236) SHOW FIELDS FROM languages
Language Load (0.004459) SELECT * FROM languages WHERE
(languages.id = 1)
Join Table Columns (0.004491) SHOW FIELDS FROM languages_users
Language Load (0.008250) SELECT * FROM languages INNER JOIN
languages_users ON languages.id = languages_users.language_id WHERE
(languages_users.user_id = 3 )
SQL (0.000500) BEGIN
SQL (0.000566) COMMIT
SQL (0.000595) BEGIN
User Update (0.001026) UPDATE users SET created_at = ‘2008-07-29
18:28:59’, login = ‘paco’, updated_at = ‘2008-07-30 15:04:28’,
name =
‘paco’ WHERE id = 3
SQL (0.001559) COMMIT
Redirected to http://localhost:3000/users/3
Completed in 0.23399 (4 reqs/sec) | DB: 0.10001 (42%) | 302 Found [
http://localhost/users/3]

Lo de Firebug, no sé lo que se envia por POST, yo lo hago asi. Le doy a
update, y en firebug tengo abierta la pestaña [NET], pero hay solo me
envia
la .css y la página a la que me redirige una vez que ha hecho el UPDATE
del
modelo user.

[1] EL PROBLEMA
Mi problema basicamente es que al editar a un usuario el cual tiene el
modelo language.rb asociado no se actualizan los lenguages de este
usuario
aunque en la pantalla de edición pulse los selects de esos idiomas. Sólo
queda clickado los idiomas que puse la primera vez que edite a ese
usuario.

Yo creo que estoy haciendo algo mal en el metodo UPDATE del controlador
users_controller.rb

En fin, no sé lo que pasa.

Un saludo

Sinceramente no se como te puede funcionar en el caso de new/create,
pero al menos entiendo porque no te funciona en el caso del
edit/update.

Cuando se envia un formulario el navegador tiene que decidir cuales
controles enviar, y cuales no. Los controles que se deben enviar se
denominan “exitosos”. Los checkbox solo son exitosos si están
activados, si no lo están no se envian. Por eso Rails cuando crea un
checkbox crea además un input hidden con el mismo nombre justo debajo.
Los controles exitosos se envian en orden de aparición, por lo que si
el checkbox está pulsado se enviará delante del input hidden y el
servidor cuando lea los parámetros leerá el valor del checkbox y
interpretará que ha sido pulsado. Si el checkbox no ha sido pulsado,
no se enviará al servidor, pero el input hidden sí, por lo que el
servidor verá el valor falso (o vacio, como en tu caso).

Rails, para enviar varios valores bajo el mismo nombre realiza el
truco de terminar el nombre del control con “[]”, que Rails interpreta
como un array y recoge todos los valores que se encuentran en la
petición del navegador, en vez del primero que recogeria si no llevase
los corchetes al final.

Después de esta explicación, vamos con tu problema, que a mi me sucede
tanto en new/create como en edit/update (lo he probado tanto en Rails
2.0.2 como en 2.1, que es lo que dice que utiliza el enlace que
enviaste).

Este es mí código en la vista:

<% @languages.each do |language| -%>

  • <%= f.check_box :language_ids, {:checked => user_speaks_language?(language)}, "#{language.id}", "" -%> <%= language.name -%>
  • <% end %>

    Que genera este código HTML:

  • English
  • Spanish
  • German
  • French
  • Como ves (al igual que en tu código) hay un input checkbox y un input
    hidden justo a continuación. Todos con el mismo nombre, pero los
    checkbox tienen value 1, 2, 3, 4 y los hidden tiene un valor vacio.

    Esto es lo que envia en un POST (new/create) y en un PUT (edit/update)
    si selecciono los tres últimos checkbox de los 4. Lo he editado un
    poco para que quede más entendible (por cierto Firebug no me ha
    funcionado porque pierde los contenidos del POST/PUT y los intenta
    realizar otra vez. He utilizado otra extensión llamada Live HTTP
    Headers).

    user[name]=U5&
    user[language_ids]=&
    user[language_ids]=2&
    user[language_ids]=&
    user[language_ids]=3&
    user[language_ids]=&
    user[language_ids]=4&
    user[language_ids]=&
    commit=Create

    Como ves se ha enviado los ids 2, 3, 4, y cuatro user[language_ids] en
    blanco (uno por cada hidden, que siempre son existosos). Como Rails
    analiza esta cadena de principio a final encuentra el primer
    language_ids vacio y lo toma como válido, como se ve en los parámetros
    que le llegan a nuestro controlador:

    Parameters: {“user”=>{“name”=>“U5”, “language_ids”=>“”},
    “commit”=>“Create”,
    “authenticity_token”=>“1ef350705e4d86b8813982fcf9a3d203a62f5e1e”,
    “action”=>“create”, “controller”=>“users”}

    Ese language_ids=>“” está tomado del primer hidden, y obviamente
    provoca todo tipo de comportamientos inesperados. En el caso de
    seleccionar el primer checkbox se envia el id del primer checkbox
    antes de su hidden y ese valor sí que queda almacenado, pero no ningún
    otro.

    Pero como digo, si a tí te funciona el new/create algo debes de tener
    diferente.

    Como por ejemplo lo que yo he cambiado para que me funcione. En las
    vistas (new y edit):

    <% @languages.each do |language| -%>

  • <%= f.check_box :language_ids, {:checked => user_speaks_language?(language), :name => 'user[language_ids][]'}, "#{language.id}", "" -%> <%= language.name -%>
  • <% end %>

    El detalle está en ese nuevo parámetro :name donde creo la cadena que
    crearía Rails, pero con el indicador de que queremos un array (hay
    otro modo poniendo de segundo parámetro en vez de “:language_ids”
    poner “language_ids][”, sí, con los corchetes al revés, pero nos crea
    un atributo id inválido).

    Con ese cambio todo parece que me funciona como debería, aunque se
    crean muchos más input hidden de los necesarios (con uno bastaría).

    Por cierto, en la documentación de Edge Rails el método check_box está
    mucho más comentado estas situaciones, e inclusa una similar a esta
    que te molestaba:
    http://github.com/rails/rails/tree/master/actionpack/lib/action_view/helpers/form_helper.rb#L444.
    Curiosamente siguen sin explicar demasiado el parámetro :checked.

    Como conclusión parece ser que check_box de Rails está más destinado a
    valores true/false o similares. Para estas cosas me parece más útil
    check_box_tag, que si bien es un poco más tedioso de utilizar con
    modelos te da un control más fino (y siempre que no te olvides de un
    input hidden al final para el caso de que el usuario no seleccione
    ningún checkbox). Esa es la solución que utilizo en el plugin.

    Bueno, suerte con tus usuarios y tus lenguajes, espero que consigas lo
    que quieres.

    Después de esta explicación, vamos con tu problema, que a mi me sucede
    tanto en new/create como en edit/update (lo he probado tanto en Rails
    2.0.2 como en 2.1, que es lo que dice que utiliza el enlace que
    enviaste).

    Te entendi mal, el que usa la version 2.0.2 soy yo no el tutorial. Lo
    siento
    por la confusión

    Bueno, suerte con tus usuarios y tus lenguajes, espero que consigas lo
    que quieres.

    Suerte ninguna, con tu explicación ya me vale :slight_smile:

    Por cierto me ha encantado la explicación. Una confusión más, a mi
    tampoco
    me funcionaba en el create. Ahora con tu explicación ya me funciona

    Tengo que ponerme en serio con esto de Rails. Llevo un tiempo tonteando
    con
    tutoriales aislados pero no llego a tener una visión global del
    Framework.
    De PHP tampoco se mucho, pero sabiendo hacer la conexion a BBDD los
    insert y
    selects y montando los formularios en html ya puedes ir tirando, con
    rails
    voy perdido. Pero bueno, tengo esperandome “The Rails Way” un estupendo
    libro que espero me aclare cosas.

    Bueno, que muchas gracias Daniel por tomarte la molestia de explicarmelo
    tan
    claro

    Un saludo

    El 31 de julio de 2008 2:10, Daniel R.
    Troitiño[email protected]
    escribió: