Relación has_one y has_many sobre la misma clase


#1

Tengo los siguientes modelos:

Table name: seasons

id :integer not null, primary key

league_id :integer not null

date :integer not null

actual_round :integer default(0), not null

created_at :datetime

updated_at :datetime

class Season < ActiveRecord::Base
has_many :rounds, :dependent => :destroy
end

Table name: rounds

id :integer not null, primary key

season_id :integer not null

round_number :integer not null

created_at :datetime

updated_at :datetime

class Rounds < ActiveRecord:Base
belongs_to :season
end

El caso es que actual_round apunta a una instancia de round para saber
cual es la jornada actual de la temporada, me gustaría saber si es
posible establecer también una relación has_one en season hacia rounds a
través del campo actual_round.

Gracias.


#2

Carlos Belizón wrote:

El caso es que actual_round apunta a una instancia de round para saber
cual es la jornada actual de la temporada, me gustaría saber si es
posible establecer también una relación has_one en season hacia rounds a
través del campo actual_round.

Fácil:

class Season < ActiveRecord::Base
has_many :rounds, :dependent => :destroy
has_one :round_actual, :foreign_key => ‘actual_round’
end

Y para acceder:

my_season.rounds
my_season.round_actual

fíjate y no pongas has_one :actual_round , porque si no rails no podrá
saber si te refieres al campo o al objeto, y te dará problemas.

s2


#3

Bueno, quizás no me expliqué todo lo bien que debía, vuelvo a explicarme
de forma más extensa:

Tengo una clase llamada Season que está relacionada con Round en una
relación de este tipo:

class Season < ActiveRecord::Base
has_many :rounds
end

class Round < ActiveRecord::Base
belongs_to :season
end

Esa relación funciona perfectamente.

Dentro del modelo season tengo un campo llamado actual_round el cuál
guarda el id de una Round (de la jornada actual del campeonato vamos), y
resulta que una Season sólo puede tener una Round como jornada actual.

Si hago esto:

class Season < ActiveRecord::Base
has_many :rounds
has_one :round_actual, :foreign_key => ‘actual_round’
end

class Round < ActiveRecord:::Base
belongs_to :season
end

Cuando hago my_season.round_actual recibo el error siguiente:

uninitialized constant Season::RoundActual

¿Es realmente posible hacer lo que estoy haciendo o no?


#4

Fernando C. wrote:

Carlos Belizón wrote:

El caso es que actual_round apunta a una instancia de round para saber
cual es la jornada actual de la temporada, me gustaría saber si es
posible establecer también una relación has_one en season hacia rounds a
través del campo actual_round.

Fácil:

class Season < ActiveRecord::Base
has_many :rounds, :dependent => :destroy
has_one :round_actual, :foreign_key => ‘actual_round’
end

Y para acceder:

my_season.rounds
my_season.round_actual

fíjate y no pongas has_one :actual_round , porque si no rails no podrá
saber si te refieres al campo o al objeto, y te dará problemas.

s2

Cuando intento acceder a my_season.round_actual me dá el siguiente
error:

uninitialized constant Season::RoundActual

¿Qué estaré haciendo mal?


#5

2009/5/7 Carlos Belizón removed_email_address@domain.invalid

Si el campo lo guardas en Season, no te puede funcionar como clave
foránea
en Round, que es lo que tú estás diciendo

Tal como lo tienes montado, la jornada actual la tienes en

current_round =
@current_season.rounds.find(@current_season.actual_round)

que lo puedes pasar a un método de Season

season.rb

class Season < ActiveRecord::Base
has_many :rounds

def current_round
rounds.find(actual_round)
end

end

@season.current_round ya teda la actual

  • Por cierto, “actual” en inglés, si no me equivoco, es más bien
    “verdadera”. La actual sería “current” or “present”

  • Supongo que es equivalente y va en gustos, pero yo preferiria guardar
    un
    booleano “current” en Round y simplemente asegurarme de que se pone y se
    quita correctamente. Así puedes usar un named_scope :current

Y por cierto, en algún momento, cambia las claves foráneas para que
acaben
en “_id”, se entiende todo mucho mejor :slight_smile:

Post Data

Como bien dicen mis compañeros, también puedes poner un belongs_to
:current_round, :class_name => ‘Round’, :foreign_key => ‘actual_round’
en
Seson. De forma que también podrías hacer @current_season.current_round,
pero nos parece semánticamente raruno


#6

Manuel González Noriega wrote:

2009/5/7 Carlos Belizón removed_email_address@domain.invalid

Como bien dicen mis compañeros, también puedes poner un belongs_to
:current_round, :class_name => ‘Round’, :foreign_key => ‘actual_round’
en
Seson. De forma que también podrías hacer @current_season.current_round,
pero nos parece semánticamente raruno

Oooops!! esto es exactamente lo que yo quería decir, pero me había
dejado el :class_name => ‘Round’, y claro… no funcionaba!!

A mí no me parece semánticamente raruno; a mí me encanta… y de hecho,
lo uso. Pero eso son gustos personales… aunque desde luego, es más
corto de escribir y más eficiente a nivel BBDD que
@current_season.rounds.find(@current_season.actual_round)

s2


#7

Manuel González Noriega wrote:

2009/5/7 Carlos Belizón removed_email_address@domain.invalid

Si el campo lo guardas en Season, no te puede funcionar como clave
foránea
en Round, que es lo que tú estás diciendo

Tal como lo tienes montado, la jornada actual la tienes en

current_round =
@current_season.rounds.find(@current_season.actual_round)

que lo puedes pasar a un método de Season

season.rb

class Season < ActiveRecord::Base
has_many :rounds

def current_round
rounds.find(actual_round)
end

end

@season.current_round ya teda la actual

He optado por implementar esta opción y otro método así:

def current_round=(other)
actual_round = other.id
end

  • Por cierto, “actual” en inglés, si no me equivoco, es más bien
    “verdadera”. La actual sería “current” or “present”

Toda la razón, esto me pasa por no tener mucha idea de inglés :).

  • Supongo que es equivalente y va en gustos, pero yo preferiria guardar
    un
    booleano “current” en Round y simplemente asegurarme de que se pone y se
    quita correctamente. Así puedes usar un named_scope :current

Pero es tener un campo replicado por todas partes (mala normalización) y
el problema es que esta aplicación es de PFC y como que los profesores
de BBDD se ponen muy pejigueras.

Y por cierto, en algún momento, cambia las claves foráneas para que
acaben
en “_id”, se entiende todo mucho mejor :slight_smile:

El problema es que en este caso he ido cambiándolo sobre la marcha :).

Post Data

Como bien dicen mis compañeros, también puedes poner un belongs_to
:current_round, :class_name => ‘Round’, :foreign_key => ‘actual_round’
en
Seson. De forma que también podrías hacer @current_season.current_round,
pero nos parece semánticamente raruno

Eso tiene un problema y es que cuando se implementa hace un select en la
tabla rounds buscando un actual_round cosa que no existe en dicha tabla
:).

Gracias a todos ;).


#8

2009/5/7 Carlos Belizón removed_email_address@domain.invalid

Mmmmm, confieso que no entiendo, un campo booleano en la tabla Rounds
¿rompe
la normalización? Equivale a cualquier campo “active”, “featured”, o
similar
en otra tabla.

Eso tiene un problema y es que cuando se implementa hace un select en la
tabla rounds buscando un actual_round cosa que no existe en dicha tabla
:).

Si el belongs_to está en Season, no debería esperar el fk en la tabla
“rounds”


#9

2009/5/7 Fernando C. removed_email_address@domain.invalid

dejado el :class_name => ‘Round’, y claro… no funcionaba!!
Pero tú decías ponerlo como has_one, no? El problema así es que buscaría
la
fk en la tabla ‘rounds’ y está en ‘seasons’

A mí no me parece semánticamente raruno; a mí me encanta… y de hecho,
lo uso. Pero eso son gustos personales… aunque desde luego, es más
corto de escribir y más eficiente a nivel BBDD que
@current_season.rounds.find(@current_season.actual_round)

No tengo problema con el has_one, pero hacer ahí que Temporada sea “hija
de”
o “pertenezca a” Jornada, me parece contraintuitivo al invertir la
relación.

En todo caso, sí que es más corto que el find con scope, pero por eso
recomendaba meterlo en un método :slight_smile:


#10

Manuel González Noriega wrote:

Mmmmm, confieso que no entiendo, un campo booleano en la tabla Rounds
¿rompe
la normalización? Equivale a cualquier campo “active”, “featured”, o
similar
en otra tabla.

Vale, quizás un flag no sea realmente no normalizar un dato, pero es un
dato repetido en cada jornada que veo bastante innecesario (vamos mi
tutor me ha recomendado quitarlo para que no me pongan pegas) ya que con
un simple “puntero” a la jornada actual solucionas el problema con el
consecuente ahorro de espacio en la BD.

Si el belongs_to está en Season, no debería esperar el fk en la tabla
“rounds”

Probaré eso, para ver como funciona y te cuento :).