Zmienna instancji w modelu

Mam model

[code=ruby]class Plant < ActiveRedord::Base
belongs_to :place

attr_accessor :post_code

def post_code
self.place ? self.place.post_code : @post_code
end
end[/code]
Kod działa jak chcę, tylko skąd w @post_code są dane skoro ich nigdzie nie przypisałem?

Pozdrawiam

A skad wiesz, ze dziala? Masz jakies planty bez place? Bo jak nie to Ci zawsze zwraca place.post_code

Plant bez place jest gdy robie nowy Plant.

Działa, bo jak robię edycję to dla Plant pobiera mi z Place post_code, a jak daje nowy to mam pusto, agdy robie zapisz i nie przechodzi walidacji to post_code wpisany pozostaje.

No tak, ale przecież masz attribute_accessor :post_code - więc jak robisz plant.post_code = ‘123’ to przypisuje się do @post_code. Elementarz.

możesz napisać co chcesz zrobić? bo kod + “działa/nie działa” to trochę za mało :stuck_out_tongue:

no i można prościej:

[code=ruby]attr_accessor :post_code

def post_code
place.try(:post_code) || super
end[/code]

Mam w modelu dwa pola dla uproszczenia: post_code, pole wirtualne i pole location_id.

W modelu początkowo miałem:
def post_code
self.place.try(:post_code)
end

Na oba pola miałem ustawioną walidację, że muszą być wypełnione.
Jak podczas zapisu nowego planta podałem tylko post_code, to po powrocie do edycji pole post_code nie było wypełnione.

A po zmianie na taki kod w modelu:
self.place ? self.place.post_code : @post_code pole post_code jest wypełnione

Dlatego napisałem że działa.
Chodziło o to aby po powrocie do edycji, po przejściu przez walidacje pole post_code było wypełnione. A w pierwszym przypadku nie było.

Mam nadzieję, że to jest dobre rozwiązanie tego problemu.

Kazdy sposob, ktory dziala, jest dobry. Pytanie, czy optymalny. :wink:

ale nie rozumiem, chcesz przy tworzeniu Plant utworzyć Place z podanym w formularzu post_code? co robisz z tym post_code przy zapisie? to przecież jest (jak to nazwałeś) wirtualne pole, więc nie zapisujesz go do bazy.

Nie, Place to baza wszystkich kodów + miejscowości w Polsce. I za pomocą wirtualnego pola ograniczam wyświetlanie miejscowości do tych z podanym kodem. W modelu Plant zapisuje w polu place_id, id z modelu Place.

To chyba zły pomysł. Pachnie mi zmiennymi globalnymi.

Raczej zdefiniuj sobie normalną metodę zwracającą odfiltrowane wyniki, w odpowiednim miejscu kontrolera (na przykład) zbierz te wyniki do innej zmiennej i pracuj na niej. Wszystko będzie bardziej czytelne i nie będziesz miał problemu, że np. w jakiejś innej akcji (w uproszczeniu - proszę nie czepiać się, że zmienne instancji normalnie nie przeżywają do następnej akcji) pozostanie zmienna filtrująca z poprzedniej i wyniki będziesz miał za każdym razem inne.

Zmienne globalne? Gdzie?

Ja nic nie ustawiam.

jedyne co to w modelu zamiast:

def post_code self.place.try(:post_code) end
zmieniłem na:

def post_code self.place ? self.place.post_code : @post_code end
Chodzi o to dlaczego w @post_code jest kod w przypadku nie przejscia przez walidacje dla akcji new.

w modelu mam oczywiście attr_accesor :post_code.

Napisałem, że jest zapach zmiennych globalnych, a nie że one same :smiley:

Chyba rozumiem, w czym problem, ale o tym później. To co wydało mi się nie za dobre, to gdy napisałeś, że “za pomocą wirtualnego pola ograniczam wyświetlanie miejscowości”. Dla potrzeb filtrowania, zmienna instancji… zgodzę się, że to jeszcze nie znaczy, że kod masz zły, ale to już wydaje się podejrzane. Zależy jak to dalej oprogramowałeś, ale bezpieczniej jest zamiast zmiennej zdefiniować metodę filtrującą i po prostu przekazać jej parametr. Wtedy “zmienna filtrująca” jest zapominana jak tylko funkcja kończy z niej korzystać i wszystko jest OK.

Teraz wyczuwam na podstawie słów “Jak podczas zapisu nowego planta podałem tylko post_code, to po powrocie do edycji pole post_code nie było wypełnione.”, że oczekiwałeś iż wartość tej zmiennej zostanie jakoś zapamiętana pomiędzy dwoma akcjami (:create i :edit). Otóż żadne zmienne instancji nie przeżywają końca akcji (tej metody w kontrolerze). Jeżeli chcesz ją zapamiętać, to musisz stworzyć normalne pole w bazie danych - nie wirtualny, a standardowy atrybut. I wtedy wszystko będzie OK.

W akcji :new nie ma walidacji. Walidacja może być wywołana w akcji :create. Nie do końca rozumiem też dlaczego nieudana walidacja miałaby powodować wyczyszczenie jakiegoś atrybutu, oczekiwałbym też, że raczej zmienna będzie pusta, a nie że będzie w niej jakiś “kod”. Czy twierdzisz, że zmienna @post_code zawiera jakąś wartość dla obiektu wyśwetlanego w akcji :new? Jeśli tak, to ja bym raczej podejrzewał, że to przeglądarka wypełnia Ci pola formatki. Możesz dać w dowolnym miejscu kody coś takiego:

Rails.logger.debug "Zmienna @post_code = #{@post_code.inspect}"

i przejrzeć plik log/development.log.

“… ale bezpieczniej jest zamiast zmiennej zdefiniować metodę filtrującą i …”

Jaką zmienną masz na myśli? @post_code ?

Tak, chodziło mi o zmienną @post_code. Oczywiście mogłem coś źle zrozumieć, kodu nie doczytać i tak dalej. Normalnie bym zapytał o to, jak tej zmiennej używasz, przejrzał testy i akcje z niej korzystające i wtedy bym wiedział, czy technika, którą stosujesz jest bezpieczna, czy nie, i dopiero wtedy moje komentarze by miały jakąś wartość.

Generalnie to zapomnij, że cokolwiek mówiłem. Przyjmijmy, że to było zagrożenie urojone. :slight_smile:

Ale czy miałem rację zakładając, że oczekiwałeś iż ta zmienna zachowa swoją wartość pomiędzy akcjami :create i :edit?

Chodzi o to, że ja tej zmiennej nie ustawiam nigdzie, tylko raz jest użyta w tym miejscu co powyżej i nic więcej i chodzi o to skąd w tej zmiennej po nie przesciu walidacji w akcji create ta zmienna jest ustawiona.

w modelu Plant jest taki kod

attr_accessor: post_code def post_code self.place ? self.place.post_code : @post_code end
Nie chodzi o to aby ta zmienna zachowała wartość tylko skąd ona w tej zmiennej @post_code (ta wartość ) się wzięła.