has_many :through - dostać się do danych

[code=ruby]create_table “bets”, :force => true do |t|
t.integer “match_id”
t.integer “user_id”
t.integer “preresult”
t.datetime “created_at”
t.datetime “updated_at”
end

create_table “matches”, :force => true do |t|
t.string “name”
t.integer “outcome”
t.datetime “created_at”
t.datetime “updated_at”
end

create_table “users”, :force => true do |t|
t.string “name”
t.datetime “created_at”
t.datetime “updated_at”
end[/code]
modele

class User < ActiveRecord::Base has_many :matches, :through => :bets has_many :bets

class Match < ActiveRecord::Base has_many :bets has_many :users, :through => :bets

class Bet < ActiveRecord::Base belongs_to :match belongs_to :user
i teraz o ile powiązania są dobre to wyświetlam w widok users:

[code=ruby]<% @users.each do |user| %>

<%=h user.name %>[/code] ale jeżeli chcę wykorzystać dalej w tym co powyżej
	<td><%=h user.matches.name %></td>

to mi wyświetla: wszędzie ‘Match’.

Coś nie mogę sobie poradzić. Pewnie jakąś dodatkową pętle trzeba zrobić …, ale nie bardzo wiem jak.

user.matches

Daje Ci tablicę obiektów typu Match.
Jeśli chcesz to dalej wykorzystać, przeiteruj po niej:tak jak zrobiłeś po tablicy users.

<% user.matches.each do |match| %> <%=h match.name %>

tpl: można wiedzieć na priva albo tutaj o jakiej tematyce ten serwis z zakładami ? pytam z ciekawości bo chwilę pracowałem w tej branży

@tjeden dzieki,

@Artur79 to tylko taki dodatek na lokalnej stronie piłkarskiej i może nie jako zakłady ale coś ala typer ma z tego wyjść.

@bets = Bet.find( :all, :conditions => [ "bets.preresult = 'matches.outcome' and bets.user.id = 1 " ], :joins => [:match])
czy to jest poprawne skonstruowane ? bo jakoś części po and mi nie wykonuje ?

po pierwsze to jest podatne na sql injection przynajmniej na razie

@bets = Bet.find( :all, :conditions => [ "bets.preresult = ? AND bets.user.id = ?", matches.outcome, 1]

Tylko te bets.preresult i matches.outcometo jakieś złączenie ma być czy co, opisz to może. Bo nie wygląda to ok

bets.preresult = matches.outcome

to miało wyświetlić wpisy z tabeli bets ,ale tylko te których pola preresult z tabeli bets będą równe polom outcome tabeli matches i to w zasadzie działało chyba ok, ale chce to jeszcze ograniczyć, że wyświetlało tylko te wpisy konkretnego usera dlatego kombinowałem z tym bets.user.id = 1.

  • to 1 to tak na próbe, bo docelowo to będzie w tym miejscu z sesji zalogowany user, no ale nie ogranicza.

No to właśnie jest złączenie, chcesz złączenie zrobić. Jak, przykłady itp masz tutaj:

http://railscasts.com/episodes/181-include-vs-joins

Błąd jest chyba taki, że jest kropka zamiast podkreślnika bo w :conditions lecisz sqlem, więc powinno być:

@bets = Bet.find( :all, :conditions => [ "bets.preresult = ? AND bets.user_id = ?", matches.outcome, 1]

Ale można jeszcze fajniej:

@user = User.find(session[:user_id]) # niech session[:user_id] przechowuje id zalogowanego usera @user.bets.all(:conditions => ["preresult = ?", matches.outcome]) # Model.all to alias do Model.find(:all)
p.s. Jak coś nie działa, to pewnie są jakieś literówki bo pisałem z kija

@Piotr Misiurek - dzięki faktycznie podkreślnik rozwiązuje sprawę.
Jednak jest problem z matches.outcome. Ponieważ jeżeli wpisze liczę to wyświetla ok. Natomiast w przypadku jak poniżej:

@bets = Bet.find(:all, :conditions => ["bets.preresult = ? AND bets.user_id = ?", matches.outcome, 1 ], :joins => [:match] )
otrzymuje undefined local variable or method `matches’ for #BetsController:0x6b600c0

Więc matches.outcome łąduje w " " i błędu nie ma ale nie uzyskuje tego (nie wyświetla żadnych rekordów) co po: bets.preresult = matches.outcome w moim podatnym na ataki conditions :frowning:

no to spróbuj :slight_smile:

@bets = Bet.find(:all, :conditions => ["bets.preresult = ? AND bets.user_id = ?","#{matches.outcome}", 1 ], :joins => [:match] )

A ja się założe, że matches to tablica i o to tu chodzi. Już tjeden to zauważył w pierwszy poście. Aby z tego wziąć wartości outcome dla poszczególnych elementów, należy zrobić tak:

matches.collect { |match| match.outcome }

Tylkoże wtedy tego Ci nie łyknie sql, bo wstawisz znów tablice do zapytania. Nie wiem czy w sql’u jest coś ala include?, ale możesz bawić się ze wstawiawianiem OR do zapytania lub być może obejrzeć railscasta o named_scope i zrobić to bardzo elegancko.

matches.collect { |match| match.outcome }

no tego to do zapytania nie włoże, jeśli chodzi o named_scope to co prawda tego jeszcze nie używałem, ale z tego co widzę to i tak musiałbym się odwoływać matches.wynikajace_ze_scope_name co stawia mnie w podobnej sytuacji co obecna czyli matches.outcome.

Wydaje się więc, że niby proste ale jak przyjdzie zrealizować to już nie za bardzo. Pewnie żle zrobiłem konstrukcje tabel w bazie (pierwszy post) skoro takie problemy teraz są.

named_scope to ładnie i prosto zrobisz, by activerecordowe find może przyjmować tablice jako argument i bierze na siebie podanie tego do sql’a

jak chcesz to prosto zrobic to mozesz tak w miare:

@bets = [] matches.each { |match| @bet = Bet.find(:all, :conditions => ["bets.preresult = ? AND bets.user_id = ?",matche.outcome, 1 ], :joins => [:match] ) @bets << @bet
cos w tym stylu. Powinno zadziałać, jeśli to jest tablica. Ale wtedy dziwne że zadziałało ci na początku.

Dzięki,

@Piotr Misiurek, chciałem nieco kontynuować named_scope, bo widać, że to właśnie tego trzeba użyć, żeby było zgodne z zasadami.
Tylko oczywiście przykłady w railcascie i API są zdecydowanie inne niż u mnie.

więc

class Bet < ActiveRecord::Base belongs_to :match belongs_to :user named_scope :qqq, :joins => :match, :conditions => ['bets.preresult = ?', "#{matches.outcome}" ]
matches.outcome jest jako undefined local variable or method `matches’ czyli ciągle nie widzi tej tablicy.

Trochę się chyba wszyscy pogubili w temacie. Na przyszłość zawsze dokładnie opisz co dana funkcjonalność ma robić (w tym wypadku jak dokładnie w teorii powinien działać ten scope).
Co to jest preresult i czy w tym scopie chcesz go zawsze przyrównywać do pola outcome tabeli matches, która jest powiązana z tym konkretnym Bet? (po asocjacji belongs_to :match wnioskuję, że tak) Trochę ciężko pomóc w rozwiązaniu problemu, kiedy nikt nie wie co autor miał na myśli.

W poprzednim poście widziałem, że próbowałeś zrobić join z tabelą matches, więc prawdopodobnie nie chciałeś parametryzować tego scopa a raczej osiągnąć coś takiego:

named_scope :preresult_like_outcome, :joins => :match, :conditions => ['bets.preresult = matches.outcome']

w tym przypadku po klauzuli WHERE rails wrzuci Ci do zapytania dokładnie to, co dałeś w conditions, czyli ‘bets.preresult = matches.outcome’, w poprzednim użyłeś “#{matches.outcome}” - w ruby #{} służy do osadzania wartości przekazanego wyrażenia w łańcuchu. Undefined local variable or method `matches’ dostajesz bo matches nie jest zadeklarowane w Twoim modelu jako metoda a próbujesz się do tego dostać używając właśnie #{}.

Jeśli dodatkowo chcesz sparametryzować to ze względu na użytkownika:

named_scope :for_user, lambda { |user| :joins => :match, :conditions => ['bets.preresult = matches.outcome AND bets.user_id = ?', user.id] }

PS tak off topic, gdy dotrzesz do momentu w którym dociera do ciebie ze namieszałeś na początku, źle baze rozplanowałes, relacje itp to warto zrobić sobie przysługę i zacząć od nowa, bo przy źle zaplanowanej aplikacji będziesz się na każdym kroku spotykał z problemami.

@balinski dzieki, właśnie o to chodzi. Ten kod nie wywala błędu:

named_scope :for_user, lambda { |user| { :joins => :match, :conditions => ["bets.preresult = matches.outcome AND bets.user_id = ?", 1] }}
dodałem dwa klamry, nie wiem czemu tak trza było, ale widocznie tak musiało być.

@gotar żebym to ja wiedział czy mam źle czy nie rozplanowane ) jak na razie to po prostu nie wiem (nie jestem programistą, nie pracuje programując, nie jestem nawet informatykiem. To tylko wszystko w ramach próby/nauki/hobby :slight_smile: )

ale wracając: gdy w konsoli robie Bet.for_user to dostaje:
NoMethodError: undefinied method ‘for_user’ for #<Class …

W kontrolerze utworzyłem metodę user_for tyle nie wiem co niej wpisać, chyba, że w modelu ma być utworzona metoda ?

Czy model Bet opisuje zawarcie zakładu czy zakład sam w sobie np na daną drużynę i zakład danego typu np najlepszy strzelec itp ?
Bo jeśli to drugie, to moim zdaniem tu by stykła tablica UserBets, która by przechowywała “zdarzenie” zawarcia zakładu, oprócz user.id i bet.id mogłbyś tam sobie wrzucić stawkę za jaką poszedł, datę i co tam chcesz, a w Bet same mozliwości zakładu czyli np nazwa meczu, drużyna obstawiana, typ zakładu. Co myślicie ?